Skip to content

Commit

Permalink
fix: 修复通过接口注入的一些错误.
Browse files Browse the repository at this point in the history
feat: 依赖注入添加SelfOnly字段,可减少非必要的注入提升性能.
  • Loading branch information
joesdu committed Jun 28, 2024
1 parent 8b830d3 commit 8a0aa84
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,4 @@ namespace EasilyNET.AutoDependencyInjection.Core.Abstractions;
/// 实现此接口的类型将自动注册为<see cref="ServiceLifetime.Scoped" />模式
/// </summary>
[IgnoreDependency]
public interface IScopedDependency
{
/// <summary>
/// 是否添加自身
/// </summary>
static bool? AddSelf { get; set; } = false;
}
public interface IScopedDependency;
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,4 @@ namespace EasilyNET.AutoDependencyInjection.Core.Abstractions;
/// 实现此接口的类型将自动注册为<see cref="ServiceLifetime.Singleton" /> 模式
/// </summary>
[IgnoreDependency]
public interface ISingletonDependency
{
/// <summary>
/// 是否添加自身
/// </summary>
static bool? AddSelf { get; set; } = false;
}
public interface ISingletonDependency;
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,4 @@ namespace EasilyNET.AutoDependencyInjection.Core.Abstractions;
/// 实现此接口的类型将自动注册为<see cref="ServiceLifetime.Transient" />模式
/// </summary>
[IgnoreDependency]
public interface ITransientDependency
{
/// <summary>
/// 是否添加自身
/// </summary>
static bool? AddSelf { get; set; } = false;
}
public interface ITransientDependency;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;

// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable ClassNeverInstantiated.Global

namespace EasilyNET.AutoDependencyInjection.Core.Attributes;
Expand All @@ -20,4 +21,9 @@ public sealed class DependencyInjectionAttribute(ServiceLifetime lifetime) : Att
/// </summary>
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public bool AddSelf { get; set; }

/// <summary>
/// 仅注册自身类型,而不注册接口
/// </summary>
public bool SelfOnly { get; set; }
}
5 changes: 3 additions & 2 deletions src/EasilyNET.AutoDependencyInjection/Modules/AppModule.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using EasilyNET.AutoDependencyInjection.Abstractions;
using EasilyNET.AutoDependencyInjection.Contexts;
using System.Collections.Frozen;
using System.Reflection;

namespace EasilyNET.AutoDependencyInjection.Modules;
Expand Down Expand Up @@ -37,8 +38,8 @@ public IEnumerable<Type> GetDependedTypes(Type? moduleType = null)
List<Type> dependList = [];
foreach (var dependedType in dependedTypes)
{
var depends = dependedType.GetDependedTypes().ToArray();
if (depends.Length == 0) continue;
var depends = dependedType.GetDependedTypes().ToFrozenSet();
if (depends.Count == 0) continue;
dependList.AddRange(depends);
foreach (var type in depends) dependList.AddRange(GetDependedTypes(type));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using EasilyNET.AutoDependencyInjection.Core.Attributes;
using EasilyNET.Core.Misc;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Frozen;
using System.Reflection;

// ReSharper disable UnusedType.Global
Expand Down Expand Up @@ -35,39 +36,42 @@ private static void AddAutoInjection(IServiceCollection services)
var types = AssemblyHelper.FindTypes(type =>
(type is { IsClass: true, IsAbstract: false } && baseTypes.Any(b => b.IsAssignableFrom(type))) ||
type.GetCustomAttribute<DependencyInjectionAttribute>() is not null);
foreach (var implementedInterType in types)
foreach (var implementedType in types)
{
var attr = implementedInterType.GetCustomAttribute<DependencyInjectionAttribute>();
var typeInfo = implementedInterType.GetTypeInfo();
var serviceTypes = typeInfo.ImplementedInterfaces
.Where(x => x.HasMatchingGenericArity(typeInfo) && !x.HasAttribute<IgnoreDependencyAttribute>() && x != typeof(IDisposable))
.Select(t => t.GetRegistrationType(typeInfo)).ToList();
var lifetime = GetServiceLifetime(implementedInterType);
var attr = implementedType.GetCustomAttribute<DependencyInjectionAttribute>();
var lifetime = GetServiceLifetime(implementedType);
if (lifetime is null) continue;
if (serviceTypes.Count is 0)
// 优化:直接从属性或特性获取AddSelf和SelfOnly的值,这里的名称属于约定项
var addSelf = attr?.AddSelf ?? GetPropertyValue<bool?>(implementedType, "DependencyInjectionSelf");
var serviceTypes = GetServiceTypes(implementedType);
if (serviceTypes.Count is 0 || addSelf is true)
{
services.Add(new(implementedInterType, implementedInterType, lifetime.Value));
continue;
services.Add(new(implementedType, implementedType, lifetime.Value));
var selfOnly = attr?.SelfOnly ?? GetPropertyValue<bool?>(implementedType, "DependencyInjectionSelfOnly");
if (selfOnly is true || serviceTypes.Count is 0) continue;
}
bool? addSelf = attr?.AddSelf ?? false;
if (addSelf is not true)
{
var addSelfProperty = implementedInterType.GetProperty("AddSelf", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
addSelf = (bool?)(addSelfProperty?.GetValue(implementedInterType) ?? false);
}
if (addSelf is true)
{
services.Add(new(implementedInterType, implementedInterType, lifetime.Value));
}
//var addSelf = (bool?)(implementedInterType.GetProperty("AddSelf", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)?.GetValue(implementedInterType) ?? false);
//if (attr?.AddSelf is true || addSelf is true) services.Add(new(implementedInterType, implementedInterType, lifetime.Value));
foreach (var serviceType in serviceTypes.Where(o => !o.HasAttribute<IgnoreDependencyAttribute>()))
{
services.Add(new(serviceType, implementedInterType, lifetime.Value));
services.Add(new(serviceType, implementedType, lifetime.Value));
}
}
}

private static FrozenSet<Type> GetServiceTypes(Type implementation)
{
var typeInfo = implementation.GetTypeInfo();
return typeInfo.ImplementedInterfaces
.Where(x => x.HasMatchingGenericArity(typeInfo) && !x.HasAttribute<IgnoreDependencyAttribute>() && x != typeof(IDisposable))
.Select(t => t.GetRegistrationType(typeInfo)).ToFrozenSet();
}

// 优化:提取获取静态属性值的通用方法,减少重复代码
private static T? GetPropertyValue<T>(Type type, string name)
{
var property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
return property is null ? default : (T?)property.GetValue(type);
}

/// <summary>
/// 获取服务生命周期
/// </summary>
Expand Down
22 changes: 21 additions & 1 deletion src/EasilyNET.AutoDependencyInjection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,33 @@ WebApplication app = context.GetApplicationHost() as WebApplication;
IHost app = context.GetApplicationHost();
```

**使用接口注入时需要注意**
- 由于无法通过接口的方式来约束类中的静态成员,所以我们这里需要做一个约定.在类中写入如下代码来实现和特性相同的功能.(所以更推荐使用特性的方式注入)
- 若是不声明这两个属性,可能会导致注入了其实现的类或接口,影响获取服务的结果.如在 WPF 中注册 MainWindow.cs,会注册其实现的接口类型.导致无法获取到正确的实现.
- 这里采用较长的名字,避免和类中别的成员出现名称冲突.
- DependencyInjectionSelf 对应 DependencyInjection 特性中的 AddSelf
- DependencyInjectionSelfOnly 对应 DependencyInjection 特性中的 SelfOnly
```csharp
/// <summary>
/// 是否添加自身
/// </summary>
// ReSharper disable once UnusedMember.Global
public static bool? DependencyInjectionSelf => true;

/// <summary>
/// 仅注册自身,而不注其父类或者接口
/// </summary>
// ReSharper disable once UnusedMember.Global
public static bool? DependencyInjectionSelfOnly => true;
```

##### 如何使用

- 使用 Nuget 包管理工具添加依赖包 EasilyNET.AutoDependencyInjection
- a.使用特性注入服务

```csharp
[DependencyInjection(ServiceLifetime.Singleton, AddSelf = true)]
[DependencyInjection(ServiceLifetime.Singleton, AddSelf = true, SelfOnly = true)]
public class XXXService : IXXXService
{
// TODO: do something
Expand Down

0 comments on commit 8a0aa84

Please sign in to comment.