[英]Inject ILogger<T> into constructor that only expects ILogger
[英]Inject ILogger<T> into an Abstract Class
作为解决不同问题的一部分,我开始尝试创建一个抽象的 class 以包含解决上述问题的 DeepCopy 功能。
作为我的 DeepCopy 的一部分,我想报告任何问题。
但是,我发现它比不简单地实现 ILogger 更麻烦,就像我实现存储库或服务一样。
实现示例:
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
namespace OrchardCore.A.B{
public abstract class DeepCopyService<T> {
private readonly ILogger<DeepCopyService<T>> _logger;
public DeepCopyService(ILogger<DeepCopyService<T>> logger) {
_logger = logger;
}
/// <summary>
/// Returns a copy of the current object which has no direct reference in memory. The return value is a completely separate object.
/// </summary>
public T DeepCopy() {
if (this == null) return default(T);
try {
var json = JsonConvert.SerializeObject(this, new JsonSerializerSettings {
PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
return JsonConvert.DeserializeObject<T>(json);
} catch (Exception ex) {
_logger.LogError(ex, "There was an issue trying to DeepCopy the value: {obj}", this);
return default(T);
}
}
}
}
从我在来这里之前所做的研究中发现的,我越来越了解我正在尝试的东西并不是注定要成为的。
使用此DeepCopyService
的类将开始需要一个接受ILogger<DeepCopyService<T>>
的构造函数。
我的用例是一个为组织记录数据结构的哑 object。 我想在通过过滤器运行它之前缓存这些数据,具体取决于查看它的用户的当前访问。 我解决的问题是缓存的值正在被修改,因为从缓存中加载信息时,它是通过引用进行的,因此被过滤器更改。 因此,在过滤以破坏参考之前,组织会被深度复制。
Org org = new Org();
Filter(org.DeepCopy());
我不想在每次使用Org
时都传入_logger
。 即使我愿意,object 的 repo 定义在ILogger<OrgRepository>
中,而不是ILogger<DeepCopyService<T>>
。
这就是我觉得它不应该出现的地方。 但是,我也觉得奇怪的是,我无法从摘要中记录问题,因为摘要可能正在做其他特别需要错误记录的事情。
也许我想错了。 但我现在问,也许我正试图以错误的方式解决这个问题,或者由于目前专注于 DI model 的隧道而错过了另一个简单的实现。
如果您在抽象 class 中有构造函数,那么您别无选择,只能在派生自它的任何 class 中使用它。 但是,特别是对于ILogger
,您使抽象 class 使用ILogger
的非泛型版本,这允许派生类使用更具体的记录器实现,因为ILogger<T>
实现ILogger
public abstract class DeepCopyService<T>
{
private readonly ILogger _logger;
public DeepCopyService(ILogger logger)
{
_logger = logger;
}
}
你派生的 class 看起来像这样:
public class DerivedClass : DeepCopyService<Foo>
{
public DeepCopyService(ILogger<DerivedClass> logger)
: base(logger)
{
}
}
您可能还希望使_logger
protected
,以便您也可以在派生类中使用它。
此外,您的示例代码手动new
了Org
object,但您确实应该通过依赖注入来做到这一点。
首先, DeepCopy
只是吞下异常这一事实非常令人困惑,我认为其他开发人员不会期望这种行为,我建议使用bool TryDeepCopy<T>(out T t)
方法。
至于问题本身 - 因为您想通过构造函数( Org org = new Org();
)而不是通过 DI 实例化对象 - 我建议分离关注点,抽象基础 class 将实现DeepCopy
抛出(通过接口抽象) 和一些将执行 rest 的服务:
public interface IDeepCopyable<T>
{
T DeepCopy();
}
public abstract class DeepCopyable<T> : IDeepCopyable<T>
{
/// <summary>
/// Returns a copy of the current object which has no direct reference in memory. The return value is a completely separate object.
/// </summary>
public T DeepCopy()
{
if (this == null) return default(T);
var json = JsonConvert.SerializeObject(this, new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
return JsonConvert.DeserializeObject<T>(json);
}
}
public interface IDeepCopyService<T> where T : IDeepCopyable<T>
{
bool TryDeepCopy(T? toCopy, out T? t);
}
public class DeepCopyService<T> : IDeepCopyService<T> where T : IDeepCopyable<T>
{
private readonly ILogger<DeepCopyService<T>> _logger;
public DeepCopyService(ILogger<DeepCopyService<T>> logger)
{
_logger = logger;
}
public bool TryDeepCopy(T? toCopy, out T? t)
{
t = default;
if (toCopy == null)
{
return true;
}
try
{
t = toCopy.DeepCopy();
}
catch (Exception e)
{
return false;
}
return true;
}
}
标准 DI 支持注册打开 generics,所以用法如下:
class Impl: DeepCopyable<Impl>
{
public int Prop { get; set; }
}
var services = new ServiceCollection();
services.AddLogging();
services.AddTransient(typeof(IDeepCopyService<>), typeof(DeepCopyService<>));
var serviceProvider = services.BuildServiceProvider();
var dcs = serviceProvider.GetRequiredService<IDeepCopyService<Impl>>();
var original = new Impl();
if (dcs.TryDeepCopy(original, out var copied))
{
Console.WriteLine(object.ReferenceEquals(original, copied)); //prints False
}
我认为您有点反对依赖注入流程。
您希望 object 执行操作并记录操作。 日志不属于 object 职责,因此它被委托给外部 object(通过接口抽象),使其成为 object 的依赖项。
因此,您的 object 必须以某种方式获得对您的 object -> 的引用,而最有效的方案是通过构造函数注入此类引用。
这是您使用新操作自己明确创建此 object 的真正问题,因此您在这里并没有真正做任何 DI,因为容器不负责 object 创建 - 您是,因此您有责任为其提供依赖项。
如果 Org object 本身被注入,情况将会改变(让 DI Container 负责它的创建,但我非常怀疑构造函数参数会是一个问题)。
理论上,您可以尝试使用服务定位器反模式,但您已被警告。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.