[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"));
            }
        }

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

补充说明

  • 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);
            }
        }
}
时间: 2024-08-29 19:28:55

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

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

这个系列已经写了5篇,链接地址如下: [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-微软的实现(5)(IEnumerable&lt;&gt;补充)

Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableService与ClosedIEnumerableService抛下没讲,现在我们就将该部分补充完整. 我们回忆ServiceProvider类的构造函数(对外部使用的)中,注册了IEnumerable<>.new OpenIEnumerableService(_table)的关系. public Se

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

微软DI文章系列如下所示: [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

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

在 DependencyInjection项目代码分析4-微软的实现(上)中介绍了“ServiceTable”.“ServiceEntry”.“IGenericService”.“IService”.“IServiceCallSite”,这篇介绍下“IGenericService."IService"."IServiceCallSite"实现类 GenericService类 做为IGenericService的唯一实现类,该类言简意赅,请看代码: internal

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

最近在研究开源代码,正好发现Asp.net5的源码,下载地址:https://github.com/aspnet. 今天主要讲的是DependencyInjection这部分,抛砖引玉,供大家参考,也欢迎莅临斧正.闲话不多说,下面就代码进行简单分析 项目架构如下: 一共包含DependencyInjection.DependencyInjection.Abstractions.DependencyInjection.Autofac.DependencyInjection.Ninject以及Dep

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

Microsoft.Framework.DependencyInjection.Autofac源码分析 该工程只有一个代码静态类AutofacRegistration,但是该类有3个扩展方法,以及3个内部类.扩展方法如下: public static class AutofacRegistration { public static void Populate(this ContainerBuilder builder,IEnumerable<ServiceDescriptor> descri

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

Microsoft.Framework.DependencyInjection.Ninject 该工程内部共包含5个类文件,底层使用Ninject实现依赖注入,工程截图如下: 从文件命名可以看出,NinjectServiceProvider和NinjectServiceScopeFactory分别是接口IServiceProvider和IServiceScopeFactory的实现类. (IServiceScope接口的实现类作为NinjectServiceScopeFactory内部类而存在,

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

前面俩种实现中,很多内部细节都无法知道,微软的框架也是为了屏蔽具体实现,只让我们关注接口.但是人都是充满好奇的,依赖注入到底是怎么实现的呢? 微软又有怎样的实现呢?下面就为大家一一呈现(说实话,代码真不好读) 先看下核心类:ServiceTable internal class ServiceTable { private readonly object _sync = new object(); private readonly Dictionary<Type, ServiceEntry>

结对队友个人项目代码分析

项目要求 项目名称:中小学数学卷子自动生成程序 用户: 小学.初中和高中数学老师. 功能: 1.命令行输入用户名和密码,两者之间用空格隔开(程序预设小学.初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示"当前选择为XX出题",XX为小学.初中和高中三个选项中的一个.否则提示"请输入正确的用户名.密码",重新输入用户名.密码: 2.登录后,系统提示"准备生成XX数学题目,请输入生成题目数量:",XX为小学.初中和高中三