C#基于Autocad的二次开发(7.服务自动注入-NLog)

开始之前确保已完成步骤1创建解决方案和类库项目,并在对应项目中安装nuget包

自动注入

项目中需要通过Autofac注入多个服务时,可通过反射机制实现服务的自动注入。
实现思路为:定义一个IBaseService接口,所有需要注入的服务都继承此接口,通过反射获取所有继承了IBaseService的服务和其实现类并注入。Microsoft.Extensions.DependencyInjection也可通过此方式实现。

ACD.Application项目中创建接口文件IBaseService.cs

1
2
public interface IBaseService {  }

ACD.Application项目中创建类文件Extensions.AppDomain.cs,封装AppDomain的扩展方法实现通过反射加载引用的程序集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static partial class Extensions
{
public static List<Assembly> GetReferanceAssemblies(this AppDomain domain, Func<AssemblyName, bool> func)
{
var list = new List<Assembly>();
domain.GetAssemblies().ForEachItem(i =>
{
GetReferanceAssemblies(i, list,func);
});
return list;
}
static void GetReferanceAssemblies(Assembly assembly, List<Assembly> list, Func<AssemblyName, bool> func)
{
assembly.GetReferencedAssemblies().WhereIF(true, func).ForEachItem(i =>
{
var ass = Assembly.Load(i);
if (!list.Contains(ass))
{
list.Add(ass);
GetReferanceAssemblies(ass, list,func);
}
});
}
}

ACD.Infrastructure项目中的Startup中实现注入方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public static class Startup
{
public static ContainerBuilder AddInfrastructure(this ContainerBuilder builder)
{
return builder
.AddServices();
}

/// <summary>
/// 配置注入
/// </summary>
/// <param name="builder"></param>
/// <param name="configFileName"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public static async Task<AppSettingConfig> AddAppConfig(this ContainerBuilder builder,string configFileName = "appsettings.json")
{
var configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, configFileName);

if (!File.Exists(configFile)) throw new FileNotFoundException($"{configFileName} 配置文件不存在");

string jsonString;
using (var stream = File.OpenRead(configFile))
{
var reader = new StreamReader(stream);
jsonString = await reader.ReadToEndAsync();
}

var appSetting = JsonConvert.DeserializeObject<AppSettingConfig>(jsonString);

builder
.RegisterInstance(appSetting)
.SingleInstance();

return appSetting;
}

/// <summary>
/// 服务注入
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
private static ContainerBuilder AddServices(this ContainerBuilder builder)
{
var interfaceType = typeof(IBaseService);

//通过表达式过滤不需要加载的程序集
var types =
AppDomain.CurrentDomain.GetReferanceAssemblies(x => x.FullName.StartsWith("ACD."));

var interfaceTypes = types
.SelectMany(s => s.GetTypes())
.Where(t => interfaceType.IsAssignableFrom(t)
&& t.IsClass && !t.IsAbstract)
.Select(t => new
{
Service = t.GetInterfaces().FirstOrDefault(),
Implementation = t
})
.Where(t => t.Service != null
&& interfaceType.IsAssignableFrom(t.Service));

foreach (var type in interfaceTypes)
{
//这里默认为瞬时注入,可根据传入参数决定注入的生命周期
//或者将IBaseService拆分为三个接口,对应三个不同的生命周期分别注入
builder.RegisterType(type.Implementation)
.As(type.Service)
.InstancePerDependency();
}

return builder;
}
}

如果要将多个服务按不同的生命周期分别注入,实现思路一样:删除IBaseService.cs接口文件,用三个接口文件例如IScopeService,ITransientService,ISingletonService代替其作用
继承自这三个接口的服务分别注入不同的生命周期

ACD.Client项目中AppInit.cs调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
internal class AppInit
{
private static IContainer Container;

internal static async Task Init()
{
var builder = new ContainerBuilder();

var config = await builder.AddAppConfig("appsettings.json");

builder.AddInfrastructure();

Container = builder.Build();
}

internal static T Resolve<T>() => Container.Resolve<T>();
}

NLog注入为例

项目ACD.Application中创建接口文件IloggerService.cs,继承自IBaseService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IloggerService:IBaseService
{
void Info(string msg);

void Info(string msg,params object[] args);

void Warn(string msg);

void Warn(string msg, params object[] args);

void Error(string msg);

void Error(string msg, params object[] args);
}

项目ACD.Infrastructure中创建类文件LoggerService.cs,实现自IloggerService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class LoggerService : IloggerService
{
private readonly ILogger _logger;
public LoggerService(ILogger logger)
{
_logger = logger;
}

public void Error(string msg)=>_logger.Error(msg);

public void Error(string msg, params object[] args) => _logger.Error(msg, args);


public void Info(string msg)=>_logger.Info(msg);

public void Info(string msg, params object[] args)=>_logger.Info(msg, args);


public void Warn(string msg)=>_logger.Warn(msg);

public void Warn(string msg, params object[] args) => _logger.Warn(msg, args);

}

项目ACD.Infrastructure中Startup.cs静态类中的AddInfrastructure方法添加以下代码,注入NLog模块

1
builder.RegisterModule<NLogModule>();

修改后

1
2
3
4
5
6
7
public static ContainerBuilder AddInfrastructure(this ContainerBuilder builder)
{
builder.RegisterModule<NLogModule>();

return builder
.AddServices();
}

至此IloggerService已注入完成,使用方法同步骤6.配置文件注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//C/S端:

await AppInit.Init();

//B/S端:

将AppInit中的内容在Global.asax中或ApplicationStart中实现

//使用

IloggerService _logger = AppInit.Resolve<IloggerService>();
_logger.Error("hello");

//or

public class ACDesign
{
private readonly IloggerService _logger;

public ACDesign(IloggerService logger) => _logger = logger;

public void Demo()
{
// _logger.Error("hello");
}
}

NLog需要单独的配置文件,ACD.Client创建nlog.config,配置文件内容网上资料很多,示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<targets>
<!--此部分中的所有目标将自动异步-->
<target name="asyncFile" xsi:type="AsyncWrapper">
<!--项目日志保存文件路径说明fileName="${basedir}/保存目录,以年月日的格式创建/${shortdate}/${记录器名称}-${单级记录}-${shortdate}.txt"-->
<target name="log_file" xsi:type="File"
fileName="${basedir}/ACDesignLogs/${shortdate}/${level}-${shortdate}.txt"
layout="${longdate} | ${message} ${onexception:${exception:format=message} ${newline} ${stacktrace} ${newline}"
archiveFileName="${basedir}/archives/${level}-${shortdate}-{#####}.txt"
archiveAboveSize="102400"
archiveNumbering="Sequence"
concurrentWrites="true"
keepFileOpen="false" />
</target>
<!--使用可自定义的着色将日志消息写入控制台-->
<target name="colorConsole" xsi:type="ColoredConsole" layout="[${date:format=HH\:mm\:ss}]:${message} ${exception:format=message}" />
</targets>

<!--规则配置,final - 最终规则匹配后不处理任何规则-->
<rules>
<logger name="Microsoft.*" minlevel="Info" writeTo="" final="true" />
<logger name="*" minlevel="Info" writeTo="asyncFile" />
<logger name="*" minlevel="Warn" writeTo="colorConsole" />
</rules>
</nlog>

C#基于Autocad的二次开发(7.服务自动注入-NLog)
https://wangyuangen.github.io/2024/05/20/CsharpAutocad7/
作者
Yuangen Wang
发布于
2024年5月20日
许可协议