繁体   English   中英

注入 ILogger<t> 成抽象 Class</t>

[英]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 ,以便您也可以在派生类中使用它。

此外,您的示例代码手动newOrg 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM