所以我是这样做的。我本来更喜欢注入一个IDictionary<string, IShipper>,但由于将“IEnumerable”注入构造函数的限制(这个限制是特定于Unity的),我想出了一个小技巧。
public interface IShipper
{
void ShipOrder(Order ord);
string FriendlyNameInstance { get;}
}
public interface IOrderProcessor
{
void ProcessOrder(String preferredShipperAbbreviation, Order ord);
}
public class Order
{
}
public class FedExShipper : IShipper
{
private readonly Common.Logging.ILog logger;
public static readonly string FriendlyName = typeof(FedExShipper).FullName;
public FedExShipper(Common.Logging.ILog lgr)
{
if (null == lgr)
{
throw new ArgumentOutOfRangeException("Log is null");
}
this.logger = lgr;
}
public string FriendlyNameInstance => FriendlyName;
public void ShipOrder(Order ord)
{
this.logger.Info("I'm shipping the Order with FedEx");
}
public class UpsShipper : IShipper
{
private readonly Common.Logging.ILog logger;
public static readonly string FriendlyName = typeof(UpsShipper).FullName;
public UpsShipper(Common.Logging.ILog lgr)
{
if (null == lgr)
{
throw new ArgumentOutOfRangeException("Log is null");
}
this.logger = lgr;
}
public string FriendlyNameInstance => FriendlyName;
public void ShipOrder(Order ord)
{
this.logger.Info("I'm shipping the Order with Ups");
}
}
public class UspsShipper : IShipper
{
private readonly Common.Logging.ILog logger;
public static readonly string FriendlyName = typeof(UspsShipper).FullName;
public UspsShipper(Common.Logging.ILog lgr)
{
if (null == lgr)
{
throw new ArgumentOutOfRangeException("Log is null");
}
this.logger = lgr;
}
public string FriendlyNameInstance => FriendlyName;
public void ShipOrder(Order ord)
{
this.logger.Info("I'm shipping the Order with Usps");
}
}
public class OrderProcessor : IOrderProcessor
{
private Common.Logging.ILog logger;
IEnumerable<IShipper> shippers;
public OrderProcessor(Common.Logging.ILog lgr, IEnumerable<IShipper> shprs)
{
if (null == lgr)
{
throw new ArgumentOutOfRangeException("Log is null");
}
if (null == shprs)
{
throw new ArgumentOutOfRangeException("ShipperInterface(s) is null");
}
this.logger = lgr;
this.shippers = shprs;
}
public void ProcessOrder(String preferredShipperAbbreviation, Order ord)
{
this.logger.Info(String.Format("About to ship. ({0})", preferredShipperAbbreviation));
foreach (IShipper sh in shippers)
{
this.logger.Info(String.Format("ShipperInterface . ({0})", sh.GetType().Name));
}
IShipper foundShipper = this.FindIShipper(preferredShipperAbbreviation);
foundShipper.ShipOrder(ord);
}
private IShipper FindIShipper(String preferredShipperAbbreviation)
{
IShipper foundShipper = this.shippers.FirstOrDefault(s => s.FriendlyNameInstance.Equals(preferredShipperAbbreviation, StringComparison.OrdinalIgnoreCase));
if (null == foundShipper)
{
throw new ArgumentNullException(
String.Format("ShipperInterface not found in shipperProviderMap. ('{0}')", preferredShipperAbbreviation));
}
return foundShipper;
}
}
调用代码:(例如在“Program.cs”中)
Common.Logging.ILog log = Common.Logging.LogManager.GetLogger(typeof(Program));
IUnityContainer cont = new UnityContainer();
cont.RegisterInstance<ILog>(log);
cont.RegisterType<IShipper, FedExShipper>(FedExShipper.FriendlyName);
cont.RegisterType<IShipper, UspsShipper>(UspsShipper.FriendlyName);
cont.RegisterType<IShipper, UpsShipper>(UpsShipper.FriendlyName);
cont.RegisterType<IOrderProcessor, OrderProcessor>();
Order ord = new Order();
IOrderProcessor iop = cont.Resolve<IOrderProcessor>();
iop.ProcessOrder(FedExShipper.FriendlyName, ord);
日志输出:
2018/09/21 08:13:40:556 [INFO] MyNamespace.Program - About to ship. (MyNamespace.Bal.Shippers.FedExShipper)
2018/09/21 08:13:40:571 [INFO] MyNamespace.Program - ShipperInterface . (FedExShipper)
2018/09/21 08:13:40:572 [INFO] MyNamespace.Program - ShipperInterface . (UspsShipper)
2018/09/21 08:13:40:572 [INFO] MyNamespace.Program - ShipperInterface . (UpsShipper)
2018/09/21 08:13:40:573 [INFO] MyNamespace.Program - I'm shipping the Order with FedEx
因此,每个具体实例都有一个静态字符串以强类型方式提供其名称。("FriendlyName")
然后,我有一个实例字符串获取属性,它使用完全相同的值来保持同步。("FriendlyNameInstance")
通过在接口上使用属性来强制执行此问题(下面是部分代码)
public interface IShipper
{
string FriendlyNameInstance { get;}
}
我可以使用这个方法从承运人集合中“查找”我的承运人。
内部方法“FindIShipper”是一种工厂方法,但它消除了需要单独的IShipperFactory和ShipperFactory接口和类的需求。从而简化了整体设置。仍然遵守构造函数注入和
组合根。
如果有人知道如何使用
IDictionary<string, IShipper>
(并通过构造函数进行注入),请告诉我。
但我的解决方案可行...只需一点点把戏就行了。
我的第三方dll依赖列表。(我正在使用dotnet core,但是使用半新版本的Unity的dotnet framework也可以。) (请参见下面的PackageReference)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Common.Logging" Version="3.4.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
<PackageReference Include="Unity" Version="5.8.11" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
追加:
这里是 autofac 版本:
(使用上述所有相同的接口和具体类)
Program.cs
namespace MyCompany.ProofOfConcepts.AutofacStrategyPatternExample.DemoCommandLineInterfaceOne
{
using System;
using System.Text;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MyCompany.ProofOfConcepts.AutofacStrategyPatternExample.Domain;
using NLog;
using NLog.Extensions.Logging;
public class Program
{
private static Logger programStaticLoggerThatNeedsToBeInitiatedInMainMethod = null;
public static int Main(string[] args)
{
Logger loggerFromNLogLogManagerGetCurrentClassLogger = NLog.LogManager.GetCurrentClassLogger();
programStaticLoggerThatNeedsToBeInitiatedInMainMethod = loggerFromNLogLogManagerGetCurrentClassLogger;
programStaticLoggerThatNeedsToBeInitiatedInMainMethod.Info("programStaticLoggerThatNeedsToBeInitiatedInMainMethod: Main.Start");
try
{
bool useCodeButNotAutofacJson = true;
string autoFacFileName = useCodeButNotAutofacJson ? "autofac.Empty.json" : "autofac.json";
programStaticLoggerThatNeedsToBeInitiatedInMainMethod.Info(string.Format("programStaticLoggerThatNeedsToBeInitiatedInMainMethod: autoFacFileName={0}", autoFacFileName));
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile(autoFacFileName)
.Build();
IServiceProvider servicesProvider = BuildDi(config, useCodeButNotAutofacJson);
using (servicesProvider as IDisposable)
{
IOrderProcessor processor = servicesProvider.GetRequiredService<IOrderProcessor>();
processor.ProcessOrder(FedExShipper.FriendlyName, new Order());
Microsoft.Extensions.Logging.ILogger loggerFromIoc = servicesProvider.GetService<ILoggerFactory>()
.CreateLogger<Program>();
loggerFromIoc.LogInformation("loggerFromIoc:Starting application");
loggerFromIoc.LogInformation("loggerFromIoc:All done!");
Console.WriteLine("Press ANY key to exit");
Console.ReadLine();
}
}
catch (Exception ex)
{
Console.WriteLine(GenerateFullFlatMessage(ex));
programStaticLoggerThatNeedsToBeInitiatedInMainMethod.Error(ex, "programStaticLoggerThatNeedsToBeInitiatedInMainMethod : Stopped program because of exception");
throw;
}
finally
{
LogManager.Shutdown();
}
Console.WriteLine("Returning 0 and exiting.");
return 0;
}
private static IServiceProvider BuildDi(IConfiguration config, bool useCodeButNotAutofacJson)
{
NLog.Extensions.Logging.NLogProviderOptions nlpopts = new NLog.Extensions.Logging.NLogProviderOptions
{
IgnoreEmptyEventId = true,
CaptureMessageTemplates = true,
CaptureMessageProperties = true,
ParseMessageTemplates = true,
IncludeScopes = true,
ShutdownOnDispose = true
};
IServiceCollection sc = new ServiceCollection()
.AddLogging(loggingBuilder =>
{
loggingBuilder.AddNLog(nlpopts);
NLog.LogManager.LoadConfiguration("nlog.config");
})
.AddSingleton<IConfiguration>(config);
Autofac.ContainerBuilder builder = new Autofac.ContainerBuilder();
builder.Populate(sc);
if (useCodeButNotAutofacJson)
{
programStaticLoggerThatNeedsToBeInitiatedInMainMethod.Info("Coding up Autofac DI");
builder.RegisterType<FedExShipper>().As<IShipper>();
builder.RegisterType<UpsShipper>().As<IShipper>();
builder.RegisterType<UspsShipper>().As<IShipper>();
builder.RegisterType<OrderProcessor>().As<IOrderProcessor>();
}
else
{
programStaticLoggerThatNeedsToBeInitiatedInMainMethod.Info("Using .json file to define Autofac DI");
var module = new Autofac.Configuration.ConfigurationModule(config);
builder.RegisterModule(module);
}
Autofac.IContainer autofacContainer = builder.Build();
return new AutofacServiceProvider(autofacContainer);
}
private static string GenerateFullFlatMessage(Exception ex)
{
return GenerateFullFlatMessage(ex, false);
}
private static string GenerateFullFlatMessage(Exception ex, bool showStackTrace)
{
string returnValue;
StringBuilder sb = new StringBuilder();
Exception nestedEx = ex;
while (nestedEx != null)
{
if (!string.IsNullOrEmpty(nestedEx.Message))
{
sb.Append(nestedEx.Message + System.Environment.NewLine);
}
if (showStackTrace && !string.IsNullOrEmpty(nestedEx.StackTrace))
{
sb.Append(nestedEx.StackTrace + System.Environment.NewLine);
}
if (ex is AggregateException)
{
AggregateException ae = ex as AggregateException;
foreach (Exception flatEx in ae.Flatten().InnerExceptions)
{
if (!string.IsNullOrEmpty(flatEx.Message))
{
sb.Append(flatEx.Message + System.Environment.NewLine);
}
if (showStackTrace && !string.IsNullOrEmpty(flatEx.StackTrace))
{
sb.Append(flatEx.StackTrace + System.Environment.NewLine);
}
}
}
nestedEx = nestedEx.InnerException;
}
returnValue = sb.ToString();
return returnValue;
}
}
}
autofac.Empty.json(设置为始终复制)
{}
autofac.json(设置为始终复制)
{
"defaultAssembly": "MyCompany.MyProject",
"components": [
{
"type": "MyCompany.MyProject.Shippers.FedExShipper",
"services": [
{
"type": "MyCompany.MyProject.Shippers.Interfaces.IShipper"
}
]
},
{
"type": "MyCompany.MyProject.Shippers.UpsShipper",
"services": [
{
"type": "MyCompany.MyProject.Shippers.Interfaces.IShipper"
}
]
},
{
"type": "MyCompany.MyProject.Shippers.UspsShipper",
"services": [
{
"type": "MyCompany.MyProject.Shippers.Interfaces.IShipper"
}
]
},
{
"type": "MyCompany.MyProject.Processors.OrderProcessor",
"services": [
{
"type": "MyCompany.MyProject.Processors.Interfaces.IOrderProcessor"
}
]
}
]
}
和 csproj
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="5.1.2" />
<PackageReference Include="Autofac.Configuration" Version="5.1.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.2" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.6.1" />
</ItemGroup>
来自
https://autofaccn.readthedocs.io/en/latest/integration/netcore.html
PS
在autofac版本中,我不得不更改被注入的Logger为LoggerFactory。
这是OrderProcessor的备用版本。您需要为所有3个具体的“Shipper”进行相同的“Microsoft.Extensions.Logging.ILoggerFactory loggerFactory”替代注入。
namespace MyCompany.MyProject.Processors
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
public class OrderProcessor : IOrderProcessor
{
private readonly IEnumerable<IShipper> shippers;
private Microsoft.Extensions.Logging.ILogger<OrderProcessor> logger;
public OrderProcessor(Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, IEnumerable<IShipper> shprs)
{
if (null == loggerFactory)
{
throw new ArgumentOutOfRangeException("loggerFactory is null");
}
if (null == shprs)
{
throw new ArgumentOutOfRangeException("ShipperInterface(s) is null");
}
this.logger = loggerFactory.CreateLogger<OrderProcessor>();
this.shippers = shprs;
}
public void ProcessOrder(string preferredShipperAbbreviation, Order ord)
{
this.logger.LogInformation(string.Format("About to ship. ({0})", preferredShipperAbbreviation));
int counter = 0;
foreach (IShipper sh in this.shippers)
{
this.logger.LogInformation(string.Format("IEnumerable:ShipperInterface. ({0} of {1}) -> ({2})", ++counter, this.shippers.Count(), sh.GetType().Name));
}
IShipper foundShipper = this.FindIShipper(preferredShipperAbbreviation);
foundShipper.ShipOrder(ord);
}
private IShipper FindIShipper(string preferredShipperAbbreviation)
{
IShipper foundShipper = this.shippers.FirstOrDefault(s => s.FriendlyNameInstance.Equals(preferredShipperAbbreviation, StringComparison.OrdinalIgnoreCase));
if (null == foundShipper)
{
throw new ArgumentNullException(
string.Format("ShipperInterface not found in shipperProviderMap. ('{0}')", preferredShipperAbbreviation));
}
return foundShipper;
}
}
}
与autofac无关
nlog.config(设置为始终复制)
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogFile="MyCompany.MyProject.Nlog.internalLogFile.log"
internalLogLevel="Info" >
<targets>
<target xsi:type="File" name="target1" fileName="MyCompany.MyProject.Nlog.MyConsoleAppProgram.log"
layout="${date}|${level:uppercase=true}|${message} ${exception}|${logger}|${all-event-properties}" />
<target xsi:type="Console" name="target2"
layout="${date}|${level:uppercase=true}|${message} ${exception}|${logger}|${all-event-properties}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="target1,target2" />
</rules>
</nlog>
2022年底追加。
如果您需要在多个地方使用以下代码功能(以下代码已从上面复制)...
private IShipper FindIShipper(String preferredShipperAbbreviation)
{
IShipper foundShipper = this.shippers.FirstOrDefault(s => s.FriendlyNameInstance.Equals(preferredShipperAbbreviation, StringComparison.OrdinalIgnoreCase));
if (null == foundShipper)
{
throw new ArgumentNullException(
String.Format("ShipperInterface not found in shipperProviderMap. ('{0}')", preferredShipperAbbreviation));
}
return foundShipper;
}
那么你应该(避免复制和粘贴),并且(更喜欢)将代码封装到自己的“工厂”中。
如果您按照下面的SOF答案链接..它将展示创建“工厂”的基础知识。 (对于这个答案,您将拥有一个IShipperFactory..在下面的答案中被称为“IDbContextsFactory”)。 但是,思路是相同的。
https://stackoverflow.com/a/74391698/214977