我正在尝试将部分类中的属性添加到实体框架模型,该实体框架模型包括已通过导航属性与某些其他行联合映射的实体。
数据库: 我有一个带有UserID主键的Users表。另外,我有一个带有OwnerUserID(外键,可为空)的Tags表,如果它是用户特定的,则设置为UserID,但也可以为null,表示它适用于所有用户。
EF模型: 正确生成导航属性Users.Tags以包括显式分配给该用户的所有标记。
问题: 我需要User实体上的一个属性,该属性包含具有User 的OwnerUserID和为null的所有标记。 所以我希望能够说出这样的话:
public partial class User
{
public IEnumerable<Tag> VisibleTags
{
get
{
return tags = (from t in {AllTags}
where t.PrivateUserID == null
select t).Union(
from t in this.Tags
select t);
}
}
}
...其中{AllTags}代表系统中的所有标签。
答案 0 :(得分:3)
对设计的快速评论:
当null
提供上下文信息(例如适用于所有用户的值)时,我不是数据库中可空值的忠实粉丝,尤其是与没有用户相反。
您可能需要考虑可以对用户进行分组的设计,并且标签对一组用户可见。如果您的需求在未来发生变化,并且更加明确,这将更加灵活。它也可以使你可以使列不可为空。
如果您现在不想将其构建到系统中,我会亲自策划从当前设计到基于组的设计的迁移是什么样的,只是为了确保您没有自己画画在设计方面进入角落。
手头的问题
您从上下文中获得AllTags
,因此您将不得不使用上下文对象。
尝试1
执行此操作的一种方法是在上下文中创建一个接受User
或UserId
的方法。
// In partial Context class...
public IEnumerable<Tag> GetVisibleTags(User user)
{
return Tags.Where(t => t.PrivateUserID == null)
.Union(user.Tags)
;
}
// Call it like this...
context.GetVisibleTags(someUser);
我不完全喜欢,因为我不认为应该使用上下文来进行查询。这是“存储库”(实体/ DbSet
/ DataSet
类)的工作。
尝试2
另一种方法(可以让用户访问它)是为getter方法添加一个context参数:
// In partial User class...
public IEnumerable<Tag> GetVisibleTags(MyContextClass context)
{
return context.Tags.Where(t => t.PrivateUserID == null)
.Union(this.Tags)
;
}
// Call it like this
someUser.GetVisibleTags(context);
我比第一个更喜欢这个,因为不应该允许“存储库”(entity / DataSet
/ DbSet
)知道有关上下文的任何内容。
尝试3
您可以创建一个查询包装器对象来解决此问题。我建议你尽可能用这种方式编写复杂的查询,这样它们就可以重用了。
诀窍是找出一个好的域名特定名称...
// Todo: This is a terrible name.
// Figure out what makes more sense in your domain, by seeing where you use it
public class VisibleTags
{
private readonly IMyContextClass context;
public VisibleTags(IMyContextClass context)
{
this.context = context;
}
// Todo: Try to see if you can get this to return IQueryable.
// I haven't used Union, so I'm not sure if it breaks that ability or not...
public IEnumerable<Tag> GetVisibleTags(User user)
{
return context.Tags
.Where(t => t.PrivateUserID == null)
.Union(user.Tags)
;
}
}