这是我想做的:
var user = db.User.First(conditions);
user.Book.First();
目前我必须这样做。
var user = db.User.Include("Book").First(conditionsForUser);
user.Book.First();
我之所以要对此进行简化,是因为我不想每次访问关系时都必须指定包含的内容。看起来很麻烦。
例如:鉴于我之前已经检索到用户,我希望能够执行以下操作:
user.Book.First()
user.Blog.First()
user.SomeOtherHasManyRelationship.Where(conditions)
这是我目前所拥有的:
public object RelationshipFor(string relationship)
{
using (var db = User.DbContext())
{
var relationshipType = TypeRepresentedBy(relationship); // unused for now, not sure if I need the type of the relationship
var myTable = ((ICollection)db.Send(RelationshipName)); // RelationshipName is "User" in this instance.
var meWithRelationship = myTable.Where(i => i.Send(IdColumn) == Id).Include(relationship); // currently, myTable doesn't know about 'Where' for some reason.
return meWithRelationship.Send(relationship);
}
}
然后将如何使用如下:
user.RelationshipFor("Book") // returns a list of books
我的代码中有一些其他逻辑,它们进一步抽象了允许我执行 user.Book.First()
的逻辑。
希望我能获得很多开源的许可,因为我正在按照 ActiveRecord 风格的 crud 对很多 api 进行建模。
请注意,我正在使用我制作的一组扩展来帮助减少动态处理的痛苦:https://github.com/NullVoxPopuli/csharp-extensions
更新 1:
public object RelationshipFor(string relationship)
{
using (var db = User.DbContext())
{
var myTable = (DbSet<DatabaseModels.User>)db.Send(RelationshipName);
var myInclude = myTable.Include(i => i.Send(relationship));
var meWithRelationship = myInclude.First(i => (long)i.Send(IdColumn) == Id);
return meWithRelationship.Send(relationship);
}
}
目前,我已经对用户的类型转换进行了硬编码,试图让某些东西正常工作。 我现在的错误是:
Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'.
最佳答案
这不是一个微不足道的问题,并且没有“一刀切”的方法。你实际上想要的是 lazy loading, which was not included in EF7 for many reasons .
我不知道您显示的代码应该做什么,但一种选择是引入存储库模式,您可以在其中指定集合级别的“要包含的实体”:
public class UserRepository
{
private readonly IQueryable<User> _dataSet;
public UserRepository(IQueryable<User> userDataSet)
{
_dataSet = userDataSet;
}
public IQueryable<User> Include()
{
return _dataSet.Include(u => u.Book)
.Include(u => u.Blog);
}
}
并且您可以将大量逻辑移至通用基类,只剩下 Include()
方法。例如,您可以在显示时使用字符串(或枚举,或...),仅选择要包含的相关实体:
public class GenericRepository
{
// ...
public IQueryable<User> Include(string includeGroup = null)
{
return IncludeGroup(includeGroup);
}
protected virtual IncludeGroup(string includeGroup)
{
return _dataSet;
}
}
然后在 UserRepository
中:
protected override IQueryable<User> IncludeGroup(string includeGroup)
{
switch (includeGroup.ToUpperInvariant())
{
case "BOOK":
return _dataSet.Include(u => u.Book)
.Include(u => u.Book.Author);
case "BLOG":
return _dataSet.Include(u => u.Blog);
default:
return base.Include(includeGroup);
}
}
然后像这样使用它:
var userRepo = new UserRepository(db.User);
var userWithBooks = userRepo.Include("Book");
var firstUser = userWithBooks.FirstOrDefault(u => u.Name == "Foo");
var firstUserFirstBook = firstUser.Book.FirstOrDefault();
一种替代方法是始终包含所有导航属性(递归),但就查询效率而言,这将是一种糟糕的方法,因为无论是否有必要,每个查询都将是对所有相关表的一次大规模连接。
关于c# - 如何简化与 Entity Framework 的多对多关系的访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34790533/