Squeak.ru - шаблоны программирования

Generic Entity Framework и репозиторий DTO, где проблема лямбда

Я пытаюсь реализовать многоуровневое приложение, используя структуру сущностей и DTO для передачи объектов между уровнями.

Я следил за некоторым кодом в строке, чтобы настроить общий репозиторий, но столкнулся с парой методов, которые не могу решить.

Я использую EntitiesToDTO для создания своих DTO из файла edmx, но, к сожалению, у них нет базового класса (слишком много для обновления вручную)

Чтобы установить сцену, у меня есть следующие 2 общих класса:

IRRepository:

public interface IRepository<TEntity> : IDisposable
{
    /// <summary>
    /// Creates a new empty entity.
    /// </summary>
    TEntity Create();

    /// <summary>
    /// Creates the existing entity.
    /// </summary>
    TEntity Create(TEntity entity);

    /// <summary>
    /// Updates the existing entity.
    /// </summary>
    TEntity Update(TEntity entity);

    /// <summary>
    /// Delete an entity using its primary key.
    /// </summary>
    void Delete(long id);

    /// <summary>
    /// Delete the given entity.
    /// </summary>
    void Delete(TEntity entity);

    /// <summary>
    /// Deletes the existing entity.
    /// </summary>
    void Delete(Expression<Func<TEntity, bool>> where);

    /// <summary>
    /// Finds one entity based on provided criteria.
    /// </summary>
    TEntity FindOne(Expression<Func<TEntity, bool>> where = null);

    /// <summary>
    /// Finds one entity based on its Identifier.
    /// </summary>
    TEntity FindById(long id);

    /// <summary>
    /// Finds entities based on provided criteria.
    /// </summary>
    IQueryable<TEntity> FindAll(Expression<Func<TEntity, bool>> where = null);

    /// <summary>
    /// Finds other related entities based of type T for queries.
    /// </summary>
    IQueryable<T> Set<T>() where T : class;

    /// <summary>
    /// Save any changes to the TContext
    /// </summary>
    bool SaveChanges();
}

Общая реализация:

public class Repository<TEntity, TContext> : IRepository<TEntity>, IDisposable
      where TEntity : class
      where TContext : DbContext
{
    protected TContext Context;

    public Repository(DbContext dbContext)
    {
        Context = dbContext as TContext;
    }

    public virtual TEntity Create()
    {
        return Context.Set<TEntity>().Create();
    }

    public virtual TEntity Create(TEntity entity)
    {
        return Context.Set<TEntity>().Add(entity);
    }

    public virtual TEntity Update(TEntity entity)
    {
        Context.Entry(entity).State = EntityState.Modified;
        return entity;
    }

    public virtual void Delete(long id)
    {
        var item = Context.Set<TEntity>().Find(id);
        Context.Set<TEntity>().Remove(item);
    }

    public virtual void Delete(TEntity entity)
    {
        Context.Set<TEntity>().Remove(entity);
    }

    public virtual void Delete(Expression<Func<TEntity, bool>> where)
    {
        var objects = Context.Set<TEntity>().Where(where).AsEnumerable();
        foreach (var item in objects)
        {
            Context.Set<TEntity>().Remove(item);
        }
    }

    public virtual TEntity FindById(long id)
    {
        return Context.Set<TEntity>().Find(id);
    }

    public virtual TEntity FindOne(Expression<Func<TEntity, bool>> where = null)
    {
        return FindAll(where).FirstOrDefault();
    }

    public IQueryable<T> Set<T>() where T : class
    {
        return Context.Set<T>();
    }

    public virtual IQueryable<TEntity> FindAll(Expression<Func<TEntity, bool>> where = null)
    {
        return null != where ? Context.Set<TEntity>().Where(where) : Context.Set<TEntity>();
    }

    public virtual bool SaveChanges()
    {
        return 0 < Context.SaveChanges();
    }

    /// <summary>
    /// Releases all resources used by the Entities
    /// </summary>
    public void Dispose()
    {
        if (null != Context)
        {
            Context.Dispose();
        }
    }
}

Класс, используемый для преобразования между записью и Order и Order DTO:

/// <summary>
/// Assembler for <see cref="Order"/> and <see cref="OrderDTO"/>.
/// </summary>
public static partial class OrderAssembler
{
    /// <summary>
    /// Invoked when <see cref="ToDTO"/> operation is about to return.
    /// </summary>
    /// <param name="dto"><see cref="OrderDTO"/> converted from <see cref="Order"/>.</param>
    static partial void OnDTO(this Order entity, OrderDTO dto);

    /// <summary>
    /// Invoked when <see cref="ToEntity"/> operation is about to return.
    /// </summary>
    /// <param name="entity"><see cref="Order"/> converted from <see cref="OrderDTO"/>.</param>
    static partial void OnEntity(this OrderDTO dto, Order entity);

    /// <summary>
    /// Converts this instance of <see cref="OrderDTO"/> to an instance of <see cref="Order"/>.
    /// </summary>
    /// <param name="dto"><see cref="OrderDTO"/> to convert.</param>
    public static Order ToEntity(this OrderDTO dto)
    {
        if (dto == null) return null;

        var entity = new Order();

        entity.OrderID = dto.OrderID;
        entity.SupplierID = dto.SupplierID;
        entity.Special = dto.Special;
        entity.RequestedBy = dto.RequestedBy;
        entity.RequestedFor = dto.RequestedFor;
        entity.Urgent = dto.Urgent;
        entity.OrderStatus = dto.OrderStatus;
        entity.DeliveryAddressID = dto.DeliveryAddressID;
        entity.OrderDate = dto.OrderDate;
        entity.Deleted = dto.Deleted;
        entity.SentToSage = dto.SentToSage;
        entity.Cancelled = dto.Cancelled;
        entity.InvoiceAddressID = dto.InvoiceAddressID;
        entity.SageOrderID = dto.SageOrderID;
        entity.SageDatabaseID = dto.SageDatabaseID;
        entity.DeliveryDate = dto.DeliveryDate;
        entity.SupplierReference = dto.SupplierReference;
        entity.Analysis1 = dto.Analysis1;
        entity.Analysis2 = dto.Analysis2;
        entity.Analysis3 = dto.Analysis3;
        entity.Analysis4 = dto.Analysis4;
        entity.Analysis5 = dto.Analysis5;
        entity.Analysis6 = dto.Analysis6;
        entity.OrderDiscount = dto.OrderDiscount;
        entity.SageDatabaseName = dto.SageDatabaseName;
        entity.SupplierName = dto.SupplierName;
        entity.RequestedByName = dto.RequestedByName;
        entity.DeliveryAddressName = dto.DeliveryAddressName;
        entity.NetValue = dto.NetValue;
        entity.DepartmentID = dto.DepartmentID;
        entity.PODocumentNo = dto.PODocumentNo;
        entity.ConstructRelated = dto.ConstructRelated;
        entity.Archived = dto.Archived;
        entity.UpdateStatus = dto.UpdateStatus;
        entity.UpdatedDate = dto.UpdatedDate;
        entity.UpdatedUser = dto.UpdatedUser;
        entity.WarehouseID = dto.WarehouseID;
        entity.ExchangeRate = dto.ExchangeRate;
        entity.CurrencySymbol = dto.CurrencySymbol;
        entity.SupplierEmailAddress = dto.SupplierEmailAddress;
        entity.SupplierContactName = dto.SupplierContactName;
        entity.SupplierTelephone = dto.SupplierTelephone;
        entity.SupplierFax = dto.SupplierFax;
        entity.HasAttachments = dto.HasAttachments;
        entity.HasAnalysisValues = dto.HasAnalysisValues;
        entity.SYSTraderAnalysisValueID = dto.SYSTraderAnalysisValueID;
        entity.InternalOrder = dto.InternalOrder;
        entity.DeliveryPostalName = dto.DeliveryPostalName;
        entity.DeliveryAddressLine1 = dto.DeliveryAddressLine1;
        entity.DeliveryAddressLine2 = dto.DeliveryAddressLine2;
        entity.DeliveryAddressLine3 = dto.DeliveryAddressLine3;
        entity.DeliveryAddressLine4 = dto.DeliveryAddressLine4;
        entity.DeliveryPostCode = dto.DeliveryPostCode;
        entity.InvoicePostalName = dto.InvoicePostalName;
        entity.InvoiceAddressLine1 = dto.InvoiceAddressLine1;
        entity.InvoiceAddressLine2 = dto.InvoiceAddressLine2;
        entity.InvoiceAddressLine3 = dto.InvoiceAddressLine3;
        entity.InvoiceAddressLine4 = dto.InvoiceAddressLine4;
        entity.InvoicePostCode = dto.InvoicePostCode;
        entity.DeliveryContactName = dto.DeliveryContactName;
        entity.InvoiceContactName = dto.InvoiceContactName;
        entity.DeliveryTelephoneNo = dto.DeliveryTelephoneNo;
        entity.DeliveryFaxNo = dto.DeliveryFaxNo;
        entity.InvoiceTelephoneNo = dto.InvoiceTelephoneNo;
        entity.InvoiceFaxNo = dto.InvoiceFaxNo;
        entity.CheckForNewDocuments = dto.CheckForNewDocuments;
        entity.EmailSent = dto.EmailSent;
        entity.DocumentNoPrefix = dto.DocumentNoPrefix;

        dto.OnEntity(entity);

        return entity;
    }

    /// <summary>
    /// Converts this instance of <see cref="Order"/> to an instance of <see cref="OrderDTO"/>.
    /// </summary>
    /// <param name="entity"><see cref="Order"/> to convert.</param>
    public static OrderDTO ToDTO(this Order entity)
    {
        if (entity == null) return null;

        var dto = new OrderDTO();

        dto.OrderID = entity.OrderID;
        dto.SupplierID = entity.SupplierID;
        dto.Special = entity.Special;
        dto.RequestedBy = entity.RequestedBy;
        dto.RequestedFor = entity.RequestedFor;
        dto.Urgent = entity.Urgent;
        dto.OrderStatus = entity.OrderStatus;
        dto.DeliveryAddressID = entity.DeliveryAddressID;
        dto.OrderDate = entity.OrderDate;
        dto.Deleted = entity.Deleted;
        dto.SentToSage = entity.SentToSage;
        dto.Cancelled = entity.Cancelled;
        dto.InvoiceAddressID = entity.InvoiceAddressID;
        dto.SageOrderID = entity.SageOrderID;
        dto.SageDatabaseID = entity.SageDatabaseID;
        dto.DeliveryDate = entity.DeliveryDate;
        dto.SupplierReference = entity.SupplierReference;
        dto.Analysis1 = entity.Analysis1;
        dto.Analysis2 = entity.Analysis2;
        dto.Analysis3 = entity.Analysis3;
        dto.Analysis4 = entity.Analysis4;
        dto.Analysis5 = entity.Analysis5;
        dto.Analysis6 = entity.Analysis6;
        dto.OrderDiscount = entity.OrderDiscount;
        dto.SageDatabaseName = entity.SageDatabaseName;
        dto.SupplierName = entity.SupplierName;
        dto.RequestedByName = entity.RequestedByName;
        dto.DeliveryAddressName = entity.DeliveryAddressName;
        dto.NetValue = entity.NetValue;
        dto.DepartmentID = entity.DepartmentID;
        dto.PODocumentNo = entity.PODocumentNo;
        dto.ConstructRelated = entity.ConstructRelated;
        dto.Archived = entity.Archived;
        dto.UpdateStatus = entity.UpdateStatus;
        dto.UpdatedDate = entity.UpdatedDate;
        dto.UpdatedUser = entity.UpdatedUser;
        dto.WarehouseID = entity.WarehouseID;
        dto.ExchangeRate = entity.ExchangeRate;
        dto.CurrencySymbol = entity.CurrencySymbol;
        dto.SupplierEmailAddress = entity.SupplierEmailAddress;
        dto.SupplierContactName = entity.SupplierContactName;
        dto.SupplierTelephone = entity.SupplierTelephone;
        dto.SupplierFax = entity.SupplierFax;
        dto.HasAttachments = entity.HasAttachments;
        dto.HasAnalysisValues = entity.HasAnalysisValues;
        dto.SYSTraderAnalysisValueID = entity.SYSTraderAnalysisValueID;
        dto.InternalOrder = entity.InternalOrder;
        dto.DeliveryPostalName = entity.DeliveryPostalName;
        dto.DeliveryAddressLine1 = entity.DeliveryAddressLine1;
        dto.DeliveryAddressLine2 = entity.DeliveryAddressLine2;
        dto.DeliveryAddressLine3 = entity.DeliveryAddressLine3;
        dto.DeliveryAddressLine4 = entity.DeliveryAddressLine4;
        dto.DeliveryPostCode = entity.DeliveryPostCode;
        dto.InvoicePostalName = entity.InvoicePostalName;
        dto.InvoiceAddressLine1 = entity.InvoiceAddressLine1;
        dto.InvoiceAddressLine2 = entity.InvoiceAddressLine2;
        dto.InvoiceAddressLine3 = entity.InvoiceAddressLine3;
        dto.InvoiceAddressLine4 = entity.InvoiceAddressLine4;
        dto.InvoicePostCode = entity.InvoicePostCode;
        dto.DeliveryContactName = entity.DeliveryContactName;
        dto.InvoiceContactName = entity.InvoiceContactName;
        dto.DeliveryTelephoneNo = entity.DeliveryTelephoneNo;
        dto.DeliveryFaxNo = entity.DeliveryFaxNo;
        dto.InvoiceTelephoneNo = entity.InvoiceTelephoneNo;
        dto.InvoiceFaxNo = entity.InvoiceFaxNo;
        dto.CheckForNewDocuments = entity.CheckForNewDocuments;
        dto.EmailSent = entity.EmailSent;
        dto.DocumentNoPrefix = entity.DocumentNoPrefix;

        entity.OnDTO(dto);

        return dto;
    }

    /// <summary>
    /// Converts each instance of <see cref="OrderDTO"/> to an instance of <see cref="Order"/>.
    /// </summary>
    /// <param name="dtos"></param>
    /// <returns></returns>
    public static List<Order> ToEntities(this IEnumerable<OrderDTO> dtos)
    {
        if (dtos == null) return null;

        return dtos.Select(e => e.ToEntity()).ToList();
    }

    /// <summary>
    /// Converts each instance of <see cref="Order"/> to an instance of <see cref="OrderDTO"/>.
    /// </summary>
    /// <param name="entities"></param>
    /// <returns></returns>
    public static List<OrderDTO> ToDTOs(this IEnumerable<Order> entities)
    {
        if (entities == null) return null;

        return entities.Select(e => e.ToDTO()).ToList();
    }
}

Моя реализация OrderRepository:

public class OrderRepository : IRepository<OrderDTO>
{
    private Repository<Order, WAPEntities> _repository;

    public OrderRepository()
    {
        _repository = new Repository<Order, WAPEntities>(new WAPEntities());
    }

    public void Dispose()
    {
        _repository.Dispose();
    }

    public OrderDTO Create()
    {
        return _repository.Create().ToDTO();
    }

    public OrderDTO Create(OrderDTO entity)
    {
        return _repository.Create(entity.ToEntity()).ToDTO();
    }

    public OrderDTO Update(OrderDTO entity)
    {
        return _repository.Update(entity.ToEntity()).ToDTO();
    }

    public void Delete(long id)
    {
        _repository.Delete(id);
    }

    public void Delete(OrderDTO entity)
    {
        _repository.Delete(entity.ToEntity());
    }

    public void Delete(Expression<Func<OrderDTO, bool>> where)
    {
        // I have tried this but it wont work
        var resultBody = Expression.Convert(where.Body, typeof(OrderDTO));
        var result = Expression.Lambda<Func<Order, bool>>(resultBody, where.Parameters);

        _repository.Delete(result);
    }

    public OrderDTO FindOne(System.Linq.Expressions.Expression<Func<OrderDTO, bool>> where = null)
    {
        //Here the same issue with the Where clause
        throw new NotImplementedException();
    }

    public OrderDTO FindById(long id)
    {
        return _repository.FindById(id).ToDTO();
    }

    public IQueryable<OrderDTO> FindAll(System.Linq.Expressions.Expression<Func<OrderDTO, bool>> where = null)
    {
        //Here the same issue with the Where clause
        throw new NotImplementedException();
    }

    public IQueryable<T> Set<T>() where T : class
    {
        return _repository.Set<T>();
    }

    public bool SaveChanges()
    {
        return _repository.SaveChanges();
    }
}

Как видите, для большинства моих методов репозитория заказов я могу довольно легко преобразовать сущности в DTO и из DTO в сущности.

Я борюсь с тем, как преобразовать это лямбда-предложение where для преобразования из лямбда-выражения, которое берет мой класс DTO Order, вызывает для него entity.ToDTO() и преобразует в лямбда-выражение для сущности заказа.

Методы, которые я не могу разработать:

public void Delete(Expression<Func<OrderDTO, bool>> where)

public OrderDTO FindOne(System.Linq.Expressions.Expression<Func<OrderDTO, bool>> where = null)

public IQueryable<OrderDTO> FindAll(System.Linq.Expressions.Expression<Func<OrderDTO, bool>> where = null)

Это вообще возможно?


  • Вы знаете об AutoMapper? Это очень хорошо, и, возможно, это поможет много. Это не имеет прямого отношения к вашему вопросу, хотя 14.09.2013
  • Я наткнулся на него сегодня, когда читал об EF, я нашел его после того, как нашел расширение EntiesToDTos для Visual Studio. Я видел несколько комментариев, в которых говорилось, что это может быть проблемой с EF, поэтому это меня немного оттолкнуло :/ 14.09.2013
  • Я использую его с E.F 4.5, и он отлично работает! Вы можете отобразить класс всего двумя строками кода. То же самое относится и к списку объектов. У него очень хорошие функциональные возможности. 14.09.2013
  • Спасибо, я проверю это, на данный момент все еще на стадии исследований и разработок, пытаясь реорганизовать более крупный продукт и пытаясь избежать кодирования тысяч классов DTO! 14.09.2013
  • только что быстро взглянул на auto mapper, и похоже, что он делает то же самое, что и мой класс ассемблера заказов (хотя он намного красивее, однако я думаю, что у меня все еще будет проблема с тем, как скрыть эти лямбда-выражения!) 14.09.2013

Ответы:


1

Ваша проблема здесь в том, что вы пытаетесь рассматривать свои DTO, как если бы они были сущностями. Они не.

Сущности - это Сущности. Это объекты, которые отображаются в вашем контексте данных. DTO — это просто простые объекты, используемые для передачи данных, как следует из их названия. Если бы ваши DTO были точно такими же, как ваши сущности, не было бы причин иметь их обоих.

Когда дело доходит до вашей лямбды... вы говорите о двух разных вещах. Лямбда-выражение для DTO — это не то же самое, что лямбда-выражение для сущности. Хотя вы можете изменить дерево выражений, это не весело и требует много работы.

Я должен спросить, почему вы вводите эту дополнительную абстракцию, если все, что вы собираетесь делать, это обращаться с ней точно так же, как со своими сущностями. Если ваша абстракция не представляет ценности, я бы избегал ее... если вы не ожидаете, что позже будете менять технологию базы данных... в этом случае, возможно, это принесет некоторую пользу... но даже если это так, вы все еще обрабатываете свои DTO как если бы они были объектами EF, что означает выполнение логического преобразования любой новой технологии, которую вы используете.

Вы должны подумать о сценариях, для которых вы собираетесь использовать лямбду. Я предполагаю, что, скорее всего, это связано с коллекциями объектов. В таком случае, почему бы просто не создать функции, которые берут коллекцию DTO, а затем переводят ее в вашу лямбду... это было бы намного проще.

Реальное преимущество этого заключается в том, что если вы измените свою модель данных, вам нужно будет изменить только свой репозиторий. Когда вы начинаете использовать лямбды повсюду в своем коде, каждый раз, когда вы что-то меняете, вам приходится искать везде, где вы используете эту лямбду. Очень полезно централизовать ваш лямбда-код.

14.09.2013
  • Взять коллекцию и преобразовать ее в лямбду? Я ломаю голову над тем, что вы могли иметь в виду. 14.09.2013
  • @WiktorZychla - я имею в виду использование выражения для выполнения действия над элементами на основе содержимого коллекции, например, с использованием содержимого. repository.Delete(x => itemsToDelete.Contains(x.Id)) или что-то подобное. 15.09.2013
  • Спасибо, возможно, я просто делаю работу для себя здесь, у меня также была мысль прошлой ночью, я мог бы просто выставить набор БД в защищенном свойстве, а затем я мог бы написать прямые запросы linq по нему, когда это необходимо 15.09.2013
  • @WraithNath - если вы собираетесь это сделать, то вы фактически сводите на нет цель своего репозитория. Вы должны либо усилить разделение, либо избавиться от всего этого вместе. 15.09.2013
  • @MystereMan - спасибо, я решил избавиться от этого, теперь объекты из уровня данных используются в других слоях, это также позволило мне использовать общие репозитории как есть. 16.09.2013
  • Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...