2011-07-12 30 views
2

我在兩個實體Media和MediaCollection之間存在多對多的關係。我想檢查某個媒體是否已經存在於一個集合中。我能做到這一點,如下所示:檢查EF4.1中是否存在多對多關係的有效方法

mediaCollection.Media.Any(m => m.id == mediaId) 

然而,mediaCollection.Media是一個ICollection的,所以對我來說,這看起來像它必須從數據庫中檢索集合中的每個媒體只是爲了讓這張支票。由於一個集合中可能有很多媒體,這看起來效率很低。我認爲我應該使用IQueryable的方法,但我看不到如何爲多對多關係做到這一點。

如何檢查關係的存在而不檢索整個集合?

編輯

我生成從我的數據庫中的數據EF模型,然後使用內置的VS POCO T4模板來生成我的數據上下文和實體類。我認爲問題是生成的代碼不會返回導航屬性的EntityCollection,而是ObjectSet。 ObjectSet實現IQueryable,但不公開CreateSourceQuery()方法。

這裏是從上下文相關線路的一個精簡版:

public partial class Entities : ObjectContext 
    { 
     public const string ConnectionString = "name=Entities"; 
     public const string ContainerName = "Entities"; 

     #region Constructors 

     public Entities() 
      : base(ConnectionString, ContainerName) 
     { 
      this.ContextOptions.LazyLoadingEnabled = true; 
     } 

     public Entities(string connectionString) 
      : base(connectionString, ContainerName) 
     { 
      this.ContextOptions.LazyLoadingEnabled = true; 
     } 

     public Entities(EntityConnection connection) 
      : base(connection, ContainerName) 
     { 
      this.ContextOptions.LazyLoadingEnabled = true; 
     } 

     #endregion 

     #region ObjectSet Properties 

     public ObjectSet<MediaCollection> MediaCollections 
     { 
      get { return _mediaCollections ?? (_mediaCollections = CreateObjectSet<MediaCollection>("MediaCollections")); } 
     } 
     private ObjectSet<MediaCollection> _mediaCollections; 

     // snipped many more 

     #endregion 
    } 

這裏是類爲MediaCollection實體一個精簡版:

public partial class MediaCollection 
    { 
     #region Primitive Properties 

     // snipped 

     #endregion 

     #region Navigation Properties  

     public virtual ICollection<Medium> Media 
     { 
      get 
      { 
       if (_media == null) 
       { 
        var newCollection = new FixupCollection<Medium>(); 
        newCollection.CollectionChanged += FixupMedia; 
        _media = newCollection; 
       } 
       return _media; 
      } 
      set 
      { 
       if (!ReferenceEquals(_media, value)) 
       { 
        var previousValue = _media as FixupCollection<Medium>; 
        if (previousValue != null) 
        { 
         previousValue.CollectionChanged -= FixupMedia; 
        } 
        _media = value; 
        var newValue = value as FixupCollection<Medium>; 
        if (newValue != null) 
        { 
         newValue.CollectionChanged += FixupMedia; 
        } 
       } 
      } 
     } 
     private ICollection<Medium> _media; 

     private void FixupMedia(object sender, NotifyCollectionChangedEventArgs e) 
     { 
      if (e.NewItems != null) 
      { 
       foreach (Medium item in e.NewItems) 
       { 
        if (!item.MediaCollections.Contains(this)) 
        { 
         item.MediaCollections.Add(this); 
        } 
       } 
      } 

      if (e.OldItems != null) 
      { 
       foreach (Medium item in e.OldItems) 
       { 
        if (item.MediaCollections.Contains(this)) 
        { 
         item.MediaCollections.Remove(this); 
        } 
       } 
      } 
     } 

     // snip 

     #endregion 
    } 

最後,這裏是模板也生成的FixupCollection:

public class FixupCollection<T> : ObservableCollection<T> 
    { 
     protected override void ClearItems() 
     { 
      new List<T>(this).ForEach(t => Remove(t)); 
     } 

     protected override void InsertItem(int index, T item) 
     { 
      if (!this.Contains(item)) 
      { 
       base.InsertItem(index, item); 
      } 
     } 
    } 

回答

2

所以看起來內置的VS POCO T4模板不會產生任何等同於CreateSourceQuery()的東西。不管!我們可以自己編寫代碼。如果您在添加以下代碼上下文的.TT文件,並重新生成:

public ObjectQuery<T> CreateNavigationSourceQuery<T>(object entity, string navigationProperty) 
{ 
    var ose = ObjectStateManager.GetObjectStateEntry(entity); 
    var rm = ObjectStateManager.GetRelationshipManager(entity); 

    var entityType = (System.Data.Metadata.Edm.EntityType)ose.EntitySet.ElementType; 
    var navigation = entityType.NavigationProperties[navigationProperty]; 

    var relatedEnd = rm.GetRelatedEnd(navigation.RelationshipType.FullName, navigation.ToEndMember.Name); 

    return ((dynamic)relatedEnd).CreateSourceQuery(); 
} 

那麼我們可以檢查的許多一對多如下存在:

var exists = _context.CreateNavigationSourceQuery<Medium>(mediaCollection, "Media") 
    .Any(m => m.Id == medium.Id); 

道具羅文的回答於Using CreateSourceQuery in CTP4 Code First這個。

1

嘗試,

mediaCollection.CreateSourceQuery() 
    .Any(.... 

CreateSourceQuery將爲關聯創建IQueryable。

+0

感謝您的幫助。但是我的上下文是使用T4模板自動生成的(右鍵單擊>添加>新項目> ADO.NET POCO實體生成器)。我的對象似乎沒有CreateSourceQuery()方法。 –

4

你可以這樣做,但你需要一個情境:

bool exists = context.Entry(mediaCollection) 
        .Collection(m => m.Media) 
        .Query() 
        .Any(x => x.Id == mediaId); 

編輯:

如果您正在使用ObjectContext的API與代理波蘇斯代替的DbContext API前者樣品將無法正常工作。你可以試試這個:

context.ContextOptions.LazyLoadingEnabled = false; 
bool exists = ((EntityCollection<Media>)mediaCollection.Media).CreateSourceQuery() 
                   .Any(x => x.Id == mediaId); 
context.ContextOptions.LazyLoadingEnabled = true; 
+0

感謝您的幫助。我可以訪問上下文,但是這個上下文是使用T4模板自動生成的(右鍵單擊>添加>新項目> ADO.NET POCO實體生成器)。它似乎沒有提供你在這裏提到的方法,即Entry()。 –

+0

那麼,如果你不使用它,你爲什麼用EF 4.1來標記你的問題呢? –

+0

嗯......我從我的數據庫生成我的EF4.1數據模型,然後使用內置的VS POCO T4實體模板創建我的上下文和實體類。我嘗試了你在編輯時提出的建議,但是我得到了下面的異常:''無法投射'TheHoges.Domain.FixupCollection'1 [TheHoges.Domain.Medium]'類型的對象來鍵入'System.Data.Objects.DataClasses。 EntityCollection'1 [TheHoges.Domain.Medium]''' –

相關問題