标签:依赖 Target public ICircularServiceCollection serviceDescriptors 有趣 where class 加载
最近多次遇到循环引用的问题,感觉于找到一种骚操作解决,懒加载。
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的东西咱们不用的心理,难道字段属性注入它不香吗?
标签:依赖,Target,public,ICircularServiceCollection,serviceDescriptors,有趣,where,class,加载 来源: https://www.cnblogs.com/yeqifeng2288/p/13138609.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。