c# - 如何简化与 Entity Framework 的多对多关系的访问?

标签 c# entity-framework entity-framework-core

这是我想做的:

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/

相关文章:

c# - 首先使用实体​​框架代码和 IOC 容器来创建没有默认构造函数的实体

c# - Entity Framework 的悲观并发

c# - 内存数据库不保存数据

c# - EF Core 2.0 - 添加时出现 System.NotSupportedException

c# - 简化 Entity Framework Core 查询

c# - 使用 Active Directory 组进行 Windows 身份验证

c# - 使用C#编辑Xml文件

C#:为什么我的 SQL 查询中缺少 .Where() 条件之一?

c# - 自动完成方法括号

c# - 为什么 IDependencyResolver 与 System.Web 紧密耦合?