Во-первых, spec. Мы используем MVC5,.NET 4.5.1 и Entity framework 6.1.
В нашем бизнес-приложении MVC5 у нас много повторяющегося кода CRUD. Моя задача - "автоматизировать" большую часть, что означает извлечение ее на базовые классы и ее повторное использование. Прямо сейчас у меня есть базовые классы для контроллеров, моделей представлений и моделей сущностей EF6.
Мой абстрактный базовый класс, который наследует все объекты EF6:
public abstract class BaseEntity<TSubclass>
where TSubclass : BaseEntity<TSubclass>
{
public abstract Expression<Func<TSubclass, object>> UpdateCriterion();
}
UpdateCriterion
метод используется в AddOrUpdate
методе контекста базы данных. У меня есть общий параметр для подклассов, потому что UpdateCriterion
должен возвращать лямбда-выражение, которое использует точный тип подкласса, а не интерфейс или базовый класс. Очень упрощенный подкласс, реализующий этот абстрактный базовый класс, будет выглядеть так:
public class Worker : BaseEntity<Worker>
{
public int ID { get; set; }
public int Name { get; set; }
public override Expression<Func<Worker, object>> UpdateCriterion()
{
return worker => worker.ID;
}
}
После этого, в SaveOrUpdate
действии моего базового контроллера, у меня был бы такой код:
public ActionResult Save(TViewModel viewModel)
{
if (ModelState.IsValid)
{
var entityModel = viewModel.ConstructEntityModel();
db.Set<TEntityModel>().AddOrUpdate<TEntityModel>(entityModel.UpdateCriterion(), entityModel);
db.SaveChanges();
}
}
Благодаря этому подклассам базового контроллера не нужно внедрять метод Save
, как это было раньше. Теперь все это работает, и на самом деле он работает очень хорошо, несмотря на фанковый синтаксис (я имею в виду class BaseEntity<TSubclass> where TSubclass : BaseEntity<TSubclass>
, серьезно?).
Вот моя проблема. Для большинства объектов поле ID
является ключом, но для некоторых это не так, поэтому я не могу правильно обобщить реализацию суперкласса. Итак, на данный момент каждый подкласс объекта реализует его собственный UpdateCriterion
. Но, поскольку для большинства (90% +) объектов e => e.ID
является правильной реализацией, у меня много дублирования. Поэтому я хочу переписать базовый класс сущности на что-то вроде этого:
public abstract class BaseEntity<TSubclass>
where TSubclass : BaseEntity<TSubclass>
{
public virtual Expression<Func<TSubclass, object>> UpdateCriterion()
{
return entity => ((dynamic)entity).ID;
}
}
Цель состоит в том, чтобы обеспечить реализацию по умолчанию, использующую ID как ключ, и позволить подклассам переопределять ее, если они используют другой ключ. Я не могу использовать интерфейс или базовый класс с полем ID, потому что не все его имеют. Я решил использовать dynamic
для вывода ID
поля, но я получаю следующую ошибку: Error: An expression tree may not contain a dynamic operation
.
Итак, любая идея о том, как это сделать? Будет ли отражение работать в базе UpdateCriterion
?