Зарегистрируйте и разрешите открытые универсальные типы с множеством универсальных параметров с помощью Autofac

Я хотел бы разрешить открытый универсальный сервис из-за универсального интерфейса. Я использую автофак.

Каждый конкретный сервис работает только с конкретными классами.

Я могу разрешить только один сервис с одним generic param [увидеть SingleOpenGenericResolveTest]. Можно ли зарегистрировать и разрешить многие услуги со многими T-params [увидеть MultiOpenGenericResolveTest]?

Я добавил только один из конкретных классов для IService но можно быть много классов T, (TRegion : Region, TRegion : BigRegion, так далее...)

Вот тесты NUnit 3 или вы можете скачать мое решение здесь: https://www.dropbox.com/s/vqmdwb6hwmzgjrb/AutofacResolveTests.zip?dl=0

using System;
using NUnit.Framework;
using Autofac;
using System.Reflection;
using System.Linq;

namespace AutofacResolveTests
{
    public class Address<TCity, TRegion, TSomethingElse>
        where TCity : City<TRegion>, new()
        where TRegion : Region, new()
        where TSomethingElse : SomethingElse, new()
    {
        public int Id { get; set; }
        TCity City { get; set; }
        TRegion Region { get; set; }
        TSomethingElse SomethingElse { get; set; }
    }

    public class City<TRegion>
        where TRegion : Region, new()
    {
        public int Id { get; set; }
        TRegion Region { get; set; }
    }

    public class Region
    {
        public int Id { get; set; }
    }

    public class SomethingElse
    {
        public int Id { get; set; }
    }

    public interface IService<T> where T : class
    {
        void DoSomething(T entity);
    }

    public class AddressService<TAddress, TCity, TRegion, TSomethingElse> : IService<TAddress>
        where TAddress : Address<TCity, TRegion, TSomethingElse>
        where TCity : City<TRegion>, new()
        where TRegion : Region, new()
        where TSomethingElse : SomethingElse, new()
    {
        public void DoSomething(TAddress entity)
        {
            Console.WriteLine("Hello from address service");
        }
    }

    public class CityService<TCity, TRegion> : IService<TCity>
        where TCity : City<TRegion>, new()
        where TRegion : Region, new()
    {
        public void DoSomething(TCity entity)
        {
            Console.WriteLine("Hello from city service");
        }
    }

    public class RegionService<TRegion> : IService<TRegion>
        where TRegion : Region
    {
        public void DoSomething(TRegion entity)
        {
            Console.WriteLine("Hello from region service");
        }
    }

    [TestFixture]
    public class OpenGenericResolveTests
    {
        IContainer _ioc;

        [SetUp]
        public void Setup()
        {
            var container = new ContainerBuilder();
        //manual types registration - works
        /*
        container.RegisterType(typeof(CityService<City<Region>, Region>)).As(typeof(IService<City<Region>>)).AsImplementedInterfaces();
        container.RegisterType(typeof(AddressService<
            Address<City<Region>, Region, SomethingElse>, City<Region>, Region, SomethingElse
            >))
            .As(typeof(IService<
            Address<City<Region>, Region, SomethingElse>
            >)).AsImplementedInterfaces();
        */

            var type = typeof(IService<>);
            //just get all services which implements IService
            var generics = type.Assembly.GetTypes().Where(x =>
            !x.IsInterface
            && x.Name.Contains("Service")
            && x.IsGenericType
            && x.GetInterfaces().Any(i => i.GetGenericTypeDefinition() == type)
            );

            foreach (var svcType in generics)
            {
                container.RegisterGeneric(svcType)
                .As(typeof(IService<>))
                .AsImplementedInterfaces();
            }

            _ioc = container.Build();
        }

        [Test]
        public void SingleOpenGenericResolveTest()
        {
            var reg = new Region { };

            var actual = _ioc.Resolve<IService<Region>>();
            Assert.That(actual != null);

            actual.DoSomething(reg);
        }

        [Test]
        public void MultiOpenGenericResolveTest()
        {
            //works
            var actual1 = _ioc.Resolve<IService<Region>>();
            Assert.That(actual1 != null);

            //works only with manual registration
            var actual2 = _ioc.Resolve<IService<City<Region>>>();
            Assert.That(actual2 != null);

            //works only with manual registration
            var actual3 = _ioc.Resolve<IService<Address<City<Region>,Region,SomethingElse>>>();
            Assert.That(actual3 != null);
        }
    }
}

1 ответ

Проблема в том, что если вы решите IService<T> но реализация требует более одного общего Service<T,U,V> тогда у Autofac нет возможности узнать, откуда берутся другие типы (U, V)... Таким образом, способ, которым вы пытаетесь зарегистрировать их при массовом сканировании сборки, не сработает.

Другие вопросы по тегам