• [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)


    这个系列已经写了6篇,链接地址如下:

    [Asp.net 5] DependencyInjection项目代码分析

    [Asp.net 5] DependencyInjection项目代码分析2-Autofac

    [Asp.net 5] DependencyInjection项目代码分析3-Ninject

    [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)

    [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)

    [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)

    如果想对本篇有个更好的了解,建议需要先看

    [Asp.net 5] DependencyInjection项目代码分析

    [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)

    [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)”。

    "[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)"

    继续ServiceProvider类

    在之前的讲解中我们提到过Service类调用CreateCallSite方法时会递归调用,但是我们没具体说明如何递归调的。实际上Service类,通过反射创建实例的时候,会实例化的参数对象,而实例话参数对象通过ServiceProvider类创建,而ServiceProvider类创建参数的实例,又需要通过Service类(如果是通过Type注册的)创建。下面我们把Service的CreateInstanceCallSite方法以及ServiceProvider相关的方法列出来。

    public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
            {
                ConstructorInfo[] constructors = _descriptor.ImplementationType.GetTypeInfo()
                    .DeclaredConstructors
                    .Where(IsInjectable)
                    .ToArray();
    
                // TODO: actual service-fulfillment constructor selection
                if (constructors.Length == 1)
                {
                    ParameterInfo[] parameters = constructors[0].GetParameters();
                    IServiceCallSite[] parameterCallSites = new IServiceCallSite[parameters.Length];
                    for (var index = 0; index != parameters.Length; ++index)
                    {
                        parameterCallSites[index] = provider.GetServiceCallSite(parameters[index].ParameterType, callSiteChain);
                    
                        if (parameterCallSites[index] == null && parameters[index].HasDefaultValue)
                        {
                            parameterCallSites[index] = new ConstantCallSite(parameters[index].DefaultValue);
                        }
                        if (parameterCallSites[index] == null)
                        {
                            throw new InvalidOperationException(Resources.FormatCannotResolveService(
                                    parameters[index].ParameterType, 
                                    _descriptor.ImplementationType));
                        }
                    }
                    return new ConstructorCallSite(constructors[0], parameterCallSites);
                }
    
                return new CreateInstanceCallSite(_descriptor);
            }
    Service的CreateCallSite
    internal IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)
            {
                try
                {
                    if (callSiteChain.Contains(serviceType))
                    {
                        throw new InvalidOperationException(Resources.FormatCircularDependencyException(serviceType));
                    }
    
                    callSiteChain.Add(serviceType);
    
                    ServiceEntry entry;
                    if (_table.TryGetEntry(serviceType, out entry))
                    {
                        return GetResolveCallSite(entry.Last, callSiteChain);
                    }
    
                    object emptyIEnumerableOrNull = GetEmptyIEnumerableOrNull(serviceType);
                    if (emptyIEnumerableOrNull != null)
                    {
                        return new EmptyIEnumerableCallSite(serviceType, emptyIEnumerableOrNull);
                    }
    
                    return null;
                }
                finally
                {
                    callSiteChain.Remove(serviceType);
                }
    
            }
    
            internal IServiceCallSite GetResolveCallSite(IService service, ISet<Type> callSiteChain)
            {
                IServiceCallSite serviceCallSite = service.CreateCallSite(this, callSiteChain);
                if (service.Lifetime == ServiceLifetime.Transient)
                {
                    return new TransientCallSite(serviceCallSite);
                }
                else if (service.Lifetime == ServiceLifetime.Scoped)
                {
                    return new ScopedCallSite(service, serviceCallSite);
                }
                else
                {
                    return new SingletonCallSite(service, serviceCallSite);
                }
            }
    ServiceProvider

    对于Service的CreateCallSite方法,之前我们已经介绍过,现在我们重点讲下ServiceProvider的GetServiceCallSite方法。从上面代码中我们发现参数中含有“ ISet<Type> callSiteChain”,这个参数是防止发生A的构造函数有B类型参数,B的构织函数中有A类型参数,当A,B都是通过类型注入的,那么系统会陷入死循环。而callSiteChain得作用就是防止这样的死循环发生,当创建A时,会在callSiteChain中查询历史中是否有A的创建过程,如果有则说明发生死循环了,直接抛出异常,结束;如果没有将A加入到callSiteChain中,继续创建其参数。GetResolveCallSite方法比较简单,对于ServiceProvider已经能够获取的IServiceCallSite实例,进行包装,已保证生成的实例能够适应不同的Scoped(该处应该使用设计模式中的代理模式,不过我设计模式不过关,请帮忙确认)。

    对于TransientCallSite、ScopedCallSite、SingletonCallSite以及EmptyIEnumerableCallSite代码,如下所示:

     private class EmptyIEnumerableCallSite : IServiceCallSite
            {
                private readonly object _serviceInstance;
                private readonly Type _serviceType;
    
                public EmptyIEnumerableCallSite(Type serviceType, object serviceInstance)
                {
                    _serviceType = serviceType;
                    _serviceInstance = serviceInstance;
                }
    
                public object Invoke(ServiceProvider provider)
                {
                    return _serviceInstance;
                }
    
                public Expression Build(Expression provider)
                {
                    return Expression.Constant(_serviceInstance, _serviceType);
                }
            }
    
            private class TransientCallSite : IServiceCallSite
            {
                private readonly IServiceCallSite _service;
    
                public TransientCallSite(IServiceCallSite service)
                {
                    _service = service;
                }
    
                public object Invoke(ServiceProvider provider)
                {
                    return provider.CaptureDisposable(_service.Invoke(provider));
                }
    
                public Expression Build(Expression provider)
                {
                    return Expression.Call(
                        provider,
                        CaptureDisposableMethodInfo,
                        _service.Build(provider));
                }
            }
    
            private class ScopedCallSite : IServiceCallSite
            {
                private readonly IService _key;
                private readonly IServiceCallSite _serviceCallSite;
    
                public ScopedCallSite(IService key, IServiceCallSite serviceCallSite)
                {
                    _key = key;
                    _serviceCallSite = serviceCallSite;
                }
    
                public virtual object Invoke(ServiceProvider provider)
                {
                    object resolved;
                    lock (provider._sync)
                    {
                        if (!provider._resolvedServices.TryGetValue(_key, out resolved))
                        {
                            resolved = provider.CaptureDisposable(_serviceCallSite.Invoke(provider));
                            provider._resolvedServices.Add(_key, resolved);
                        }
                    }
                    return resolved;
                }
    
                public virtual Expression Build(Expression providerExpression)
                {
                    var keyExpression = Expression.Constant(
                        _key,
                        typeof(IService));
    
                    var resolvedExpression = Expression.Variable(typeof(object), "resolved");
    
                    var resolvedServicesExpression = Expression.Field(
                        providerExpression,
                        "_resolvedServices");
    
                    var tryGetValueExpression = Expression.Call(
                        resolvedServicesExpression,
                        TryGetValueMethodInfo,
                        keyExpression,
                        resolvedExpression);
    
                    var captureDisposableExpression = Expression.Assign(
                        resolvedExpression,
                        Expression.Call(
                            providerExpression,
                            CaptureDisposableMethodInfo,
                            _serviceCallSite.Build(providerExpression)));
    
                    var addValueExpression = Expression.Call(
                        resolvedServicesExpression,
                        AddMethodInfo,
                        keyExpression,
                        resolvedExpression);
    
                    var blockExpression = Expression.Block(
                        typeof(object),
                        new[] { resolvedExpression },
                        Expression.IfThen(
                            Expression.Not(tryGetValueExpression),
                            Expression.Block(captureDisposableExpression, addValueExpression)),
                        resolvedExpression);
    
                    return Lock(providerExpression, blockExpression);
                }
    
                private static Expression Lock(Expression providerExpression, Expression body)
                {
                    // The C# compiler would copy the lock object to guard against mutation.
                    // We don't, since we know the lock object is readonly.
                    var syncField = Expression.Field(providerExpression, "_sync");
                    var lockWasTaken = Expression.Variable(typeof(bool), "lockWasTaken");
    
                    var monitorEnter = Expression.Call(MonitorEnterMethodInfo, syncField, lockWasTaken);
                    var monitorExit = Expression.Call(MonitorExitMethodInfo, syncField);
    
                    var tryBody = Expression.Block(monitorEnter, body);
                    var finallyBody = Expression.IfThen(lockWasTaken, monitorExit);
    
                    return Expression.Block(
                        typeof(object),
                        new[] { lockWasTaken },
                        Expression.TryFinally(tryBody, finallyBody));
                }
            }
    
            private class SingletonCallSite : ScopedCallSite
            {
                public SingletonCallSite(IService key, IServiceCallSite serviceCallSite) : base(key, serviceCallSite)
                {
                }
    
                public override object Invoke(ServiceProvider provider)
                {
                    return base.Invoke(provider._root);
                }
    
                public override Expression Build(Expression provider)
                {
                    return base.Build(Expression.Field(provider, "_root"));
                }
            }
    View Code

     该工程所有类的关系图(包括内部类以及一些接口),如下所示:

    补充说明

    • IServiceCallSite中定义了Build方法,该方法使用了Expression,但是该篇文章没有对其进行具体的研究,并且Build方法是相对独立的。
    • 对于OpenIEnumerableService泛型省略详解。

    示例测试代码:

        public static class TestServices
        {
            public static IServiceCollection DefaultServices()
            {
                var services = new ServiceCollection();
    
                services.AddTransient<IFakeService, FakeService>();
                services.AddTransient<IFakeMultipleService, FakeOneMultipleService>();
                services.AddTransient<IFakeMultipleService, FakeTwoMultipleService>();
                services.AddTransient<IFakeOuterService, FakeOuterService>();
                services.AddInstance<IFakeServiceInstance>(new FakeService() { Message = "Instance" });
                services.AddScoped<IFakeScopedService, FakeService>();
                services.AddSingleton<IFakeSingletonService, FakeService>();
                services.AddTransient<IDependOnNonexistentService, DependOnNonexistentService>();
                services.AddTransient<IFakeOpenGenericService<string>, FakeService>();
                services.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>));
    
                services.AddTransient<IFactoryService>(provider =>
                {
                    var fakeService = provider.GetService<IFakeService>();
                    return new TransientFactoryService
                    {
                        FakeService = fakeService,
                        Value = 42
                    };
                });
    
                services.AddScoped(provider =>
                {
                    var fakeService = provider.GetService<IFakeService>();
                    return new ScopedFactoryService
                    {
                        FakeService = fakeService,
                    };
                });
    
                services.AddTransient<ServiceAcceptingFactoryService, ServiceAcceptingFactoryService>();
                return services;
            }
        }
    
    
    public class ServiceProviderContainerTests : ScopingContainerTestBase
        {
            protected override IServiceProvider CreateContainer()
            {
                return TestServices.DefaultServices().BuildServiceProvider();
            }
    
    [Fact]
            public void ScopedServiceCanBeResolved()
            {
                IServiceProvider container = CreateContainer();
    
                var scopeFactory = container.GetService<IServiceScopeFactory>();
                using (var scope = scopeFactory.CreateScope())
                {
                    var containerScopedService = container.GetService<IFakeScopedService>();
                    var scopedService1 = scope.ServiceProvider.GetService<IFakeScopedService>();
                    Thread.Sleep(200);
                    var scopedService2 = scope.ServiceProvider.GetService<IFakeScopedService>();
    
                    Assert.NotEqual(containerScopedService, scopedService1);
                    Assert.Equal(scopedService1, scopedService2);
                }
            }
    }
  • 相关阅读:
    IIS是如何处理ASP.NET请求的
    数据库访问性能优化
    通信交互总结
    数据库集群技术漫谈
    VS2010中出现无法嵌入互操作类型
    正则表达式-更新版
    IIS部署SSL证书后提示不可信的解决方案
    CSS水平居中和垂直居中解决方案
    jQuery get/post区别及contentType取值
    配置Tomcat使用https协议
  • 原文地址:https://www.cnblogs.com/watermoon2/p/4525613.html
Copyright © 2020-2023  润新知