Jak pogodzić IDisposable i MKOl?

W końcu owijam głowę wokół IoC I DI W C# i zmagam się z niektórymi krawędziami. Używam kontenera Unity, ale myślę, że to pytanie dotyczy szerzej.

Używanie kontenera IoC do wydawania instancji implementujących IDisposable mnie przeraża! Skąd masz wiedzieć, czy powinieneś się pozbyć ()? Instancja może być stworzona tylko dla Ciebie (i dlatego powinieneś ją usunąć), lub może to być instancja, której życie jest zarządzane gdzie indziej (i dlatego lepiej nie). Nic w kodzie nie mówi, a w rzeczywistości może się to zmienić w zależności od konfiguracji!!! To wydaje mi się zabójcze.

Czy któryś z ekspertów MKOl może opisać dobre sposoby radzenia sobie z tą dwuznacznością?

Author: Mr. Putty, 2009-06-12

7 answers

AutoFac obsługuje to, umożliwiając Tworzenie zagnieżdżonego kontenera. Gdy kontener jest skończony, automatycznie usuwa wszystkie IDisposable obiektów w nim. Więcej Tutaj .

.. Gdy rozwiązujesz usługi, Autofac śledzi rozwiązane komponenty jednorazowe (IDisposable). pod koniec jednostki pracy usuwasz związany z nią zakres życia, a Autofac automatycznie oczyści / usunie rozwiązane usługi.

 7
Author: Samuel Jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-04 17:22:49

Zdecydowanie nie chcesz wywoływać Dispose () na obiekcie, który został wstrzyknięty do twojej klasy. Nie możesz zakładać, że jesteś jedynym konsumentem. Najlepszym rozwiązaniem jest zawinięcie niezarządzanego obiektu w jakiś zarządzany interfejs:

public class ManagedFileReader : IManagedFileReader
{
    public string Read(string path)
    {
        using (StreamReader reader = File.OpenRead(path))
        {
            return reader.ReadToEnd();
        }
    }
}

To tylko przykład, użyłbym File.ReadAllText (path) jeśli próbowałem odczytać plik tekstowy na ciąg znaków.

Innym podejściem jest wstrzyknięcie fabryki i samodzielne zarządzanie obiektem:

public void DoSomething()
{
    using (var resourceThatShouldBeDisposed = injectedFactory.CreateResource())
    {
        // do something
    }
}
 17
Author: Mike Valenty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 04:35:06

To też często mnie zastanawiało. Choć nie jestem z tego zadowolony, zawsze doszedłem do wniosku, że najlepszym rozwiązaniem jest nie zwracanie w sposób przejściowy idealnego przedmiotu.

Ostatnio przeformułowałem sobie pytanie: czy to naprawdę problem z IoC, czy z. NET framework? Pozbywanie się i tak jest niezręczne. Nie ma żadnego sensownego celu funkcjonalnego, tylko technicznego. Więc to bardziej kwestia RAM, z którą musimy się uporać, niż kwestia MKOl.

To, co lubię w DI, to to, że może poprosić o umowę zapewniającą mi funkcjonalność, bez konieczności zawracania sobie głowy szczegółami technicznymi. Nie jestem właścicielem. Nie wiadomo, w której warstwie jest. Brak wiedzy o tym, jakie technologie są wymagane do realizacji zamówienia, brak obaw o żywotność. Mój kod wygląda ładnie i czysto i jest wysoce testowalny. Mogę realizować obowiązki w warstwach, do których należą.

Więc jeśli jest jakiś wyjątek od tej reguły, który wymaga ode mnie zorganizowania życia, zróbmy ten wyjątek. Czy mi się to podoba, czy nie. Jeśli obiekt implementujący interfejs wymaga ode mnie pozbycia się go, chcę o tym wiedzieć, ponieważ od tego czasu jestem wyzwalany, aby użyć obiektu tak krótko, jak to możliwe. Sztuczka polegająca na rozwiązaniu go za pomocą kontenera potomnego, który jest usuwany jakiś czas później, może jeszcze spowodować, że utrzymam obiekt przy życiu dłużej niż powinienem. Dozwolony czas życia obiektu jest ustalany podczas rejestracji obiektu. Nie przez funkcjonalność, która tworzy kontener potomny i posiada na pewien czas.

Tak długo, jak my deweloperzy musimy martwić się o pozbycie (czy to się kiedyś zmieni?) Postaram się wstrzyknąć jak najmniej przedmiotów jednorazowego użytku. 1. Staram się, aby obiekt nie był możliwy do zidentyfikowania, na przykład nie utrzymując obiektów jednorazowych na poziomie klasy, ale w mniejszym zakresie. 2. Staram się, aby obiekt był wielokrotnego użytku, aby można było zastosować inny menedżer życia.

Jeśli nie jest to wykonalne, używam fabryki, aby wskazać, że użytkownik zleceniodawca jest właścicielem i powinien wziąć za nią odpowiedzialność.

Jest jedno zastrzeżenie: zmiana wykonawcy umowy z jednorazowego użytku na jednorazowy będzie zmianą zrywającą. W tym czasie interfejs nie będzie już zarejestrowany, ale interfejs do fabryki. Ale myślę, że dotyczy to również innych scenariuszy. Zapomnienie o użyciu kontenera dziecięcego spowoduje od tego momentu problemy z pamięcią. Podejście fabryczne spowoduje wyjątek IOC resolve.

Niektóre przykładowy kod:

using System;
using Microsoft.Practices.Unity;

namespace Test
{
    // Unity configuration
    public class ConfigurationExtension : UnityContainerExtension
    {
        protected override void Initialize()
        {
            // Container.RegisterType<IDataService, DataService>(); Use factory instead
            Container.RegisterType<IInjectionFactory<IDataService>, InjectionFactory<IDataService, DataService>>();
        }
    }

    #region General utility layer

    public interface IInjectionFactory<out T>
        where T : class
    {
        T Create();
    }

    public class InjectionFactory<T2, T1> : IInjectionFactory<T2>
        where T1 : T2
        where T2 : class

    {
        private readonly IUnityContainer _iocContainer;

        public InjectionFactory(IUnityContainer iocContainer)
        {
            _iocContainer = iocContainer;
        }

        public T2 Create()
        {
            return _iocContainer.Resolve<T1>();
        }
    }

    #endregion

    #region data layer

    public class DataService : IDataService, IDisposable
    {
        public object LoadData()
        {
            return "Test data";
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                /* Dispose stuff */
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }

    #endregion

    #region domain layer

    public interface IDataService
    {
        object LoadData();
    }

    public class DomainService
    {
        private readonly IInjectionFactory<IDataService> _dataServiceFactory;

        public DomainService(IInjectionFactory<IDataService> dataServiceFactory)
        {
            _dataServiceFactory = dataServiceFactory;
        }

        public object GetData()
        {
            var dataService = _dataServiceFactory.Create();
            try
            {
                return dataService.LoadData();
            }
            finally
            {
                var disposableDataService = dataService as IDisposable;
                if (disposableDataService != null)
                {
                    disposableDataService.Dispose();
                }
            }
        }
    }

    #endregion
}
 4
Author: Remco te Wierik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-05 13:49:41

Myślę, że ogólnie najlepszym rozwiązaniem jest po prostu nie pozbywanie się czegoś, co zostało wstrzyknięte; trzeba założyć, że wtryskiwacz dokonuje alokacji i dealokacji.

 2
Author: Paul Sonier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-12 16:52:48

To zależy od struktury DI. Niektóre struktury pozwalają określić, czy dana instancja ma być współdzielona (zawsze przy użyciu tego samego odniesienia) dla każdej wprowadzanej zależności. W takim przypadku najprawdopodobniej nie chcesz się pozbyć.

Jeśli możesz określić, że chcesz wstrzyknąć unikalną instancję, to będziesz chciał ją usunąć (ponieważ została ona specjalnie skonstruowana dla Ciebie). Nie znam się na Unity - trzeba by sprawdzić w dokumentach jak to działa. To część atrybutu z MEF i niektórych innych próbowałem, choć.

 2
Author: Reed Copsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-12 16:55:29

Umieszczenie fasady przed pojemnikiem może rozwiązać ten problem. Ponadto możesz go rozszerzyć, aby śledzić bardziej bogaty cykl życia, taki jak przestoje i starty usług lub przejścia Stanów usług hostingowych.

Mój kontener ma tendencję do życia w IExtension, który implementuje interfejs IServiceLocator. Jest to fasada dla jedności i pozwala na łatwy dostęp do usług WCf. Dodatkowo mam dostęp do wydarzeń serwisowych z bazy ServiceHostBase.

Kod, z którym skończysz, będzie próbował aby sprawdzić, czy jakikolwiek singleton Zarejestrowany lub jakikolwiek stworzony typ implementuje którykolwiek z interfejsów, które fasada śledzi.

Nadal nie pozwala na pozbycie się w odpowiednim czasie, ponieważ jesteś związany z tymi wydarzeniami, ale trochę pomaga.

Jeśli chcesz pozbyć się w odpowiednim czasie (aka, teraz v. s. po wyłączeniu usługi). Musisz wiedzieć, że przedmiot, który otrzymujesz, jest jednorazowy, jest częścią logiki biznesowej, aby go pozbyć, więc IDisposable powinien być częścią interfejsu obiekt. I pewnie powinna być weryfikacja oczekiwań związanych z wywołaniem metody zbycia.

 2
Author: Bas Hamer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-12 21:04:32

W ramach Unity są dwa sposoby rejestracji klas wtryskiwanych: jako singletony (otrzymujesz zawsze tę samą instancję klasy, gdy ją rozwiązujesz), lub takie, jak otrzymujesz nową instancję klasy przy każdej rozdzielczości.

W późniejszym przypadku masz obowiązek usunięcia rozwiązanej instancji, gdy jej nie potrzebujesz (co jest całkiem rozsądnym podejściem). Z drugiej strony, gdy pozbywasz się kontenera (klasy, która obsługuje rozdzielczości obiektów), wszystkie obiekty singleton są również automatycznie usuwane.

Dlatego najwyraźniej nie ma problemów z wtryskiwanymi obiektami jednorazowego użytku z Unity framework. Nie znam się na innych frameworkach, ale przypuszczam, że tak długo, jak Framework dependency injection jest wystarczająco solidny, na pewno radzi sobie z tym problemem w taki czy inny sposób.

 1
Author: Konamiman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-19 20:28:26