c# – 如何简化与实体框架的有多个关系的访问?

这是我想要做的:

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

一种替代方案是始终包含所有导航属性(递归),但就查询效率而言,这将是一种可怕的方法,因为每个查询将是对所有相关表的一个大规模连接,无论是否必要.

点赞