Создание пользовательского IoC - как реализовать DI, который имеет область? - PullRequest
2 голосов
/ 30 июня 2010

Я пишу контейнер IoC для моего собственного обучения / роста.Обычно я писал бы что-то вроде следующего:

using(DisposableObject dispObj = new DisposableObject())
{
    UserRepository users = new UserRepository(dispObj);

    // Do stuff with user.
}

Обратился бы к:

using(IDisposableObject dispObj = Container.Resolve<IDisposableObject>())
{
    IUserRepository users = Container.Resolve<IUserRepository>();

    // Do stuff with user.
}

Как я могу абстрагировать DisposableObject, чтобы он был единственным экземпляром, используемым в usingсфера при использовании IoC?Я пытаюсь выяснить, как это делает Autofac, но я не совсем уверен.

РЕДАКТИРОВАТЬ: Когда объект создается с областью действия using, все вызовы, чтобы разрешить этоТип (в данном случае IDisposableObject) должен возвращать переменную области, а не новый экземпляр.Также важно, чтобы после оператора using и вызова другого Resolve<IDisposableObject> он возвращал новый экземпляр.

Ответы [ 2 ]

2 голосов
/ 01 июля 2010

Вы можете вернуть объект типа Owned<Foo> вместо Foo.

Owned<T> может выглядеть следующим образом:

public class Owned<T> : IDisposable
{
   private readonly Container container;
   private readonly T value;

   public Owned(Container container, T value)
   {
      this.container = container;
      this.value = value;
   }

   public T Value { get { return value; } }

   public void Dispose()
   {
      this.container.ReleaseOwned(this);
   }

}

Теперь клиент, тянущий Owned<Foo>, можетуведомите контейнер, который сделан с объектом, утилизируя его, даже если Foo сам не является одноразовым.Таким образом, контейнер знает, когда нужно очистить объект и его зависимости (которые также могут быть одноразовыми, но невидимыми для клиента), и вы можете реализовать поведение, которое вам нужно.

Autofac делает что-то оченьаналогичный.См. The Relationship Zoo , сообщение в блоге Николаса Блумхардта, где он представляет эту концепцию.

2 голосов
/ 30 июня 2010

При использовании одноразового объекта таким способом следует помнить, что ссылка в контейнере также будет удалена, поэтому в идеале, когда вы возвращаете экземпляр через Resolve <>, он должен каждый раз возвращать новый экземпляр.

То, что вам нужно будет сделать, - это при регистрации типов в вашем контейнере разрешить ему указывать поведение: Shared (Singleton) или NonShared, например ::

.
Container.RegisterType<IDisposableObject>(CreationPolicy.NonShared);

using (var disposable = Container.Resolve<IDisposableObject>()) {

}

Вышеуказанное будет работать для экземпляров NonShared, поскольку каждый раз создается новый экземпляр, поэтому мы можем безопасно его утилизировать. Если вы попробовали описанное выше с CreationPolicy = Shared, синглтон был бы удален, поэтому в будущем доступ, скорее всего, приведет к исключению ObjectDisposedException.

Встраивая это поведение, вы можете создавать экземпляры Singleton, передавая CreationPolicy = Shared, например ::

Container.RegisterType<IUserRepository>(CreationPolicy.Shared);

using (var disposable = Container.Resolve<IDisposableObject>()) {
    var userRepository = Container.Resolve<IUserRepository>();
    // only one instance of user repository is created and persisted by the container.
}

Надеюсь, это поможет?

Эта терминология может быть знакома, если вы ранее использовали MEF.

РЕДАКТИРОВАТЬ : Итак, я понимаю, что вы хотите сделать что-то вроде:

using (var repository = Container.Resolve<IUserRepository>())
{
  var other = Container.Resolve<IUserRepository>();
  // should resolve to the same instance.
}

Вам нужно найти какой-нибудь способ наблюдения за одноразовым предметом в контейнере. Возможно, введите дополнительную политику создания SharedScope, например:

Container.Register<IUserRepository, UserRepository>(CreationPolicy.SharedScope);

Теперь, когда вы определяете тип с помощью контейнера, вам необходимо выяснить CreationPolicy элемента. Если элемент SharedScope, и он не был создан, создайте его экземпляр и верните.

Если вы разрешите экземпляр, и он уже создан, верните существующий экземпляр.

Когда Dispose вызывается для этого элемента, вам нужен какой-то способ для обратного вызова контейнера, чтобы удалить экземпляр.

РЕДАКТИРОВАТЬ ВТОРОЕ :

Ну, нет простого способа понять это. Единственный способ, которым я могу думать, - это ввести другой интерфейс:

public interface IMonitoredDisposable : IDisposable
{
  bool IsDisposed { get; set; }
}

При удалении объекта убедитесь, что он устанавливает свойство IsDisposed. Не могли бы вы затем отслеживать это свойство из вашего контейнера?

...