我有一个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);
}
}
在上面的示例中,我已将Photos集合类型设置为IEnumerable,因为这将使其成为只读。添加/删除照片的唯一方法是通过公共方法。
问题在于,实体框架无法将Photo实体加载到IEnumerable集合中,因为它不是ICollection类型。
通过将类型更改为ICollection将允许调用者调用集合本身的Add mentod,这是不好的。
我有什么选择?
修改
我可以重构代码,因此它不会公开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);
}
}
使用GetPhotos()返回集合。该方法的另一个问题是我将失去更改跟踪功能,因为我无法将集合标记为虚拟 - 无法将属性标记为私有虚拟。
在NHibernate中,我相信可以通过配置将代理类映射到私有集合。我希望这将成为EF4的一个功能。目前我不喜欢无法控制集合!
答案 0 :(得分:5)
这样做的方法是拥有一个在您的模型中映射的受保护虚拟属性和一个返回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);
}
}
答案 1 :(得分:1)
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”方式使用您的产品实体...您的团队中的任何开发人员都可以简单地访问实体目录的属性并且需要运行显式的硬类型函数
答案 2 :(得分:0)
派对迟到了,但这就是Observable对象的用途。允许数据结构执行最佳功能。如果您不想构建自己的集合来执行您需要的操作并从您的属性中公开常规ICollection类型,请使用ObservableCollection作为您的字段类型。当集合中的相关实体通过CollectionChanged事件更改时,您可以在父实体中运行任何逻辑。如果您需要有选择地启用或禁用修改,则可以轻松扩展现有集合类型或编写允许调用方法的代理集合来切换集合的可变性(ISupportInitialize可用于表示此功能的良好效果BTW )。
答案 3 :(得分:-1)
(我最初的简短道歉 - 我正在接听电话)
您可以通过EF实体集上的LINQ查询构建集合。但是,您将生成的集合保留为业务类的内部数据成员,并通过公共照片的结果公开在实体集上调用IEnumerable<Photo>
返回的AsEnumerable()
。
您也可以在内部缓存IEnumerable<Photos>
,这样每次调用者请求收集时都不会调用AsEnumerable()
。当然,这意味着如果用户需要通过公共方法更新集合,则可能必须刷新缓存的IEnumerable
。如果调用者还缓存了指向前一个IEnumerable
的指针,则可能会出现小问题。
或者,如果您的调用者将始终使用完整的实体集,则EntitySet
类(所有EF集将继承的类)实现IEnumerable<TEntity>
,因此您可以直接将实体集返回到你的来电者。
请注意,如果您希望从EF实体集加载集合在业务类范围之外,则可以在类上创建一个带ICollection
的构造函数。这样,一旦您创建了对象,该集合就会被密封在其中,并且仅作为IEnumerable
公开。
答案 4 :(得分:-1)
为什么不尝试以下操作并保留使用属性?
private ICollection<Photo> photos{get; set;}
public IEnumerable<Photo> Photos
{
get {return (IEnumberable<Photo>)photos;}
}
或者,您可以使用装饰器模式将类封装到无法直接修改集合的类中。