【问题标题】:Linq to Entities with Repository Pattern and EFLinq to Entity with Repository Pattern 和 EF
【发布时间】:2019-06-01 15:08:42
【问题描述】:

我最近一直在研究实体框架和存储库模式,在存储库类中,我创建了一个名为 find 的函数,它使用谓词从中生成实体。这是我的存储库函数。

public T Find(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderby = null, string includeProperties = "")
{
    IQueryable<T> query = dbSet;
    if (filter != null)
    {
        query = query.Where(filter);
    }
    foreach(var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
    {
        query = query.Include(includeProperty);
    }

    if (orderby != null)
    {
        return orderby(query).First();
    }
    else
    {
        return query.First();
    }
}

这是我的 DTO 课程。

public class UsersDo
{
    public int UserId {get;set}
    public string Username {get;set}
    ...
}

现在我在我的页面上调用 Find 函数,例如:

usersDao.Find(x=>x.Username == "username")

但是我得到了错误

The entity or complex type 'UsersDo' cannot be constructed in a LINQ to Entities query.

谁能指出这里出了什么问题。

编辑
在存储库类下,我有一个构造函数:

private readonly DbSet<T> dbSet;
private readonly DataContext context;
public GenericDao(DataContext _context)
{
    context = _context;
    dbSet = context.Set<T>();
}

我的道课:

public class UsersDao : GenericDao<UsersDo>, IUsers
{
     public UsersDao(DataContext context) : base (context) {}
     ...
}

【问题讨论】:

  • 你怎么知道正确的dbSet?顺便说一句,实体框架本身遵循存储库模式,我不建议您使用外部存储库模式,因为它会导致复杂性、功能不足、不需要的错误等等。我可以说你最终会需要绕过你的存储库模式
  • @Simonare 更新了我的问题,您能否给我任何参考,说明我如何使用默认的 EF 存储库模式,希望通过上面的示例说明我的担忧。
  • 你可以试试IQueryable&lt;T&gt; query = dbSet.AsQueryable();吗?
  • @Simonare,这没有用,它仍然抛出同样的错误。

标签: c# entity-framework repository-pattern predicate


【解决方案1】:

你可以试试这个

public class UserContext : DbContext
{
    public DbSet<UsersDo> Users { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UsersDo>()
            .HasKey(e => e.UsersId);

        base.OnModelCreating(modelBuilder);
    }
}

public class Repo<T> where T : class
{
    private readonly DbSet<T> dbSet;

    public Repo(DbContext context)
    {
        dbSet = context.Set<T>();
    }

    public T Find(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderby = null, string includeProperties = "")
    {
        IQueryable<T> query = dbSet;
        if (filter != null)
        {
            query = query.Where(filter);
        }
        foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderby != null)
        {
            query = orderby(query);
        }

        // If you use the First() method you will get an exception when the result is empty.
        return query?.FirstOrDefault();
    }
}

------- 测试代码

internal class Program
{
    private static void Main(string[] args)
    {
        var usersDao = new Repo<UsersDo>(new UserContext());

        var r = usersDao.Find(x => x.Username == "username");
    }
}

【讨论】:

  • 即使 FirstOrDefault 也不起作用,其余代码类似。
  • 我在 DbContext 中将属性 UsersId 标记为 Key,你看到了吗?
  • 是的,但我已经将 UserId 标记为我的实体的键。 [Key] public int UserId {get;set;} public string Username {get;set;}
  • 在你的 DbContext 中,你有一个 DbSet 吗?
  • 是的,我有 DbSet
【解决方案2】:

我强烈推荐一种简化的存储库模式,它有助于模拟您的数据源以进行测试,并提供更大的灵活性。我不建议使用通用存储库,而是将存储库类似于控制器。 (我为一组特定的操作提供数据)这减少了依赖引用的数量,但有利于 SRP 而不是 DNRY。

例如:

public class OrderRepository : IOrderRepository
{
    private MyDbContext Context
    {
        return _contextLocator.Get<MyDbContext>() ?? throw new InvalidOperation("The repository must be called from within a context scope.");
    }

    IQueryable<Order> IOrderRepository.GetOrders()
    {
        var query = Context.Orders.Where(x => x.IsActive);
        return query;
    }

    IQueryable<Order> IOrderRepository.GetOrderById(int orderId)
    {
        var query = Context.Orders.Where(x => x.IsActive && x.OrderId == orderId);
        return query;
    }

    Order IOrderRepository.CreateOrder( /* Required references/values */)
    {
    }

    void IOrderRepository.DeleteOrder(Order order)
    {
    }
}

通过返回 IQueryable,消费代码可以保持对可选过滤条件、排序、分页和对数据的操作的控制,而不会触发不必要的数据读取。不需要用于过滤、排序的复杂表达式参数或用于管理分页的额外参数。存储库充当 IsActive、授权检查等所需过滤器的看门人。存储库还可以充当实体工厂,确保在创建新实体时提供所有必填字段和引用。我还让存储库管理删除操作,以确保强制执行所有验证和完整性,以及审计记录,并处理软删除场景。 (IsActive)

有些人回避使用 IQueryable,因为它会将 EF 主义“泄漏”到控制器中。然而,它泄露它们只不过是传递表达式以试图抽象出 EF 的复杂方法。每个条件表达式都同样容易需要符合 EF-isms。 (即传递一个引用实体上的私有方法或静态方法的 order-by 表达式)

像这样的存储库模式(相对于只让代码访问 DbSet)的好处是易于测试。模拟存储库只需要返回一个 List&lt;T&gt; AsQueryable 并且您的控制器等可以单独测试。它还为所需的过滤器和针对实体的操作提供了很好的集中化。

【讨论】:

    【解决方案3】:

    问题是 userDoa 没有在你的 DbContext 中注册实体。

    还有,

    public class UsersDao : GenericDao<UsersDo>, IUsers
    {
         public UsersDao(DataContext context) : base (context) {}
         ...
    }
    

    我相信不需要。该问题与您的通用存储库无关。

    public class DataContext : DbContext
    {
         public virtual DbSet<UserDo> UserDos { get; set; }
    }
    
    public class UserDo 
    {
        [Key]
        public int UserId {get;set}
    
        public string Username {get;set}
    }
    

    然后

    var result = new UserContext().Find(x => x.Username == "John");
    

    【讨论】:

    • 是的,我确实在 DbContext 中注册了 UsersDo,我使用的是代码优先方法,并且仅基于 DbContext 在数据库中创建表,并且我的表已经成功创建。
    • 另外我还有另一个通用函数T Get(object Id),它与usersDao.Get(2) 完美配合,所以可能不应该是UsersDo 注册问题,对吗?
    • public T Get(object id) { var find = context.Set&lt;T&gt;().Find(id); if (find != null) return find; return null; }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-21
    • 1970-01-01
    • 1970-01-01
    • 2017-06-23
    • 2012-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多