Разобрался - это было нелегко, поэтому я постараюсь объяснить как можно лучше.(для тех, кому не все равно)
Соответствующий код контроллера:
// _userContentService is IUserContentService
_userContentService.Update(review);
Итак, мой контроллер вызывает метод, называемый Update
на IUserContentService
, проходя черезстрого типизированный Review
объект.
Соответствующий код службы пользовательского контента
public void Update(Post post)
{
// _userContentRepository is IPostRepository
_userContentRepository.UpdateModel(post);
}
Итак, моя служба вызывает метод UpdateModel
для IPostRepository
,проходя через строго типизированный Review
объект.
Теперь вот сложная часть.
У меня на самом деле нет конкретных репозиториев .У меня есть универсальный репозиторий с именем GenericRepository<T> : IRepository<T>
, который обрабатывает всех различных репозиториев.
Так что, когда что-то запрашивает IPostRepository
(что делал мой сервис)Я бы дал ему GenericRepository<Post>
.
Но теперь я даю ему PostRepository
:
public class PostRepository : GenericRepository<Post>, IPostRepository
{
public void UpdateModel(Post post)
{
var originalPost = CurrentEntitySet.SingleOrDefault(p => p.PostId == post.PostId);
Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}
}
И потому, что класс наследуется от GenericRepository , он наследует всю логику репозитория ядра (Найти, Добавить и т. Д.).
Сначала я попытался поместить этот код UpdateModel в GenericRepository сам класс (и тогда мне не понадобился бы этот конкретный репозиторий), но проблема в том, что логика извлечения существующего объекта основана на конкретном ключе объекта, о котором GenericRepository<T>
не знал бы.
Но конечным результатом является сшивание , скрытое глубоко в глубине слоя данных, и я получаю действительно чистый контроллер.
EDIT
Этот "метод заглушки" также работает:
public void UpdateModel(Post post)
{
var stub = new Review {PostId = post.PostId};
CurrentEntitySet.Attach(stub);
Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}
Но проблемаэто потому, что Post является абстрактным, я не могу создать экземпляр и, следовательно, должен будет проверить тип Post и создать заглушки для каждого производного типа.Не совсем вариант.
РЕДАКТИРОВАТЬ 2 (ПОСЛЕДНЕЕ ВРЕМЯ)
Хорошо, у нас есть «метод заглушки», работающий с абстрактными классами, поэтому теперь проблема параллелизма решена.
Я добавил параметр общего типа в свой метод UpdateModel и специальное ограничение new () .
Реализация:
public void UpdateModel<T>(T post) where T : Post, new()
{
var stub = new T { PostId = post.PostId };
CurrentEntitySet.Attach(stub);
Context.ApplyCurrentValues(GetEntityName<Post>, post);
}
Интерфейс:
void UpdateModel<T>(T post) where T : Post, new();
Это избавляет меня от необходимости определять тип T вручную, предотвращает проблемы параллелизмаа также предотвращает дополнительную поездку в БД.
Довольно заводной.
РЕДАКТИРОВАТЬ 3 (я думал, что последний раз был в последний раз)
Вышеприведенная «методика заглушки» работает, но если я предварительно извлекаю объект, он выдает исключение, сообщающее, что сущность с таким ключом уже существует в OSM.
Может кто-нибудь посоветовать, как с этим справиться?
РЕДАКТИРОВАТЬ 4 (ОК - это оно!)
Я нашел решение, благодаря этому SO ответу: Является ли яМожно ли проверить, подключен ли объект к контексту данных в Entity Framework?
Я пытался «проверить, присоединен ли объект», используя следующий код:
ObjectStateEntry entry;
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(entity, out entry);
Но он всегда возвращал ноль , даже когда я исследовал OSM, я мог видеть свою сущность там с тем же ключом.
Но этот код работает:
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), entity), out entry)
Может быть, из-за того, что я использую Pure POCO, у OSM возникли проблемы с определением ключа сущности, кто знает.
Да, и еще одну вещь, которую я добавил - чтобы мне не нужно было добавлять конкретныйрепозиторий для каждой сущности, я создал атрибут с именем " [EntityKey] " (атрибут публичного свойства).
Все POCO должны иметь 1 открытое свойство, украшенное этим атрибутом, или я выбрасываю исключениев моем модуле репозитория.
Таким образом, мой общий репозиторий затем ищет это свойство для создания / настройки заглушки.
Да - он использует отражение, но это умное отражение (на основе атрибутов)и я всеготов использовать отражение для преобразования имен наборов сущностей из T.
В любом случае, проблема решена - теперь все работает нормально!