Как разрешить предупреждение «Не вызывать переопределенные методы в конструкторах» для виртуальных объектов Entity Framework? - PullRequest
0 голосов
/ 24 сентября 2019

Я использую Entity Framework и хочу использовать ленивую загрузку для свойств, поэтому я делаю свойства virtual.

Пример:

public class Child
{
    public string Name { get; set; }
}

public class Parent
{
    public Parent()
    {
        Child = new Child();
    }

    public virtual Child Child { get; set; }
}

Когда я делаючто я получаю предупреждение CA2214 :

Severity    Code    Description Project File    Line    Suppression State
Warning CA2214  'Parent.Parent()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: 

Parent..ctor()
Parent.set_Child(Child):Void    Project C:\Parent.cs    18  Active

Я бы хотел удалить это предупреждение, но если я помету Parent как sealed, я получу ожидаемую ошибку:

Severity    Code    Description Project File    Line    Suppression State
Error   CS0549  'Parent.Child.get' is a new virtual member in sealed class 'Parent' Project C:\Parent.cs    24  N/A

Так, как я могу разрешить это предупреждение (не игнорируя его) и все еще использовать virtual?

1 Ответ

0 голосов
/ 25 сентября 2019

Используйте инициализатор для свойства, а не конструктор.

public class Parent
{
    public virtual Child Child { get; set; } = new Child();
}

Редактировать: Относительно вышеизложенного и того, что

... бывают случаи, когда мне нужноустановить свойства для Child в конструкторе ...

Простое правило: "Вы, вероятно, не должны".Роль объекта состоит в том, чтобы представлять состояние данных для этого объекта, не более того.Инициализация «графа» сущности должна выполняться не в конструкторе сущности верхнего уровня, а с использованием шаблона Factory.Например, я использую шаблон Repository с EF, который управляет не только геттерами, но и служит фабрикой, предоставляющей методы Create, а также обрабатывающей Delete для сценариев мягкого удаления.Это помогает гарантировать, что сущность с зависимостями всегда создается в «минимально завершенном» и действительном состоянии.

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

using (var context = new MyContext ()) {var parent = new Parent ();parent.Name = "Myself";context.SaveChanges ();}

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

Единственное место, где я бы рекомендовал автоматическую инициализацию, - это коллекции:

public class Parent
{
    public virtual ICollection<Child> Children { get; internal set; } = new List<Child>();
}

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

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

В качестве примера, если у меня есть сущность Orderчто я могу создать, который состоит из номера заказа, ссылки на клиента и одной или нескольких строк заказа для продуктов, которые необходимы для сохранения действительного заказа, мой OrderRepository может иметь метод CreateOrder что-то вроде этого:

public Order CreateOrder(int customerId, IEnumerable<OrderedProductViewModel> orderedProducts)
{
    if (!orderedProducts.Where(x => x.Quantity > 0).Any())
       throw new ArgumentException("No products selected.");

    var customer = Context.Customers.Single(x => x.CustomerId == customerId);
    var products = Context.Products.Where(x => orderedProducts.Where(o => o.Quantity > 0).Select(o => o.ProductId).Contains(x.ProductId)).ToList();
    if (products.Count() < orderedProducts.Count())
       throw new ArgumentException("Invalid products included in order.");
   var order = new Order 
   { 
      Customer = customer,
      OrderLines = orderedProducts.Select(x => new OrderLine 
      { 
         Product = products.Single(p => p.ProductId == x.ProductId),
         Quantity = x.Quantity
      }
   }
   Context.Orders.Add(order);
   return order;
}

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

Я мог бы вызвать код вызова, например:

using (var contextScope = ContextScopeFactory.Create())
{
    var order = OrderRepository.Create(selectedCustomerId, selectedProducts);
    contextScope.SaveChanges();
}

И это было бы здорово.Или я мог бы продолжить устанавливать доступную информацию о заказе перед вызовом SaveChanges.Мой OrderRepository не будет иметь какой-либо бизнес-логики, потому что эта бизнес-логика может зависеть от конфигурации для клиента, и у репозитория нет деловой информации об этом;но он может иметь зависимость для чего-то вроде IOrderValidator для передачи недавно предложенного Ордера, в который может перейти бизнес-логика, чтобы утверждать, что Орден достаточно действителен для сохранения.Репозиторий / фабрика утверждает, что он достаточно завершен, но его можно привязать к валидатору обратно в бизнес-логике, чтобы утверждать, что он достаточно действителен.(Т.е. минимальный / максимальный размер / стоимость заказа и т. Д.)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...