简体   繁体   English

如何使用实体框架将实体加载到私有集合中

[英]How to load entities into private collections using the entity framework

I have a POCO domain model which is wired up to the entity framework using the new ObjectContext class. 我有一个POCO域模型,它使用新的ObjectContext类连接到实体框架。

public class Product
    {
        private ICollection<Photo> _photos;

        public Product()
        {
            _photos = new Collection<Photo>();         
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public virtual IEnumerable<Photo> Photos
        {
            get
            {
                return _photos;
            }
        }

        public void AddPhoto(Photo photo)
        {
            //Some biz logic
            //...
            _photos.Add(photo);
        }
    }

In the above example i have set the Photos collection type to IEnumerable as this will make it read only. 在上面的例子中,我已经将Photos集合类型设置为IEnumerable,因为这将使它只读。 The only way to add/remove photos is through the public methods. 添加/删除照片的唯一方法是通过公共方法。

The problem with this is that the Entity Framework cannot load the Photo entities into the IEnumerable collection as it's not of type ICollection. 这样做的问题是实体框架无法将Photo实体加载到IEnumerable集合中,因为它不是ICollection类型。

By changing the type to ICollection will allow callers to call the Add mentod on the collection itself which is not good. 通过将类型更改为ICollection将允许调用者调用集合本身的Add mentod,这是不好的。

What are my options? 我有什么选择?

Edit: 编辑:

I could refactor the code so it does not expose a public property for Photos: 我可以重构代码,因此它不会公开Photos的公共属性:

public class Product
    {
    public Product()
    {
        Photos = new Collection<Photo>();         
    }

    public int Id { get; set; }
    public string Name { get; set; }
    private Collection<Photo> Photos {get; set; }

    public IEnumerable<Photo> GetPhotos()
    {
        return Photos; 
    }

    public void AddPhoto(Photo photo)
    {
        //Some biz logic
        //...
        Photos.Add(photo);
    }

    }

And use the GetPhotos() to return the collection. 并使用GetPhotos()返回集合。 The other problem with the approach is that I will loose the change tracking abilities as I cannot mark the collection as Virtual - It is not possible to mark a property as private virtual. 该方法的另一个问题是我将失去更改跟踪功能,因为我无法将集合标记为虚拟 - 无法将属性标记为私有虚拟。

In NHibernate I believe it's possible to map the proxy class to the private collection via configuration. 在NHibernate中,我相信可以通过配置将代理类映射到私有集合。 I hope that this will become a feature of EF4. 我希望这将成为EF4的一个功能。 Currently i don't like the inability to have any control over the collection! 目前我不喜欢无法控制集合!

The way to do this is to have a protected virtual property which is mapped in your model and a public property that returns an IEnumerable. 执行此操作的方法是在模型中映射受保护的虚拟属性,并返回返回IEnumerable的公共属性。

public class Product
{
    public Product()
    {
        PhotoCollection = new Collcation<Photo>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    protected virtual ICollection<Photo> PhotoCollection {get; set; }

    public IEnumerable<Photo> Photos
    {
        get { return PhotoCollection ; } 
    }

    public void AddPhoto(Photo photo)
    {
        //Some biz logic
        //...
        PhotoCollection .Add(photo);
    }
}

Anton, it would help me understand your problem more if you can explain why is it that you do not want developers to access the Add method of your collection. 如果您能解释为什么您不希望开发人员访问您的集合的Add方法,那么它会帮助我更好地理解您的问题。 Is this because the list is strictly read-only, or is it because you want to run some custom business logic when a new entity is added? 这是因为列表是严格只读的,还是因为你想在添加新实体时运行一些自定义业务逻辑?

Anyway... I am going to assume that you are trying to do the latter (ie run custom business logic when the collection is modified). 无论如何......我将假设您正在尝试执行后者(即在修改集合时运行自定义业务逻辑)。 I have done a similar solution on a project of mine, and the idea is as follows: 我在我的一个项目上做了类似的解决方案,其想法如下:

The TT template that produces POCOs in EF4 creates all collections as TrackableCollection lists. 在EF4中生成POCO的TT模板将所有集合创建为TrackableCollection列表。 This class has an event called 'CollectionChanged' which you can subscribe to and listen to any changes to your collection. 此类有一个名为“CollectionChanged”的事件,您可以订阅并收听对集合的任何更改。

So you can do something as follows: 所以你可以做如下的事情:

public class Product
{
    public Product()
    {
        Photos.CollectionChanged += ListCollectionChanged;
    }

    public int Id { get; set; }

    public string Name { get; set; }

    public TrackableCollection<Photo> Photos
    {
        get
        {
            // default code generated by EF4 TT
        }
        set
        {
            // default code generated by EF4 TT
        }
    }

    private void ListCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            // A new item has been added to collection
            case NotifyCollectionChangedAction.Add:
                {
                    T newItem = (T) e.NewItems[0];
                    // Run custom business logic
                }
                break;

            // An existing item has been removed
            case NotifyCollectionChangedAction.Remove:
                {
                    T oldItem = (T) e.OldItems[0];
                    // Run custom business logic
                }
                break;
        }
    }
}

The nice thing about the above solution is that you still use your Product entity in an 'EF' manner... were any developer in your team can simply access a property of the entity directory and need run an explicit hard typed function. 上述解决方案的优点在于您仍然以“EF”方式使用您的产品实体......您的团队中的任何开发人员都可以简单地访问实体目录的属性,并且需要运行显式的硬类型函数。

Bit late to the party but this is what Observable objects are for. 派对迟到了,但这就是Observable对象的用途。 Allow the data structure to do what it does best. 允许数据结构执行最佳功能。 Use ObservableCollection as your field type if you don't want to build your own collection that does what you need and expose the regular ICollection type from your property. 如果您不想构建自己的集合来执行您需要的操作并从您的属性中公开常规ICollection类型,请使用ObservableCollection作为您的字段类型。 You can run any logic in the parent entity you need when the related entities in the collection change via the CollectionChanged event. 当集合中的相关实体通过CollectionChanged事件更改时,您可以在父实体中运行任何逻辑。 If you need to selectively enable or disable modifications it's easy enough to extend an existing collection type or write a proxy collection that allows a call to a method to toggle the mutability of the collection (ISupportInitialize can be used to good effect for representing this ability BTW). 如果您需要有选择地启用或禁用修改,则可以轻松扩展现有集合类型或编写允许调用方法的代理集合来切换集合的可变性(ISupportInitialize可用于表示此功能的良好效果BTW )。

(Apologies for my initial post brevity - I was answering from my phone) (我最初的简短道歉 - 我正在通过手机接听)

You can construct your collection through a LINQ query over an EF entity set. 您可以通过EF实体集上的LINQ查询构建集合。 However, you keep the resulting collection as internal data member to your business class and expose the IEnumerable<Photo> returned by calling AsEnumerable() on the entity set as a result of the public photo. 但是,您将生成的集合保留为业务类的内部数据成员,并通过在公共照片的结果上调用实体集上的AsEnumerable()来公开返回的IEnumerable<Photo>

You could cache the IEnumerable<Photos> internally as well, so that you don't call AsEnumerable() every time your caller asks for the collection. 您也可以在内部缓存IEnumerable<Photos> ,这样每次调用者请求收集时都不会调用AsEnumerable() Of course, that means that if the user needs to update the collection through your public methods, you might have to refresh the cached IEnumerable . 当然,这意味着如果用户需要通过公共方法更新集合,则可能必须刷新缓存的IEnumerable This might pose small issue if the caller has also cached the pointer to the previous IEnumerable . 如果调用者还缓存了指向前一个IEnumerable的指针,这可能会造成小问题。

Alternatively, if your caller will always work with the full entity set, the EntitySet class (of which all your EF sets will inherit) implements IEnumerable<TEntity> , so you can directly return the entity set to your caller. 或者,如果您的调用者将始终使用完整的实体集,则EntitySet类(其所有EF集将继承)将实现IEnumerable<TEntity> ,因此您可以直接将实体集返回给调用者。

Note that if you want the loading of the collection from an EF entity set to happen outside of the scope of your business class, you can make a constructor on your class that takes an ICollection . 请注意,如果您希望从EF实体集加载集合发生在业务类范围之外,则可以在类上创建一个带有ICollection的构造函数。 This way, once you create your object, the collection is sealed in it, and exposed only as an IEnumerable . 这样,一旦创建了对象,集合就会被密封在其中,并且只作为IEnumerable公开。

Why not try the following and leave use properties? 为什么不尝试以下并留下使用属性?

private ICollection<Photo> photos{get; set;}
public IEnumerable<Photo> Photos
{
    get {return (IEnumberable<Photo>)photos;}
}

Alternatively you could use the decorator pattern to encapsulate the class into one which the collection can't be directly modified. 或者,您可以使用装饰器模式将类封装到无法直接修改集合的类中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM