Linq для сущностей с репозиторием Pattern и EF - PullRequest
0 голосов
/ 07 января 2019

Я недавно работал над структурой Entity Framework и Repository, в классе репозитория я создал функцию с именем 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.

Кто-нибудь может подсказать, что здесь происходит не так.

EDIT
в классе репозитория у меня есть конструктор:

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 голосов
/ 07 января 2019

Я настоятельно рекомендую использовать упрощенный шаблон репозитория, который поможет при проверке источника данных для тестирования и обеспечит большую гибкость. Я не рекомендую использовать универсальные репозитории, а скорее для обработки репозитория, похожего на контроллер. (Я работаю с данными для определенного набора операций) Это сокращает количество ссылок на зависимости, хотя предпочитает 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-измам. (То есть, передавая упорядоченное выражение, которое ссылается на закрытый метод на объекте или статический метод)

Преимущество такого шаблона хранилища (по сравнению с простым доступом к коду DbSets) - простота тестирования. Модерируемое хранилище просто должно вернуть List<T> AsQueryable, и ваши контроллеры и т. Д. Могут быть протестированы изолированно. Он также обеспечивает хорошую централизацию необходимых фильтров и операций с объектами.

0 голосов
/ 07 января 2019

Проблема в том, что 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");
0 голосов
/ 07 января 2019

Можете ли вы попробовать это

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");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...