Как стать автором
Обновить

Комментарии 11

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

namespace BlazorServerApp
{
    internal static partial class ClockDomain
    {
        private static readonly Pure.DI.ResolversTable Resolvers = new Pure.DI.ResolversTable(new Pure.DI.Pair<System.Type, System.Func<object>>[]{new Pure.DI.Pair<System.Type, System.Func<object>>(typeof(Clock.Models.IClock), () =>
        {
            return (new Clock.Models.SystemClock());
        }), new Pure.DI.Pair<System.Type, System.Func<object>>(typeof(Clock.Models.ITimer), () =>
        {
            return (new Timer(TimeSpan.FromSeconds(1)));
        }), new Pure.DI.Pair<System.Type, System.Func<object>>(typeof(Clock.ViewModels.IDispatcher), () =>
        {
            return (Dispatcher__Singleton.Shared);
        }), new Pure.DI.Pair<System.Type, System.Func<object>>(typeof(Clock.ViewModels.IClockViewModel), () =>
        {
            return (new Clock.ViewModels.ClockViewModel((new Pure.DI.ServiceProviderInstance<Clock.Models.IClock>()).Value, (new Pure.DI.ServiceProviderInstance<Clock.Models.ITimer>()).Value, (Dispatcher__Singleton.Shared)));
        }), new Pure.DI.Pair<System.Type, System.Func<object>>(typeof(Pure.DI.ServiceProviderInstance<Clock.Models.ITimer>), () =>
        {
            return (new Pure.DI.ServiceProviderInstance<Clock.Models.ITimer>());
        }), new Pure.DI.Pair<System.Type, System.Func<object>>(typeof(Pure.DI.ServiceProviderInstance<Clock.Models.IClock>), () =>
        {
            return (new Pure.DI.ServiceProviderInstance<Clock.Models.IClock>());
        })}, default(System.Func<System.Type, object, object>));
       
        private const System.UInt32 ResolversDivisor = 28U;
        private static Pure.DI.Pair<System.Type, System.Func<object>>[] ResolversBuckets = Resolvers.ResolversBuckets;
        private static System.Func<System.Type, object, object> ResolversDefaultFactory = Resolvers.ResolversDefaultFactory;

        [MethodImplAttribute((MethodImplOptions)256)]
        public static object Resolve(System.Type type)
        {
            var pair = ResolversBuckets[(uint)type.GetHashCode() % ResolversDivisor];
            do
            {
                if (pair.Key == type)
                {
                    return pair.Value();
                }

                pair = pair.Next;
            }
            while (pair != null);
            return ResolversDefaultFactory != null ? ResolversDefaultFactory(type, null) ?? new ArgumentException($"Cannot resolve an instance of the type {type.Name}.") : throw new ArgumentException($"Cannot resolve an instance of the type {type.Name}.");
        }

        private static class Dispatcher__Singleton
        {
            public static readonly Clock.ViewModels.Dispatcher Shared = new Clock.ViewModels.Dispatcher();
        }

        public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddClockDomain(this Microsoft.Extensions.DependencyInjection.IServiceCollection services)
        {
            Microsoft.Extensions.DependencyInjection.IMvcBuilder builder = Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllers(services);
            builder.PartManager.PopulateFeature(new Microsoft.AspNetCore.Mvc.Controllers.ControllerFeature());
            Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.Replace(builder.Services, Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Transient<Microsoft.AspNetCore.Mvc.Controllers.IControllerActivator, Microsoft.AspNetCore.Mvc.Controllers.ServiceBasedControllerActivator>());
            Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(services, serviceProvider =>
            {
                System.IServiceProvider prevServiceProvider = Pure.DI.ServiceProviderInstance.ServiceProvider;
                try
                {
                    Pure.DI.ServiceProviderInstance.ServiceProvider = serviceProvider;
                    return (Clock.ViewModels.IDispatcher)new Clock.ViewModels.Dispatcher();
                }
                finally
                {
                    Pure.DI.ServiceProviderInstance.ServiceProvider = prevServiceProvider;
                }
            });
            Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(services, serviceProvider =>
            {
                System.IServiceProvider prevServiceProvider = Pure.DI.ServiceProviderInstance.ServiceProvider;
                try
                {
                    Pure.DI.ServiceProviderInstance.ServiceProvider = serviceProvider;
                    return (Clock.ViewModels.IClockViewModel)new Clock.ViewModels.ClockViewModel((new Pure.DI.ServiceProviderInstance<Clock.Models.IClock>()).Value, (new Pure.DI.ServiceProviderInstance<Clock.Models.ITimer>()).Value, (Dispatcher__Singleton.Shared));
                }
                finally
                {
                    Pure.DI.ServiceProviderInstance.ServiceProvider = prevServiceProvider;
                }
            });
            Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddScoped(services, serviceProvider =>
            {
                System.IServiceProvider prevServiceProvider = Pure.DI.ServiceProviderInstance.ServiceProvider;
                try
                {
                    Pure.DI.ServiceProviderInstance.ServiceProvider = serviceProvider;
                    return (Clock.Models.ITimer)new Timer(TimeSpan.FromSeconds(1));
                }
                finally
                {
                    Pure.DI.ServiceProviderInstance.ServiceProvider = prevServiceProvider;
                }
            });
            Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, serviceProvider =>
            {
                System.IServiceProvider prevServiceProvider = Pure.DI.ServiceProviderInstance.ServiceProvider;
                try
                {
                    Pure.DI.ServiceProviderInstance.ServiceProvider = serviceProvider;
                    return (Clock.Models.IClock)new Clock.Models.SystemClock();
                }
                finally
                {
                    Pure.DI.ServiceProviderInstance.ServiceProvider = prevServiceProvider;
                }
            });
            return services;
        }
    }

    internal struct ServiceProviderInstance
    {
        [System.ThreadStatic] public static System.IServiceProvider ServiceProvider;
    }

    internal struct ServiceProviderInstance<T>
    {
        public T Value => ServiceProviderInstance.ServiceProvider != null ? (T)ServiceProviderInstance.ServiceProvider.GetService(typeof(T)) : throw new System.InvalidOperationException("Cannot resolve an instance outside a container.");
    }
}
try
{
Pure.DI.ServiceProviderInstance.ServiceProvider = serviceProvider;
return (Clock.ViewModels.IClockViewModel)new Clock.ViewModels.ClockViewModel((new Pure.DI.ServiceProviderInstance<Clock.Models.IClock>()).Value, (new Pure.DI.ServiceProviderInstance<Clock.Models.ITimer>()).Value, (Dispatcher__Singleton.Shared));
}
finally
{
Pure.DI.ServiceProviderInstance.ServiceProvider = prevServiceProvider;
}

internal struct ServiceProviderInstance
{
[System.ThreadStatic] public static System.IServiceProvider ServiceProvider;
}

Ээээ.


Можете случайно получить много смешных багов. Да, очень маловероятно, прямо очень. Но не надо так все равно.


Ну и не очень понятно, зачем вы что-то делаете с MVC-шными сервисами, будем честными. Это невежливо по отношению к пользователю контейнера.


Короче говоря, как я и боялся: получившийся код очень тяжело читать. Понять, что в нем пошло не так… более чем рискованно.

Можете случайно получить много смешных багов. Да, очень маловероятно, прямо очень. Но не надо так все равно.

Я, причем, не очень понимаю, что вам мешает сделать "резолвер" для каждого "вашего" сервиса в виде Func<IServiceProvider,T>, и просто передавать сервис-провайдер вниз по цепочке. Грубо говоря, вот так:


class A {}
class B { B(ILogger<B> logger) } //системная зависимость
class C { C(A a, B b) } //прикладные зависимости

Func<IServiceProvider, A> FactoryA = _ => new A();
Func<IServiceProvider, B> FactoryB = sp => new B(sp.GetRequiredService<ILogger<B>>());
Func<IServiceProvider, C> FactoryC = sp => new C(FactoryA(sp), FactoryB(sp));

//регистрация

services.AddSingleton<A>(FactoryA);
services.AddScoped<B>(FactoryB);
services.AddTransient<C>(FactoryC);

Я, конечно, из головы писал, но не очень сходу вижу, какие у такого подхода проблемы.

Func<IServiceProvider, C> FactoryC = sp => new C(FactoryA(sp), FactoryB(sp));

Тут, на самом деле, должно быть хитрее, и если зависимости Scoped или Singleton, то надо тоже вызывать sp.GetRequiredService<T>, а Factory вызывать только если временем жизни зависимости управляет не контейнер. Но это вполне очевидные (и просто реализуемые) мелочи.

Можете случайно получить много смешных багов. Да, очень маловероятно, прямо очень. Но не надо так все равно.

Например?

Ну и не очень понятно, зачем вы что-то делаете с MVC-шными сервисами, будем честными. Это невежливо по отношению к пользователю контейнера.


Я решал вопрос интеграции с ASP.NET, Microsoft DI контейнер — не лучше и не хуже других

Короче говоря, как я и боялся: получившийся код очень тяжело читать. Понять, что в нем пошло не так… более чем рискованно.


Если убрать интеграцию с ASP.NET, то код получается проще. Для понимания того, что происходит важна только таблица зависимостей, там все просто. Возможно, стоит улучшить форматирование и добавить комментарии в сгенерированном коде. Нужно понять как бы написал этот код программист руками.

Я, причем, не очень понимаю, что вам мешает сделать «резолвер» для каждого «вашего» сервиса в виде Func<IServiceProvider,T>, и просто передавать сервис-провайдер вниз по цепочке. Грубо говоря, вот так:

IServiceProvider нужен только привязкам с временем жизни ContainerSingleton или Scoped и только для случая интеграции с ASP.NET. Ваш подход не сработает для привязок To<>(…) с lambda функцией и я пока не вижу смысла держать отдельную таблицу для этих методов и дополнительную логику для обработки этого. Если возникнет необходимость, я подумаю над решением
Например?

Например, скачок кода между тредами, и получение не того сервис-провайдера.


Я решал вопрос интеграции с ASP.NET

Зачем для этого регистрировать в контейнере что-то, кроме ваших собственных сервисов? Зачем вы вот это делаете:


Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.Replace(builder.Services, Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Transient<Microsoft.AspNetCore.Mvc.Controllers.IControllerActivator, Microsoft.AspNetCore.Mvc.Controllers.ServiceBasedControllerActivator>());

Если убрать интеграцию с ASP.NET

Вы очень, очень зря приравниваете Microsoft.Extensions.DependencyInjection к ASP.NET.


IServiceProvider нужен только привязкам с временем жизни ContainerSingleton или Scoped

Гм. Так я еще раз спрошу: а откуда ваши сервисы получают зависимости вида ILogger<T>?


Ваш подход не сработает для привязок To<>(…) с lambda функцией

Зависит исключительно от того, как вы сделаете контракт этой функции.

Например, скачок кода между тредами, и получение не того сервис-провайдера

Если Pure.DI будет строить композиции объектов «скачками кода между тредами» я подумаю над решением этой проблемы.
Зачем для этого регистрировать в контейнере что-то, кроме ваших собственных сервисов? Зачем вы вот это делаете:

Этот код нужен для интеграции с ASP.NET
Гм. Так я еще раз спрошу: а откуда ваши сервисы получают зависимости вида ILogger

Чтобы получить зависимость в DI нужно делать «привязку». Для ILogger это можно сделать так:
.Bind<ILogger<TT>>().To(ctx => (ILogger<TT>)ctx.Resolve<IServiceProvider>().GetService(typeof(ILogger<TT>)));


Но я добил привязку по умолчанию для ILogger, так что в последней версии ни чего делать не нужно. Может быть стоит добавить функцию расширения, которая упрощает это. И собрать список того что чаще всего используется
Этот код нужен для интеграции с ASP.NET

Нет, не нужен. Вы где-то неправильно настраиваете ASP.NET Core-сервисы.


Чтобы получить зависимость в DI нужно делать «привязку».

… и так для всех потребляемых сервисов? Вместо того, чтобы научить вашу генерилку делать это автоматически?


Вы простите за прямоту, это неюзабельно.

… и так для всех потребляемых сервисов? Вместо того, чтобы научить вашу генерилку делать это автоматически? Вы простите за прямоту, это неюзабельно.

Pure.DI это НЕ библиотека внедрения зависимостей, это генератор кода. Он анализирует зависимости в момент компиляции, соответственно в этот момент он должен иметь представление о графе. Я понимаю, сложно менять шаблоны и мне жаль, что это неюзабельно для вас.
В любом случае спасибо вам за то, что натолкнули меня на идею добавить привязки для основных типов ASP.NET, как уже было сделано для BCL типов .NET ранее
Pure.DI это НЕ библиотека внедрения зависимостей, это генератор кода. Он анализирует зависимости в момент компиляции, соответственно в этот момент он должен иметь представление о графе.

Я именно об этом и говорю. У вас есть представление о графе, поэтому вы знаете, что ILogger у вас отсутствует. И у вас есть IServiceProvider, из которого этот ILogger можно взять. Все, что осталось — это сгенерить код, который возьмет ILogger (или любой другой отсутствующий сервис) из IServiceProvider.


В любом случае спасибо вам за то, что натолкнули меня на идею добавить привязки для основных типов ASP.NET

Во-первых, не ASP.NET, а ASP.NET Core. Во-вторых, ни ILogger, ни IOptions, ни IHostEnvironment, ни IFileProvider, ни IHttpClientFactory не относятся к ASP.NET Core. Так что вы себе не представляете масштаб работы.


Я понимаю, сложно менять шаблоны и мне жаль, что это это неюзабельно для вас.

Дело не в шаблонах, дело в том, как это будет использоваться.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории