動手造輪子:實現一個簡單的依賴註入(一)

来源:https://www.cnblogs.com/weihanli/archive/2019/10/28/implement-dependency-injection-01.html
-Advertisement-
Play Games

動手造輪子:實現一個簡單的依賴註入(一) Intro 在上一篇文章中主要介紹了一下要做的依賴註入的整體設計和大概編程體驗,這篇文章要開始寫代碼了,開始實現自己的依賴註入框架。 類圖 首先來溫習一下上次提到的類圖 服務生命周期 服務生命周期定義: 服務定義 服務註冊定義: 為了使用起來更方便添加了一些 ...


動手造輪子:實現一個簡單的依賴註入(一)

Intro

在上一篇文章中主要介紹了一下要做的依賴註入的整體設計和大概編程體驗,這篇文章要開始寫代碼了,開始實現自己的依賴註入框架。

類圖

首先來溫習一下上次提到的類圖

服務生命周期

服務生命周期定義:

public enum ServiceLifetime : sbyte
{
    /// <summary>
    /// Specifies that a single instance of the service will be created.
    /// </summary>
    Singleton = 0,

    /// <summary>
    /// Specifies that a new instance of the service will be created for each scope.
    /// </summary>
    Scoped = 1,

    /// <summary>
    /// Specifies that a new instance of the service will be created every time it is requested.
    /// </summary>
    Transient = 2,
}

服務定義

服務註冊定義:

public class ServiceDefinition
{
    // 服務生命周期
    public ServiceLifetime ServiceLifetime { get; }
    // 實現類型
    public Type ImplementType { get; }
    // 服務類型
    public Type ServiceType { get; }
    // 實現實例
    public object ImplementationInstance { get; }
    // 實現工廠
    public Func<IServiceProvider, object> ImplementationFactory { get; }

    // 獲取真實的實現類型
    public Type GetImplementType()
    {
        if (ImplementationInstance != null)
            return ImplementationInstance.GetType();

        if (ImplementationFactory != null)
            return ImplementationFactory.Method.DeclaringType;

        if (ImplementType != null)
            return ImplementType;

        return ServiceType;
    }

    public ServiceDefinition(object instance, Type serviceType)
    {
        ImplementationInstance = instance;
        ServiceType = serviceType;
        ServiceLifetime = ServiceLifetime.Singleton;
    }

    public ServiceDefinition(Type serviceType, ServiceLifetime serviceLifetime) : this(serviceType, serviceType, serviceLifetime)
    {
    }

    public ServiceDefinition(Type serviceType, Type implementType, ServiceLifetime serviceLifetime)
    {
        ServiceType = serviceType;
        ImplementType = implementType ?? serviceType;
        ServiceLifetime = serviceLifetime;
    }

    public ServiceDefinition(Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime serviceLifetime)
    {
        ServiceType = serviceType;
        ImplementationFactory = factory;
        ServiceLifetime = serviceLifetime;
    }
}

為了使用起來更方便添加了一些靜態方法

public static ServiceDefinition Singleton<TService>(Func<IServiceProvider, object> factory)
{
    return new ServiceDefinition(typeof(TService), factory, ServiceLifetime.Singleton);
}

public static ServiceDefinition Scoped<TService>(Func<IServiceProvider, object> factory)
{
    return new ServiceDefinition(typeof(TService), factory, ServiceLifetime.Scoped);
}

public static ServiceDefinition Transient<TService>(Func<IServiceProvider, object> factory)
{
    return new ServiceDefinition(typeof(TService), factory, ServiceLifetime.Transient);
}

public static ServiceDefinition Singleton<TService>()
{
    return new ServiceDefinition(typeof(TService), ServiceLifetime.Singleton);
}

public static ServiceDefinition Scoped<TService>()
{
    return new ServiceDefinition(typeof(TService), ServiceLifetime.Scoped);
}

public static ServiceDefinition Transient<TService>()
{
    return new ServiceDefinition(typeof(TService), ServiceLifetime.Transient);
}

public static ServiceDefinition Singleton<TService, TServiceImplement>() where TServiceImplement : TService
{
    return new ServiceDefinition(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Singleton);
}

public static ServiceDefinition Scoped<TService, TServiceImplement>() where TServiceImplement : TService
{
    return new ServiceDefinition(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Scoped);
}

public static ServiceDefinition Transient<TService, TServiceImplement>() where TServiceImplement : TService
{
    return new ServiceDefinition(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Transient);
}

ServiceContainer

serviceContainer v1

public class ServiceContainer : IServiceContainer
{
    internal readonly List<ServiceDefinition> _services;

    private readonly ConcurrentDictionary<Type, object> _singletonInstances;

    private readonly ConcurrentDictionary<Type, object> _scopedInstances;
    
    private readonly List<object> _transientDisposables = new List<object>();

    private readonly bool _isRootScope;

    public ServiceContainer()
    {
        _isRootScope = true;
        _singletonInstances = new ConcurrentDictionary<Type, object>();
        _services = new List<ServiceDefinition>();
    }

    internal ServiceContainer(ServiceContainer serviceContainer)
    {
        _isRootScope = false;
        _singletonInstances = serviceContainer._singletonInstances;
        _services = serviceContainer._services;
        _scopedInstances = new ConcurrentDictionary<Type, object>();
    }

    public void Add(ServiceDefinition item)
    {
        _services.Add(item);
    }

    public IServiceContainer CreateScope()
    {
        return new ServiceContainer(this);
    }

    private bool _disposed;

    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        if (_isRootScope)
        {
            lock (_singletonInstances)
            {
                if (_disposed)
                {
                    return;
                }

                _disposed = true;
                foreach (var instance in _singletonInstances.Values)
                {
                    (instance as IDisposable)?.Dispose();
                }

                foreach (var o in _transientDisposables)
                {
                    (o as IDisposable)?.Dispose();
                }
            }
        }
        else
        {
            lock (_scopedInstances)
            {
                if (_disposed)
                {
                    return;
                }

                _disposed = true;
                foreach (var instance in _scopedInstances.Values)
                {
                    (instance as IDisposable)?.Dispose();
                }

                foreach (var o in _transientDisposables)
                {
                    (o as IDisposable)?.Dispose();
                }
            }
        }
    }

    private object GetServiceInstance(Type serviceType, ServiceDefinition serviceDefinition)
    {
        if (serviceDefinition.ImplementationInstance != null)
            return serviceDefinition.ImplementationInstance;

        if (serviceDefinition.ImplementationFactory != null)
            return serviceDefinition.ImplementationFactory.Invoke(this);

        var implementType = (serviceDefinition.ImplementType ?? serviceType);

        if (implementType.IsInterface || implementType.IsAbstract)
        {
            throw new InvalidOperationException($"invalid service registered, serviceType: {serviceType.FullName}, implementType: {serviceDefinition.ImplementType}");
        }

        var ctorInfos = implementType.GetConstructors(BindingFlags.Instance | BindingFlags.Public);
        if (ctorInfos.Length == 0)
        {
            throw new InvalidOperationException($"service {serviceType.FullName} does not have any public constructors");
        }

        ConstructorInfo ctor;
        if (ctorInfos.Length == 1)
        {
            ctor = ctorInfos[0];
        }
        else
        {
            // try find best ctor
            ctor = ctorInfos
                .OrderBy(_ => _.GetParameters().Length)
                .First();
        }

        var parameters = ctor.GetParameters();
        if (parameters.Length == 0)
        {
            // TODO: cache New Func
            return Expression.Lambda<Func<object>>(Expression.New(ctor)).Compile().Invoke();
        }
        else
        {
            var ctorParams = new object[parameters.Length];
            for (var index = 0; index < parameters.Length; index++)
            {
                var parameter = parameters[index];
                var param = GetService(parameter.ParameterType);
                if (param == null && parameter.HasDefaultValue)
                {
                    param = parameter.DefaultValue;
                }

                ctorParams[index] = param;
            }
            return Expression.Lambda<Func<object>>(Expression.New(ctor, ctorParams.Select(Expression.Constant))).Compile().Invoke();
        }
    }

    public object GetService(Type serviceType)
    {
        var serviceDefinition = _services.LastOrDefault(_ => _.ServiceType == serviceType);
        if (null == serviceDefinition)
        {
            return null;
        }

        if (_isRootScope && serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)
        {
            throw new InvalidOperationException($"can not get scope service from the root scope, serviceType: {serviceType.FullName}");
        }

        if (serviceDefinition.ServiceLifetime == ServiceLifetime.Singleton)
        {
            var svc = _singletonInstances.GetOrAdd(serviceType, (t) => GetServiceInstance(t, serviceDefinition));
            return svc;
        }
        else if (serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)
        {
            var svc = _scopedInstances.GetOrAdd(serviceType, (t) => GetServiceInstance(t, serviceDefinition));
            return svc;
        }
        else
        {
            var svc = GetServiceInstance(serviceType, serviceDefinition);
            if (svc is IDisposable)
            {
                _transientDisposables.Add(svc);
            }
            return svc;
        }
    }
}

為了使得服務註冊更加方便,可以寫一些擴展方法來方便註冊:

public static IServiceContainer AddSingleton<TService>([NotNull]this IServiceContainer serviceContainer, [NotNull]TService service)
{
    serviceContainer.Add(new ServiceDefinition(service, typeof(TService)));
    return serviceContainer;
}

public static IServiceContainer AddSingleton([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType)
{
    serviceContainer.Add(new ServiceDefinition(serviceType, ServiceLifetime.Singleton));
    return serviceContainer;
}

public static IServiceContainer AddSingleton([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType, [NotNull]Type implementType)
{
    serviceContainer.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Singleton));
    return serviceContainer;
}

public static IServiceContainer AddSingleton<TService>([NotNull]this IServiceContainer serviceContainer, [NotNull]Func<IServiceProvider, object> func)
{
    serviceContainer.Add(ServiceDefinition.Singleton<TService>(func));
    return serviceContainer;
}


public static IServiceContainer AddSingleton<TService>([NotNull]this IServiceContainer serviceContainer)
{
    serviceContainer.Add(ServiceDefinition.Singleton<TService>());
    return serviceContainer;
}


public static IServiceContainer AddSingleton<TService, TServiceImplement>([NotNull]this IServiceContainer serviceContainer) where TServiceImplement : TService
{
    serviceContainer.Add(ServiceDefinition.Singleton<TService, TServiceImplement>());
    return serviceContainer;
}

public static IServiceContainer AddScoped([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType)
{
    serviceContainer.Add(new ServiceDefinition(serviceType, ServiceLifetime.Scoped));
    return serviceContainer;
}

public static IServiceContainer AddScoped([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType, [NotNull]Type implementType)
{
    serviceContainer.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Scoped));
    return serviceContainer;
}

public static IServiceContainer AddScoped<TService>([NotNull]this IServiceContainer serviceContainer, [NotNull]Func<IServiceProvider, object> func)
{
    serviceContainer.Add(ServiceDefinition.Scoped<TService>(func));
    return serviceContainer;
}


public static IServiceContainer AddScoped<TService>([NotNull]this IServiceContainer serviceContainer)
{
    serviceContainer.Add(ServiceDefinition.Scoped<TService>());
    return serviceContainer;
}


public static IServiceContainer AddScoped<TService, TServiceImplement>([NotNull]this IServiceContainer serviceContainer) where TServiceImplement : TService
{
    serviceContainer.Add(ServiceDefinition.Scoped<TService, TServiceImplement>());
    return serviceContainer;
}

public static IServiceContainer AddTransient([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType)
{
    serviceContainer.Add(new ServiceDefinition(serviceType, ServiceLifetime.Transient));
    return serviceContainer;
}

public static IServiceContainer AddTransient([NotNull]this IServiceContainer serviceContainer, [NotNull]Type serviceType, [NotNull]Type implementType)
{
    serviceContainer.Add(new ServiceDefinition(serviceType, implementType, ServiceLifetime.Transient));
    return serviceContainer;
}

public static IServiceContainer AddTransient<TService>([NotNull]this IServiceContainer serviceContainer, [NotNull]Func<IServiceProvider, object> func)
{
    serviceContainer.Add(ServiceDefinition.Transient<TService>(func));
    return serviceContainer;
}


public static IServiceContainer AddTransient<TService>([NotNull]this IServiceContainer serviceContainer)
{
    serviceContainer.Add(ServiceDefinition.Transient<TService>());
    return serviceContainer;
}


public static IServiceContainer AddTransient<TService, TServiceImplement>([NotNull]this IServiceContainer serviceContainer) where TServiceImplement : TService
{
    serviceContainer.Add(ServiceDefinition.Transient<TService, TServiceImplement>());
    return serviceContainer;
}

通過上面的代碼就可以實現基本依賴註入了,但是從功能上來說,上面的代碼只支持獲取單個服務的實例,不支持註冊一個介面的多個實現,獲取介面的所有實現,為此對 ServiceContainer 中的 Instance 的 ConcurrentDictionary 的 Key 進行一下改造,使得可以能夠以介面類型和實現類型聯合作為 key,為此就有了第二版的 ServiceContainer

ServiceContainer v2

為此定義了一個 ServiceKey 的類型,請註意這裡一定要重寫 GetHashCode 方法:

private class ServiceKey : IEquatable<ServiceKey>
{
    public Type ServiceType { get; }

    public Type ImplementType { get; }

    public ServiceKey(Type serviceType, ServiceDefinition definition)
    {
        ServiceType = serviceType;
        ImplementType = definition.GetImplementType();
    }

    public bool Equals(ServiceKey other)
    {
        return ServiceType == other?.ServiceType && ImplementType == other?.ImplementType;
    }

    public override bool Equals(object obj)
    {
        return Equals((ServiceKey)obj);
    }

    public override int GetHashCode()
    {
        var key = $"{ServiceType.FullName}_{ImplementType.FullName}";
        return key.GetHashCode();
    }
}

第二版的 ServiceContainer :

public class ServiceContainer : IServiceContainer
{
    internal readonly ConcurrentBag<ServiceDefinition> _services;

    private readonly ConcurrentDictionary<ServiceKey, object> _singletonInstances;

    private readonly ConcurrentDictionary<ServiceKey, object> _scopedInstances;
    private ConcurrentBag<object> _transientDisposables = new ConcurrentBag<object>();

    private class ServiceKey : IEquatable<ServiceKey>
    {
        public Type ServiceType { get; }

        public Type ImplementType { get; }

        public ServiceKey(Type serviceType, ServiceDefinition definition)
        {
            ServiceType = serviceType;
            ImplementType = definition.GetImplementType();
        }

        public bool Equals(ServiceKey other)
        {
            return ServiceType == other?.ServiceType && ImplementType == other?.ImplementType;
        }

        public override bool Equals(object obj)
        {
            return Equals((ServiceKey)obj);
        }

        public override int GetHashCode()
        {
            var key = $"{ServiceType.FullName}_{ImplementType.FullName}";
            return key.GetHashCode();
        }
    }

    private readonly bool _isRootScope;

    public ServiceContainer()
    {
        _isRootScope = true;
        _singletonInstances = new ConcurrentDictionary<ServiceKey, object>();
        _services = new ConcurrentBag<ServiceDefinition>();
    }

    private ServiceContainer(ServiceContainer serviceContainer)
    {
        _isRootScope = false;
        _singletonInstances = serviceContainer._singletonInstances;
        _services = serviceContainer._services;
        _scopedInstances = new ConcurrentDictionary<ServiceKey, object>();
    }

    public IServiceContainer Add(ServiceDefinition item)
    {
        if (_disposed)
        {
            throw new InvalidOperationException("the service container had been disposed");
        }
        if (_services.Any(_ => _.ServiceType == item.ServiceType && _.GetImplementType() == item.GetImplementType()))
        {
            return this;
        }

        _services.Add(item);
        return this;
    }

    public IServiceContainer TryAdd(ServiceDefinition item)
    {
        if (_disposed)
        {
            throw new InvalidOperationException("the service container had been disposed");
        }
        if (_services.Any(_ => _.ServiceType == item.ServiceType))
        {
            return this;
        }
        _services.Add(item);
        return this;
    }

    public IServiceContainer CreateScope()
    {
        return new ServiceContainer(this);
    }

    private bool _disposed;

    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        if (_isRootScope)
        {
            lock (_singletonInstances)
            {
                if (_disposed)
                {
                    return;
                }

                _disposed = true;
                foreach (var instance in _singletonInstances.Values)
                {
                    (instance as IDisposable)?.Dispose();
                }

                foreach (var o in _transientDisposables)
                {
                    (o as IDisposable)?.Dispose();
                }

                _singletonInstances.Clear();
                _transientDisposables = null;
            }
        }
        else
        {
            lock (_scopedInstances)
            {
                if (_disposed)
                {
                    return;
                }

                _disposed = true;
                foreach (var instance in _scopedInstances.Values)
                {
                    (instance as IDisposable)?.Dispose();
                }

                foreach (var o in _transientDisposables)
                {
                    (o as IDisposable)?.Dispose();
                }

                _scopedInstances.Clear();
                _transientDisposables = null;
            }
        }
    }

    private object GetServiceInstance(Type serviceType, ServiceDefinition serviceDefinition)
    {
        if (serviceDefinition.ImplementationInstance != null)
            return serviceDefinition.ImplementationInstance;

        if (serviceDefinition.ImplementationFactory != null)
            return serviceDefinition.ImplementationFactory.Invoke(this);

        var implementType = (serviceDefinition.ImplementType ?? serviceType);

        if (implementType.IsInterface || implementType.IsAbstract)
        {
            throw new InvalidOperationException($"invalid service registered, serviceType: {serviceType.FullName}, implementType: {serviceDefinition.ImplementType}");
        }

        if (implementType.IsGenericType)
        {
            implementType = implementType.MakeGenericType(serviceType.GetGenericArguments());
        }

        var ctorInfos = implementType.GetConstructors(BindingFlags.Instance | BindingFlags.Public);
        if (ctorInfos.Length == 0)
        {
            throw new InvalidOperationException($"service {serviceType.FullName} does not have any public constructors");
        }

        ConstructorInfo ctor;
        if (ctorInfos.Length == 1)
        {
            ctor = ctorInfos[0];
        }
        else
        {
            // TODO: try find best ctor
            ctor = ctorInfos
                .OrderBy(_ => _.GetParameters().Length)
                .First();
        }

        var parameters = ctor.GetParameters();
        if (parameters.Length == 0)
        {
            // TODO: cache New Func
            return Expression.Lambda<Func<object>>(Expression.New(ctor)).Compile().Invoke();
        }
        else
        {
            var ctorParams = new object[parameters.Length];
            for (var index = 0; index < parameters.Length; index++)
            {
                var parameter = parameters[index];
                var param = GetService(parameter.ParameterType);
                if (param == null && parameter.HasDefaultValue)
                {
                    param = parameter.DefaultValue;
                }

                ctorParams[index] = param;
            }
            return Expression.Lambda<Func<object>>(Expression.New(ctor, ctorParams.Select(Expression.Constant))).Compile().Invoke();
        }
    }

    public object GetService(Type serviceType)
    {
        if (_disposed)
        {
            throw new InvalidOperationException($"can not get scope service from a disposed scope, serviceType: {serviceType.FullName}");
        }

        var serviceDefinition = _services.LastOrDefault(_ => _.ServiceType == serviceType);
        if (null == serviceDefinition)
        {
            if (serviceType.IsGenericType)
            {
                var genericType = serviceType.GetGenericTypeDefinition();
                serviceDefinition = _services.LastOrDefault(_ => _.ServiceType == genericType);
                if (null == serviceDefinition)
                {
                    var innerServiceType = serviceType.GetGenericArguments().First();
                    if (typeof(IEnumerable<>).MakeGenericType(innerServiceType)
                        .IsAssignableFrom(serviceType))
                    {
                        var innerRegType = innerServiceType;
                        if (innerServiceType.IsGenericType)
                        {
                            innerRegType = innerServiceType.GetGenericTypeDefinition();
                        }
                        //
                        var list = new List<object>(4);
                        foreach (var def in _services.Where(_ => _.ServiceType == innerRegType))
                        {
                            object svc;
                            if (def.ServiceLifetime == ServiceLifetime.Singleton)
                            {
                                svc = _singletonInstances.GetOrAdd(new ServiceKey(innerServiceType, def), (t) => GetServiceInstance(innerServiceType, def));
                            }
                            else if (def.ServiceLifetime == ServiceLifetime.Scoped)
                            {
                                svc = _scopedInstances.GetOrAdd(new ServiceKey(innerServiceType, def), (t) => GetServiceInstance(innerServiceType, def));
                            }
                            else
                            {
                                svc = GetServiceInstance(innerServiceType, def);
                                if (svc is IDisposable)
                                {
                                    _transientDisposables.Add(svc);
                                }
                            }
                            if (null != svc)
                            {
                                list.Add(svc);
                            }
                        }

                        var methodInfo = typeof(Enumerable)
                            .GetMethod("Cast", BindingFlags.Static | BindingFlags.Public);
                        if (methodInfo != null)
                        {
                            var genericMethod = methodInfo.MakeGenericMethod(innerServiceType);
                            var castedValue = genericMethod.Invoke(null, new object[] { list });
                            if (typeof(IEnumerable<>).MakeGenericType(innerServiceType) == serviceType)
                            {
                                return castedValue;
                            }
                            var toArrayMethod = typeof(Enumerable).GetMethod("ToArray", BindingFlags.Static | BindingFlags.Public)
                                .MakeGenericMethod(innerServiceType);

                            return toArrayMethod.Invoke(null, new object[] { castedValue });
                        }
                        return list;
                    }

                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        if (_isRootScope && serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)
        {
            throw new InvalidOperationException($"can not get scope service from the root scope, serviceType: {serviceType.FullName}");
        }

        if (serviceDefinition.ServiceLifetime == ServiceLifetime.Singleton)
        {
            var svc = _singletonInstances.GetOrAdd(new ServiceKey(serviceType, serviceDefinition), (t) => GetServiceInstance(t.ServiceType, serviceDefinition));
            return svc;
        }
        else if (serviceDefinition.ServiceLifetime == ServiceLifetime.Scoped)
        {
            var svc = _scopedInstances.GetOrAdd(new ServiceKey(serviceType, serviceDefinition), (t) => GetServiceInstance(t.ServiceType, serviceDefinition));
            return svc;
        }
        else
        {
            var svc = GetServiceInstance(serviceType, serviceDefinition);
            if (svc is IDisposable)
            {
                _transientDisposables.Add(svc);
            }
            return svc;
        }
    }
}

這樣我們就不僅支持了 IEnumerable<TService> 的註冊,也支持 IReadOnlyList<TService>/ IReadOnlyCollection<TService> 的註冊

因為 GetService 返回是 object , 不是強類型的,所以為了使用起來方便,定義了幾個擴展方法,類似於微軟的依賴註入框架里的 GetService<TService>()/GetServices<TService>()/GetRequiredService<TService>()

/// <summary>
/// ResolveService
/// </summary>
/// <typeparam name="TService">TService</typeparam>
/// <param name="serviceProvider">serviceProvider</param>
/// <returns></returns>
public static TService ResolveService<TService>([NotNull]this IServiceProvider serviceProvider)
    => (TService)serviceProvider.GetService(typeof(TService));

/// <summary>
/// ResolveRequiredService
/// throw exception if can not get a service instance
/// </summary>
/// <typeparam name="TService">TService</typeparam>
/// <param name="serviceProvider">serviceProvider</param>
/// <returns></returns>
public static TService ResolveRequiredService<TService>([NotNull] this IServiceProvider serviceProvider)
{
    var serviceType = typeof(TService);
    var svc = serviceProvider.GetService(serviceType);
    if (null == svc)
    {
        throw new InvalidOperationException($"service had not been registered, serviceType: {serviceType}");
    }
    return (TService)svc;
}

/// <summary>
/// Resolve services
/// </summary>
/// <typeparam name="TService">TService</typeparam>
/// <param name="serviceProvider">serviceProvider</param>
/// <returns></returns>
public static IEnumerable<TService> ResolveServices<TService>([NotNull]this IServiceProvider serviceProvider)
    => serviceProvider.ResolveService<IEnumerable<TService>>();

More

後面還更新了一版,主要優化性能,目前來說還不太滿意,暫時這裡先不提了

Reference


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述 HikariCP提供了一些監控指標,他的監控指標都是基於MicroMeter提供出來的,然後支持Prometheus和Dropwizard。本次我們將討論一下HikariCp的監控指標有哪些,為什麼提供這些指標,以及咱們如何去做監控。 監控指標 就像 提供的那樣,幾個重要的指標都存儲在pool ...
  • 主要做兩個事,從properties配置文件中讀取信息,通過反射創建對象 思路主要有兩種,遍歷得到的屬性集合,然後設置類的屬性 遍歷類的屬性集合,從配置文件中讀取(不推薦,因為類的屬性有多樣化,會報錯) 以上方法要註意幾點: 註入的類的屬性要是String類,如果是其他類,要調用相應的方法,不然會報 ...
  • 練習:將文本中的內容用詞雲的形式輸出 實現: 實現效果: ...
  • uvm_objection和uvm_component基礎 uvm_objection和uvm_component是uvm中兩大基礎類,剛開始學習的時候,對兩個東西認識不深,以為它們倆差不多,誰知道它兩是一個是“爺爺”,一個是孫子的關係,兩者貫穿整個 uvm驗證方法學。至於為什麼要劃分uvm_obj ...
  • Redis是一個key-value資料庫,支持存儲的value類型包括string(字元串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。在Java中,使用較為廣泛的客戶端有Redisson、Jedis。Spring Data Redis模... ...
  • 隨著互聯網時代的不斷發展,開發者可能會面臨這樣的困境:為瞭解決問題、提升開發效率而竭力研發出來的“創新”,似乎削弱了他們在公司的重要程度,甚至取代了他們原先的地位。比如,在雲原生時代,部分企業更願意選擇 K8s 來解決運維、彈性的問題,而不是組建一支需要耗費大量雇佣資金、管理資金的研發團隊。 對於 ...
  • 對於剛入門的Pythoner在學習過程中運行代碼是或多或少會遇到一些錯誤,剛開始可能看起來比較費勁。隨著代碼量的積累,熟能生巧當遇到一些運行時錯誤時能夠很快的定位問題原題。下麵整理了常見的17個錯誤,希望能夠幫助到大家。 1、忘記在if,for,def,elif,else,class等聲明末尾加 : ...
  • 【ASP.NET Core學習】基礎知識,配置文件,日誌,依賴註入 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...