Обновление модели на уровне сервиса без чтения всей сущности в MVVM - PullRequest
0 голосов
/ 24 апреля 2018

Как известно, сервисный уровень отвечает за обновление (и, конечно, чтение, запись и удаление) моделей (или я могу назвать их сущностями). Давайте пока проигнорируем слой Repository, потому что он мне не нравится, он просто скрывает множество возможностей Entity Framework.

Поток данных в хорошо спроектированной системе должен быть:

Service (model) <<<-->>> Controller (mapping Model <-> ViewModel)

Чтобы иметь возможность обновлять модель в базе данных с помощью вышеуказанного потока данных, я должен прочитать всю модель из базы данных в службу для контроллера и использовать AutoMapper (например) для сопоставления данных, полученных от меня. ViewModel для модели (теперь у нас есть старая модель с некоторыми измененными значениями). Теперь я могу передать эту отредактированную модель обратно в службу и выполнить DbContext.Update(Model).

Минусы этого:

  • Нам пришлось выполнить дополнительный запрос на чтение, чтобы прочитать всю модель (мы должны были сделать это, в противном случае DbContext.Update(Model) оставит свойство без отображения по умолчанию).

  • Кроме того, для всей модели был сгенерирован запрос на обновление (хотя я мог бы изменить только небольшую часть модели.)

Иногда я обнаруживаю, что шаблоны проектирования заставляют скрывать множество функций, которые могут сделать программу более эффективной.

Существует ли какой-либо подход (или, скажем, шаблон проектирования или любое редактирование шаблона службы), в котором я могу сопоставить ViewModel с моделью, передать модель в службу и обновить только сопоставленные свойства (так что в этом нет необходимости) прочитать всю сущность перед сопоставлением свойств)?

1 Ответ

0 голосов
/ 25 апреля 2018

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

Вы говорите

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

Дело в том,этот сервисный уровень не должен использоваться таким образом.Я имею в виду - у вас вообще не должно быть так называемых услуг.То, что вы описываете, известно как модель анемичной области .Это комбинация сущностей и сервисов, где сущности являются простыми структурами данных (а не собственными объектами!), А сервисы используются для выполнения операций над сущностями.

По словам Мартина Фаулера

[...] существует набор сервисных объектов, которые захватывают всю логику домена, выполняют все вычисления и обновляют объекты модели результатами.Эти сервисы работают поверх модели домена и используют модель домена для данных.

И это плохо.

Объединение методов и данных внутри одного объекта,Тогда то, что вы будете искать, это шаблон проектирования Unit of Work.На самом деле Entity Framework уже реализует этот шаблон.Что касается реализации этого подхода с расширенными объектами с помощью EF (или фактически любого ORM, который вы можете использовать в .NET), я рекомендую пост Вона Вернона о проектировании агрегатов с EF .Невозможно суммировать весь этот пост, но во избежание ответа только по ссылке: в основном Вон предлагает два метода: создание отдельного интерфейса с помощью класса реализации и объекта домена, для которого создается резервная копия чем-то, что он называет объектом состояния.

Чтобы ответить на комментарии: в основном да - предложение сводить данные и методы вместе, вместо того, чтобы иметь одну так называемую сущность (которая в большинстве случаев является простой структурой данных, используемой только для получения данных из БД) и отдельный класс, называемый сервисом, который выполняет действия надсущность.

Какая бы хорошая и забавная идея это ни звучало, это все еще теоретически, верно?Вторая ссылка о том, как применить этот подход к работе.Естественно, когда вы начнете проводить рефакторинг своего кода, поворачивающегося из модели анемичного домена в модель расширенного домена, вам придется задать себе вопрос - как мне сохранить мой богатый объект в базе данных?Я имею в виду - вся цель этого состоит в том, чтобы заключить бизнес (доменную) логику в объект без необходимости иметь дело со всем этим техническим беспорядком.Мы стремимся к тому, чтобы как можно более чистый объект C # имел как можно меньше зависимостей.Давайте поговорим о примерах.Представьте, что у вас есть Order и Product класс.

[Table("orders")]
class Order
{
    [Key]
    public int Id { get; set; }
    public List<Product> Products { get; set; }
    // rest omitted for clarity
}

[Table("products")]
class Product
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    // rest omitted for clarity
}

В модели с анемичной областью у вас будет служба, выполняющая такие действия, как

class OrderService
{
    public void AddProduct(int orderId, int productId)
    {
        Order order = this.orderRepository.FirstOrDefault(orderId);
        Product product = this.productRepository.FirstOrDefault(productId);

        if(!order.Products.Contains(product)
        {
            order.Products.Add(product);
        }

        this.orderRepository.Save(order);
    }
}

С моделью с расширенной областью у вас будет класс, подобныйthis

[Table("orders")]
class Order
{
    [Key]
    public int Id { get; set; }
    public List<Product> Products { get; set; }

    public bool AddProduct(Product product)
    {
        if(this.Products.Contains(product))
        {
            return false;
        }

        order.Products.Add(product);

        return true;
    }

}

Этот класс будет использоваться в контроллере как

class OrderController
{
    public ActionResult AddProduct(int orderId, int productId){
        Order order = this.orderRepository.FirstOrDefault(orderId);
        Product product = this.productRepository.FirstOrDefault(productId);

        bool productAdded = order.AddProduct(product);

        // do something else
    }
}

Обратите внимание на две вещи: 1. Мы переместили технические вещи в контроллер и инкапсулировали бизнес-логику внутри класса Order (давайтепритвориться, что if(this.Products.Contains(product) - наше единственное бизнес-правило) [хорошо] 1. Order класс должен иметь только деловые вещи.Специфичные для ORM аннотации не вносят ничего, кроме технического шума, когда дело доходит до чтения и понимания этого класса, не говоря уже о том, что у нас есть ненужные зависимости внутри нашей модели [плохая вещь]

Вон обсуждает два способа решения этой проблемы.Вы либо извлекаете отдельный интерфейс с помощью своих бизнес-методов

interface IOrder
{
    bool AddProduct(Product product);
}

, а затем реализуете его в своем классе, например class Order : IOrder.Недостатки этого решения указаны в сообщении в блоге

Вездесущий язык на самом деле не подкреплен использованием интерфейсов, таких как IProduct, IBacklogItem и т. Д. IProduct и IBacklogItem не на нашем вездесущем языке, а Product и BacklogItem. Таким образом, клиентские имена должны быть Product, BacklogItem и тому подобное. Мы могли бы сделать это просто, назвав интерфейсы Product, BacklogItem, Release и Sprint, но это означало бы, что нам придется придумывать разумные имена для классов реализации. Давайте просто остановимся там и перейдем ко второму и связанному с ним вопросу.

и

На самом деле нет веских причин для создания отдельного интерфейса. Маловероятно, что мы когда-либо создадим две или более реализации IProduct или любого другого интерфейса. Лучшая причина, по которой мы имеем создание отдельного интерфейса, - это когда может быть или есть несколько реализаций, и это просто не произойдет в этом базовом домене.

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

В конце концов, наша цель - держаться подальше от Entity Framework

Это действительно широкий предмет. Трудно попытаться объяснить это таким способом - на эту тему есть целые книги ;-). Как правило, я бы порекомендовал вам прочитать книгу Вернона под названием Реализация доменного дизайна . Речь идет о DDD, но также показывает, как можно написать правильный объектно-ориентированный код.

...