Многоуровневое решение - тип «Заказчик» определен в сборке, на которую нет ссылок - PullRequest
0 голосов
/ 06 марта 2020

У меня есть следующий макет для моего решения.

  • Уровень данных
  • Уровень сущностей (POCO для создания первой базы кода)
  • Уровень служб (Web) API)
  • Веб-слой (MVC Слой для презентации)

Я создал модели на уровне веб-API, которые имитируют c сущности в слое сущностей, поэтому я могу легче ссылаться на свойства. У меня есть те, которые называются моделями. Я хотел бы, чтобы уровень API выполнял работу с данными на случай, если я захочу обновить веб-уровень позже или go в другом направлении. Я использовал инструмент создания контроллера VS 2019, и я ссылался на мою модель сервисного уровня как модель и контекст данных из моего уровня данных. Я получаю следующую ошибку:

"Error  CS0012  The type 'Customer' is defined in an assembly that is not referenced. You must add a reference to assembly 'XX.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.XX.Services"

Я понимаю, почему я это понимаю, Модель на уровне Сервиса (хотя и соответствует свойствам) не соответствует пространству имен. Как можно ссылаться на уровень данных, не используя что-то вроде AutoMapper? Кажется, я упускаю что-то очевидное, но, возможно, нет.

1 Ответ

1 голос
/ 06 марта 2020

Если ваш уровень Сервисов будет содержать вашу логику c и взаимодействовать с вашими сущностями, а ваши MVC Контроллеры в основном anemi c, проходящие через Сервисы, похоже, что вы хотите, чтобы ваши Сервисы возвращались Модели (DTO или ViewModels), которые отделяют данные, возвращаемые от ваших сущностей.

Для этого Службам потребуется ссылка на ваши сущности, поскольку именно таков уровень данных. В идеале уровень данных должен возвращать IQueryable<TEntity>, а не IEnumerable<TEntity> или даже TEntity, чтобы службы могли уточнить запросы для эффективности, не возвращая больше данных, чем им нужно, или добавляя много сложности или большое количество подобных одноцелевые методы в слое данных.

Я не уверен, каково ваше отвращение к Automapper, но он идеально подходит для обработки преобразования и копирования данных между Entity и ViewModel. Безусловно, вы можете сделать это без Automapper, используя Select метод Linq.

Например, чтобы получить список OrderModels из вашего сервиса:

public IEnumerable<OrderModel> GetOrdersForCustomer(int customerId, int pageNumber, int pageSize)
{
    var orders = OrderRepository.GetByCustomerId(customerId)
        .OrderByDescending(x => x.CreatedAt)
        .Select(x => new OrderModel
        {
             OrderId = x.OrderId,
             OrderNumber = x.OrderNumber,
             CreatedAt = x.CreatedAt,
             // ... et al, 
             OrderItems = x.OrderItems.Select( oi => new OrderItemModel
             {
                 OrderItemId = oi.OrderItemId,
                 ProductId = oi.Product.ProductId,
                 ProductName = oi.Product.Name,
                 Quantity = oi.Quantity,
                 UnitPrice = oi.Product.Price,
                 // ...
             }).ToList()
        }).Skip(pageNumber*pageSize)
        .Take(pageSize)
        .ToList();
     return orders;
}

Неуклюжий, но гибкий Работа выполнена. С настроенным сопоставлением Automapper:

public IEnumerable<OrderModel> GetOrdersForCustomer(int customerId, int pageNumber, int pageSize)
{
    var orders = OrderRepository.GetByCustomerId(customerId)
        .OrderByDescending(x => x.CreatedAt)
        .ProjectTo<OrderModel>()
        .Skip(pageNumber*pageSize)
        .Take(pageSize)
        .ToList();
     return orders;
}

Ключ к чему-то подобному, работающему эффективно с точки зрения производительности и использования памяти, заключается в том, что OrderRepository.GetCustomerById(int) возвращает IQueryable<Order> не IEnumerable<Order> Это позволяет Select / ProjectTo и Skip & Take для компиляции до SQL, который возвращает только столбцы, необходимые для заполнения ваших моделей.

ViewModels / DTOs не должны соответствовать своим аналогам Entity 1: 1. Вам нужно только включить поля, которые, как вы знаете, будут использовать потребители. Это помогает защитить вашу схему, но также может оптимизировать объем данных, передаваемых по беспроводной сети, повышая производительность и уменьшая использование памяти сервера. Вы можете определить столько моделей представлений, сколько вам нужно для сущности, и использовать наследование, чтобы расширить их, если это необходимо.

При переходе другим способом, например при выполнении вставки или обновления, Automapper может помочь упростить переходы данных, и даже охватывают правила, чтобы гарантировать, что данные, которые не должны быть изменены, никогда не будут изменены. Вы можете загрузить графы сущностей в вашем сервисе, как описано выше, но с помощью Include для предварительной выборки связанных данных, которые могут быть обновлены, и выбора конкретной сущности для обновления. С Automapper вы можете обрабатывать как вставные сценарии ios, так и обновления:

Вставки:

var newOrder = _mapper.Map<Order>(orderViewModel);
_context.Orders.Add(newOrder);
_context.SaveChanges();

Обновления:

var existingOrder = _context.Orders.Single(x => x.OrderId = orderViewModel.OrderId);
_mapper.Map(orderViewModel, existingOrder);
_context.SaveChanges();

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

...