• 有趣的懒加载解决依赖循环的问题


    最近多次遇到循环引用的问题,感觉于找到一种骚操作解决,懒加载。

    C# 中的Lazy<> 类型,只有在使用到这个值的时候才会去实例化,在此之前将会保存实例化的委托,于是可以利用这种方式解决依赖循环,当然,缺点是不能在构造函数中使用实例,否则又会进入到循环了。

    一、首先创建一个接口ICircular<> 以后将会产生循环的类使用这个接口调用。

        /// <summary>
        /// 循环引用的接口。
        /// </summary>
        /// <typeparam name="TClass">会循环引用的类型。</typeparam>
        public interface ICircular<Target> where Target : class
        {
            /// <summary>
            /// 实例。
            /// </summary>
            public Target Instance { get; }
    
            /// <summary>
            /// 生命周期类型。
            /// </summary>
            public ServiceLifetime Lifetime { get; }
        }

    实现方法也很简单。

        public class Circular<Target> : ICircular<Target> where Target : class
        {
            private readonly Lazy<Target> _implemente;
    
            public Circular(Lazy<Target> target, ServiceLifetime lifetime)
            {
                _implemente = target;
                Lifetime = lifetime;
            }
    
            public Target Instance => _implemente.Value;
    
            public ServiceLifetime Lifetime { get; }
        }

    为了方便注册服务,创建注册服务的接口

        /// <summary>
        /// 循环引用的服务容器。
        /// </summary>
        public interface ICircularServiceCollection
        {
            /// <summary>
            /// 添加单例。
            /// </summary>
            /// <returns></returns>
            ICircularServiceCollection AddTransient<Target>() where Target : class;
    
    
            /// <summary>
            /// 添加Scoped。
            /// </summary>
            /// <returns></returns>
            ICircularServiceCollection AddScoped<Target>() where Target : class;
    
            /// <summary>
            /// 添加Scoped、
            /// </summary>
            /// <typeparam name="Target">目标。</typeparam>
            /// <typeparam name="TImplementation">实例。</typeparam>
            /// <returns></returns>
            ICircularServiceCollection AddScoped<Target, TImplementation>() where Target : class where TImplementation : class, Target;
    
            /// <summary>
            /// 添加单例。
            /// </summary>
            /// <returns></returns>
            ICircularServiceCollection AddSingleton<Target>() where Target : class;
    
            /// <summary>
            /// 完成。
            /// </summary>
            /// <returns></returns>
            IServiceCollection Completed();
        }

    实现依然很简单

        public class CircularServiceCollection : ICircularServiceCollection
        {
            private readonly IServiceCollection _serviceDescriptors;
    
            /// <summary>
            /// 构造函数。
            /// </summary>
            /// <param name="serviceDescriptors">注入容器。</param>
            public CircularServiceCollection(IServiceCollection serviceDescriptors)
            {
                _serviceDescriptors = serviceDescriptors;
            }
    
            public ICircularServiceCollection AddScoped<Target>() where Target : class
            {
                _serviceDescriptors.AddScoped<Target>();
                _serviceDescriptors.AddScoped(ImplementationAction<Target>(ServiceLifetime.Scoped));
                return this;
            }
    
            public ICircularServiceCollection AddScoped<Target, TImplementation>()
                where Target : class
                where TImplementation : class, Target
            {
                _serviceDescriptors.AddScoped<Target, TImplementation>();
                _serviceDescriptors.AddScoped(ImplementationAction<Target>(ServiceLifetime.Scoped));
                return this;
            }
    
            public ICircularServiceCollection AddSingleton<Target>() where Target : class
            {
                _serviceDescriptors.AddSingleton<Target>();
                _serviceDescriptors.AddSingleton(ImplementationAction<Target>(ServiceLifetime.Singleton));
                return this;
            }
    
            public ICircularServiceCollection AddTransient<Target>() where Target : class
            {
                _serviceDescriptors.AddTransient<Target>();
                _serviceDescriptors.AddTransient(ImplementationAction<Target>(ServiceLifetime.Transient));
                return this;
            }
    
            public IServiceCollection Completed()
            {
                return _serviceDescriptors;
            }
    
            private Func<IServiceProvider, ICircular<Target>> ImplementationAction<Target>(ServiceLifetime lifetime) where Target : class
            {
                return (serviceProvider) =>
                {
                    return new Circular<Target>(new Lazy<Target>(() => serviceProvider.GetService<Target>()), lifetime);
                };
            }
        }

    在服务容器中使用

            /// <summary>
            /// 重复引用选项。
            /// </summary>
            public static ICircularServiceCollection AddCircularOptions(this IServiceCollection serviceDescriptors)
            {
                return new CircularServiceCollection(serviceDescriptors);
            }

    测试一下:下面代码中,原本A构造函数需要注入B,B构造函数需要注入A,将其改成ICircular<B>这种形式

        internal class A : BaseClass
        {
            private readonly ICircular<B> _b;
    
            public A(ICircular<B> b)
            {
                _b = b;
            }
    
            public B B { get => _b.Instance; }
        }
    
        internal class B : BaseClass
        {
            private readonly ICircular<A> _a;
    
            public B(ICircular<A> a)
            {
                _a = a;
            }
    
            public A A { get => _a.Instance; }
        }
         [Fact]
            public void Test_AReferenceB_Should_B()
            {
                var serviceProvider = new ServiceCollection()
                    .AddCircularOptions()
                    .AddScoped<A>()
                    .AddTransient<B>()
                    .Completed().BuildServiceProvider();
    
                var a = serviceProvider.GetRequiredService<A>();
                var b = serviceProvider.GetRequiredService<ICircular<B>>();
                var c = serviceProvider.GetRequiredService<B>();
    
                Assert.Equal(ServiceLifetime.Transient, b.Lifetime);
                Assert.Equal("B", a.B.Name);
                Assert.Equal("A", c.A.Name);
            }

    这样就可以解决依赖循环的问题了。

    然而这并没有什么卵用,循环引用本来就应该分离业务,而不是用这种花里胡巧的方式解决。另外排除java的东西咱们.net不用的心理,难道字段属性注入它不香吗?

    https://github.com/yeqifeng2288/BoringThings/tree/master/src/DeCircular

  • 相关阅读:
    XtraBackUp 热备份工具
    (原始)数据库的备份与恢复
    liunx 中安装mysql 图形界面 phpmyadmin
    mysql 引擎
    使用正则进行HTML页面属性的替换
    mysql 表锁死的问题
    mysql 函数tree状
    tree 树状构建
    java项目部署jar包
    RSA2
  • 原文地址:https://www.cnblogs.com/yeqifeng2288/p/13138609.html
Copyright © 2020-2023  润新知