#domain-driven-design #repository #inversion-of-control #autofac
#проектирование, управляемое доменом #репозиторий #инверсия контроля #autofac
Вопрос:
У меня есть два типа POCO, A и B. У меня есть репозиторий для каждого из них, Rep <A
> и Rep <B
>, оба из которых реализуют IRep <A
> и IRep <B
> обслуживается контейнером IoC (в данном случае AutoFac).
Существует несколько видов репозиториев — загрузка по требованию из базы данных (или чего-либо еще), отложенная загрузка коллекций в память, кэшированные результаты веб-службы и т.д. Вызывающие пользователи не могут заметить разницу. Как Rep <A
>, так и Rep <B
> являются коллекциями в памяти, поскольку A и B не сильно меняются и живут долгое время.
Одним из свойств B является A. Что я делаю сейчас, так это то, что когда у B запрашивается его A, B получает IRep <A
> для поиска своего A и возвращает его. Это происходит каждый раз — каждый запрос для B’s A включает IRep <A
>.Find(). Преимущество в том, что B никогда не привязываются к A, и каждый запрос учитывает независимо от состояния Rep, в котором он находится. Недостатком является большой отток IoC / IRep <A
.
Я подумываю использовать здесь шаблон Lazy <T
>, чтобы a B запрашивал IRep <A
> один раз и сохранял то, что он получил. Но что произойдет, если A будет удален из его репозитория?
Я ищу чистый способ для представителя <A
> уведомлять всех, кто заинтересован, когда это изменилось. В моем примере может быть удален определенный B A, поэтому я хотел бы, чтобы Rep <A
> вызывал событие, когда что-то удаляется или добавляется и т.д. Представитель <B
> может подписаться на это событие, чтобы очистить любые B, которые ссылаются на A, которые теперь исчезли, и т.д. Как это подключить?
В идеале ничего не меняется при создании экземпляра Rep <A
>. Он не должен иметь представления о том, кто слушает, и A могут манипулировать весь день, даже не вызывая представителя.
Но когда появляется Rep <B
>, ему нужен способ подписаться на событие Rep <A
>. Возможно, представителя <A
> еще нет в живых, но, несомненно, он появится, как только B попросят указать его A, так что, кажется, можно запустить представителя <A
>.
По сути, при создании экземпляра Rep <B
> требуется, чтобы он регистрировался в Rep <A
> для уведомления о событии. Я не хочу загрязнять интерфейс IRep <T
>, потому что это не должно иметь значения ни для кого за пределами уровня репозитория. И другим типам репозиториев, возможно, вообще не придется беспокоиться об этом.
Имеет ли это какой-либо смысл?
Ответ №1:
Что, если вы заставили Rep<A>
возвращать «наблюдаемый» объект, который может вычисляться до A, а также имеет событие подписки, которое возникает, когда что-то в этом A изменяется? Просто мысль. Таким образом, вам не нужно проверять обработчики, чтобы убедиться, что их A изменилось; если событие, которое они прослушивают, запущено, это касается их экземпляра, а не любого другого.
Вы могли бы закодировать это следующим образом:
public class Observable<T>:IDisposable
{
private T instance;
public T Instance
{
get{return instance;}
set{
instance = value;
var handlers = ReferenceChanged;
if(handlers != null) handlers(this, instance);
}
public static implicit operator T(Observable<T> obs)
{
return obs.Instance;
}
//DO NOT attach anonymous delegates or lambdas to this event, or you'll cause a leak
public event EventHandler<T> ReferenceChanged;
public void Dispose()
{
var handlers = ReferenceChanged;
if(handlers != null) handlers(this, null);
foreach(var handler in handlers) ReferenceChanged -= handler;
}
}
public class Rep<T>
{
private Dictionary<T, Observable<T>> observableDictionary = new Dictionary<T, Observable<T>>();
...
public Observable<T> GetObservableFactory(Predicate<T> criteria)
{
//criteria should test only uniquely-identifying information
if(observableDictionary.Keys.Any(criteria))
return observableDictionary[observableDictionary.Keys.First(criteria)];
else
{
//TODO: get object from source according to criteria and set to variable queryResult
var observable = new Observable<T>{Instance = queryResult};
observableDictionary.Add(queryResult, observable);
return observable;
}
}
}
...
var observableA = myRepA.GetObservable(myCriteria);
observableA.ReferenceChanged = DoSomethingWhenReferenceChanges;
Теперь потребляющий код будет уведомлен, если внутренняя ссылка изменена или observable удален (что также удаляет внутреннюю ссылку). Чтобы observable также уведомлял потребляющий код, если изменяются дочерние ссылки As, A само по себе должно быть observable, вызывая событие, обрабатываемое Observable<T>
, которое «всплывает» либо через ReferenceChanged, либо через более конкретный обработчик, такой как InstanceDataChanged , (или как вы хотите это назвать).
Комментарии:
1. О, мне это нравится! Я должен провести некоторые эксперименты, но это отличный способ взглянуть на это. Спасибо, Кит!