Linq到具有存储库模式和EF的实体

时间:2019-01-06 21:33:17

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

我最近一直在研究实体框架和存储库模式,在存储库类中,我创建了一个名为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) {}
     ...
}

3 个答案:

答案 0 :(得分:0)

你能试试吗

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");
    }
}

答案 1 :(得分:0)

我强烈建议您使用简化的存储库模式,该模式将有助于模拟您的数据源以进行测试,并提供更大的灵活性。我不建议使用通用存储库,而是将存储库视为类似于Controller的存储库。 (我为一组特定的操作提供数据)尽管相对于DNRY更支持SRP,但减少了依赖性引用的数量。

例如:

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,授权检查等)的关守。该存储库还可以充当实体工厂,确保在创建新实体时提供所有必填字段和引用。我还让存储库管理删除操作,以确保执行所有验证和完整性以及审核记录,并处理软删除方案。 (处于活动状态)

有些人回避使用IQueryable,因为它会将EF-ism“泄漏”到控制器中。但是,它泄漏它们的方法不过是传递表达式以试图提取EF的复杂方法而已。每个条件表达式同样容易需要遵循EF-ism。 (即,传递引用实体上的私有方法或静态方法的order-by表达式)

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

答案 2 :(得分:0)

问题是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");