检查EF4.1中是否存在多对多关系的有效方法 [英] Efficient way of checking if many-to-many relationship exists in EF4.1

查看:70
本文介绍了检查EF4.1中是否存在多对多关系的有效方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在两个实体-Media和MediaCollection之间建立了多对多关系.我想检查集合中是否已存在某种媒体.我可以按照以下步骤进行操作:

I have a many-to-many relationship between two entities - Media and MediaCollection. I want to check if a certain Media already exists in a collection. I can do this as follows:

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

但是,mediaCollection.Media是一个ICollection,因此对我来说,这似乎必须从数据库中检索馆藏中的每个Media才能进行此检查.由于馆藏中可能有许多媒体,所以这似乎效率很低.我以为我应该使用IQueryable方法,但是我看不到如何处理多对多关系.

However, mediaCollection.Media is an ICollection, so to me this looks like it will have to retrieve every Media in the collection from the database just to make this check. As there could be many media in a collection, this seems very inefficient. I'n thinking that I should use a method of IQueryable, but I can't see how to do this for many-to-many relationships.

如何在不检索整个集合的情况下检查该关系是否存在?

How can I check for the existence of the relationship without retrieving the whole collection?

编辑

我正在从数据库中生成EF数据模型,然后使用内置的VS POCO T4模板来生成我的数据上下文和实体类.我认为问题在于生成的代码不会为导航属性返回EntityCollection,而是返回ObjectSet. ObjectSet实现IQueryable,但不公开CreateSourceQuery()方法.

I am generating the EF data model from my database, then using the built in VS POCO T4 templates to generate my data context and entity classes. I think the problem is that the generated code does not return EntityCollection for the navigation properties, but instead ObjectSet. ObjectSet implements IQueryable, but does not expose a CreateSourceQuery() method.

这是上下文中相关行的精简版本:

Here is a stripped down version of the relevant lines from the context:

    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实体的类的精简版本:

And here is a stripped down version of the class for the MediaCollection entity:

    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:

And finally, here is the FixupCollection that the template also generates:

    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);
            }
        }
    }

推荐答案

因此,似乎内置的VS POCO T4模板不会生成与CreateSourceQuery()等效的任何内容.不管!我们可以自己编写代码.如果将以下代码添加到上下文的.tt文件中,然后重新生成:

So it seems that the built in VS POCO T4 template does not generate anything equivalent to CreateSourceQuery(). No matter! We can code it ourselves. If you add the following code at to the context's .tt file and regenerate:

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();
}

然后我们可以检查存在多对多的情况,如下所示:

then we can check for the existence of a many-to-many as follows:

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

在CTP4代码优先中使用CreateSourceQuery 一个.

这篇关于检查EF4.1中是否存在多对多关系的有效方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆