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

发布于 2024-08-27 11:43:00 字数 1583 浏览 6 评论 0原文

我有一个 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);
        }
    }

在上面的示例中,我将照片集合类型设置为 IEnumerable,因为这将使其只读。添加/删除照片的唯一方法是通过公共方法。

这样做的问题是实体框架无法将照片实体加载到 IEnumerable 集合中,因为它不是 ICollection 类型。

通过将类型更改为 ICollection 将允许调用者在集合本身上调用 Add mentod,这是不好的。

我有什么选择?

编辑:

我可以重构代码,这样它就不会公开照片的公共属性:

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

    }

并使用 GetPhotos() 返回集合。该方法的另一个问题是,我将失去更改跟踪能力,因为我无法将集合标记为虚拟 - 不可能将属性标记为私有虚拟。

在 NHibernate 中,我相信可以通过配置将代理类映射到 private 集合。我希望这将成为 EF4 的一个功能。目前我不喜欢无法对收藏品进行任何控制!

I have a POCO domain model which is wired up to the entity framework using the new ObjectContext class.

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. 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.

By changing the type to ICollection will allow callers to call the Add mentod on the collection itself which is not good.

What are my options?

Edit:

I could refactor the code so it does not expose a public property for 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. 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. I hope that this will become a feature of EF4. Currently i don't like the inability to have any control over the collection!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

清君侧 2024-09-03 11:43:00

执行此操作的方法是拥有一个映射到模型中的受保护虚拟属性和一个返回 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);
    }
}

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.

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);
    }
}
挽清梦 2024-09-03 11:43:00

Anton,如果您能解释为什么您不希望开发人员访问您集合的 Add 方法,这将帮助我更好地理解您的问题。这是因为该列表是严格只读的,还是因为您想在添加新实体时运行一些自定义业务逻辑?

无论如何......我将假设您正在尝试执行后者(即在修改集合时运行自定义业务逻辑)。我在我的一个项目中做了类似的解决方案,其想法如下:

在 EF4 中生成 POCO 的 TT 模板将所有集合创建为 TrackableCollection 列表。此类有一个名为“CollectionChanged”的事件,您可以订阅该事件并侦听集合的任何更改。

因此,您可以执行以下操作:

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

上述解决方案的好处是您仍然以“EF”方式使用您的 Product 实体...如果您团队中的任何开发人员都可以简单地访问实体目录的属性并需要运行显式硬类型函数。

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. 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 (i.e. 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. This class has an event called 'CollectionChanged' which you can subscribe to and listen to any changes to your collection.

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.

心碎无痕… 2024-09-03 11:43:00

虽然有点晚了,但这就是 Observable 对象的用途。让数据结构发挥它最擅长的作用。如果您不想构建自己的集合来执行您需要的操作并从您的属性公开常规 ICollection 类型,请使用 ObservableCollection 作为字段类型。当集合中的相关实体通过 CollectionChanged 事件发生更改时,您可以在所需的父实体中运行任何逻辑。如果您需要有选择地启用或禁用修改,则很容易扩展现有集合类型或编写一个代理集合,该代理集合允许调用方法来切换集合的可变性(ISupportInitialize 可以用来很好地表示这种能力 BTW )。

Bit late to the party but this is what Observable objects are for. 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. You can run any logic in the parent entity you need when the related entities in the collection change via the CollectionChanged event. 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).

巡山小妖精 2024-09-03 11:43:00

(对于我最初的帖子的简洁性表示歉意 - 我是通过手机回答的)

您可以通过 EF 实体集上的 LINQ 查询来构建您的集合。但是,您将结果集合保留为业务类的内部数据成员,并公开通过在实体集上调用 AsEnumerable() 返回的 IEnumerable 作为以下结果:公开照片。

您也可以在内部缓存 IEnumerable,这样您就不会在每次调用者请求集合时都调用 AsEnumerable()。当然,这意味着如果用户需要通过您的公共方法更新集合,您可能必须刷新缓存的 IEnumerable。如果调用者还缓存了指向前一个 IEnumerable 的指针,这可能会带来小问题。

或者,如果您的调用方始终使用完整的实体集,则 EntitySet 类(您的所有 EF 集都将继承该类)实现 IEnumerable,因此您可以直接将实体集返回给调用者。

请注意,如果您希望在业务类的范围之外从 EF 实体集加载集合,则可以在您的类上创建一个采用 ICollection 的构造函数。这样,一旦创建了对象,集合就会被密封在其中,并且仅作为 IEnumerable 公开。

(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. 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.

You could cache the IEnumerable<Photos> internally as well, so that you don't call AsEnumerable() every time your caller asks for the collection. 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. This might pose small issue if the caller has also cached the pointer to the previous 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.

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. This way, once you create your object, the collection is sealed in it, and exposed only as an IEnumerable.

生活了然无味 2024-09-03 11:43:00

为什么不尝试以下方法并保留使用属性呢?

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

或者,您可以使用装饰器模式将类封装到不能直接修改集合的类中。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文