如何使用自定义工厂方法注册开放泛型?

2024-01-12

TL;DR:我可以用 Autofac 创建一个通用工厂,以便我可以注入IProduct<TModel>而不是从解决它IFactory我在哪里需要它?有没有办法将工厂解析任务移至组合根?

因此,我使用第三方库,它公开了一些通过工厂创建的通用接口。出于演示目的,我们假设以下代码是库:

第三方库模型:

public interface IFactory
{
    IProduct<TModel> CreateProduct<TModel>(string identifier);
}

internal class Factory : IFactory
{
    private readonly string _privateData = "somevalues";

    public IProduct<TModel> CreateProduct<TModel>(string identifier)
    {
        return new Product<TModel>(_privateData, identifier);
    }
}

public interface IProduct<TModel>
{
    void DoSomething();
}

internal sealed class Product<TModel>: IProduct<TModel>
{
    private readonly string _privateData;
    private readonly string _identifier;

    public Product(string privateData, string identifier)
    {
        _privateData = privateData;
        _identifier = identifier;
    }

    public void DoSomething()
    {
        System.Diagnostics.Debug.WriteLine($"{_privateData} + {_identifier}");
    }
}

My code:

And my TModel:

public class Shoe { }

现在,假设我想要一个IProduct<Shoe> in MyService。我需要在那里解决它:

public class MyService
{
    public MyService(IFactory factory)
    {
        IProduct<Shoe> shoeProduct = factory.CreateProduct<Shoe>("theshoe");
    }
}

但如果我能像这样声明鞋子岂不是更好:

public class ProductIdentifierAttribute : System.Attribute
{
    public string Identifier { get; }

    public ProductIdentifierAttribute(string identifier)
    {
        this.Identifier = identifier;
    }
}

[ProductIdentifier("theshoe")]
public class Shoe { }

然后像这样注入?:

public class MyService
{
    public MyService(IProduct<Shoe> shoeProduct) { }
}

借助 Autofac,我可以使用工厂来创建常规的非泛型类,如下所示:

builder
    .Register<INonGenericProduct>(context =>
    {
        var factory = context.Resolve<INonGenericFactory>();
        return factory.CreateProduct("bob");
    })
    .AsImplementedInterfaces();

但这不适用于泛型类。我必须使用RegisterGeneric。不幸的是,你传递给的类型RegisterGeneric是开放的concrete类型,而不是开放界面类型。我想出了两种解决方法。

解决方法1: 反映IFactory提取_privateData(在真实的库中,这有点复杂,并且涉及访问其他库internal方法和类等),然后将其作为 Autofac 参数提供OnPreparing:

Type factoryType = typeof(Factory);
Type factoryField = factoryType.GetField("_privateData", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Getfield);
Type productType = typeof(Product); // this is `internal` in the third party library, so I have to look it up from the assembly in reality

builder
.RegisterGeneric(productType)
.OnPreparing(preparing =>
{
    var factory = preparing.Context.Resolve<IFactory>();
    var privateFieldValue = factoryField.GetValue(factory);
    var closedProductType = preparing.Component.Activator.LimitType;
    var productModel = closedProductType.GetGenericArguments().Single();
    var productIdentifier = productModel.GetGenericArgument<ProductIdentifierAttribute>().Identifier;

    preparing.Parameters = new List<Parameter>()
    {
        new PositionalParameter(0, privateFieldValue),
        new PositionalParameter(0, productIdentifier)
    };
})
.AsImplementedInterfaces();

但显然这是一个糟糕的解决方案出于多种原因,最重要的是它很容易受到库内部变化的影响。

解决方法2:创建一个虚拟类型并将其替换为OnActivating:

public class DummyProduct<TModel> : IProduct<TModel>
{
    public void DoSomething() => throw new NotImplementedException("");
}

因此,我们将其注册为开放泛型,并在注入之前替换其值:

MethodInfo openProductBuilder = this.GetType().GetMethod(nameof(CreateProduct), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod);
builder
    .RegisterGeneric(typeof(DummyProduct<>))
    .OnActivating(activating => 
    {
        var productModel = activating.Instance.GetType().GetGenericArguments().First();
        var productIdentifier = productModel.GetGenericArgument<ProductIdentifierAttribute>().Identifier;
        var factory = activating.Context.Resolve<IFactory>();
        var closedProductBuilder = openProductBuilder.MakeGenericMethod(productModel);
        object productObject = closedProductBuilder.Invoke(this, new object[] { factory, productIdentifier });
        handler.ReplaceInstance(productObject);
    })
    .AsImplementedInterfaces();

我们有一个辅助方法,这样我们就只依赖于反射方法在这个 Mongo 模块类中:

private IProduct<TModel> CreateProduct<TModel>(IFactory factory, string identifier)
{
    return factory.CreateProduct<TModel>(identifier);
}

现在,显然这比第一种方法更好,并且不依赖太多的反射。不幸的是,它确实涉及到每次我们想要真实对象时创建一个虚拟对象。太糟糕了!

Question:还有其他方法可以使用 Autofac 来做到这一点吗?我可以以某种方式创建 Autofac 可以使用的通用工厂方法吗?我的主要目标是取消创建虚拟类型,并直接跳到调用CreateProduct code.

Notes:我已经删除了一些我通常会做的错误检查等,以使这个问题尽可能短,同时仍然充分展示问题和我当前的解决方案。


如果没有非泛型Create工厂中的方法,您将需要调用MakeGenericMethod.

代替OnActivating事件你可以使用IRegistrationSource组件的作用与解决方法 2 中的相同

internal class FactoryRegistrationSource : IRegistrationSource
{
    private static MethodInfo openProductBuilder = typeof(Factory).GetMethod(nameof(Factory.CreateProduct));

    public Boolean IsAdapterForIndividualComponents => false;

    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        IServiceWithType typedService = service as IServiceWithType;

        if (typedService != null && typedService.ServiceType.IsClosedTypeOf(typeof(IProduct<>)))
        {
            IComponentRegistration registration = RegistrationBuilder.ForDelegate(typedService.ServiceType, (c, p) =>
             {
                 IFactory factory = c.Resolve<IFactory>();

                 Type productModel = typedService.ServiceType.GetGenericArguments().First();
                 String productIdentifier = productModel.GetCustomAttribute<ProductIdentifierAttribute>()?.Identifier;

                 MethodInfo closedProductBuilder = openProductBuilder.MakeGenericMethod(productModel);
                 Object productObject = closedProductBuilder.Invoke(factory, new object[] { productIdentifier });

                 return productObject;
             }).As(service).CreateRegistration();
            yield return registration;
        }
        yield break;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用自定义工厂方法注册开放泛型? 的相关文章

  • BASIC 中的 C 语言中的 PeekInt、PokeInt、Peek、Poke 等效项

    我想知道该命令的等效项是什么Peek and Poke 基本和其他变体 用 C 语言 类似PeekInt PokeInt 整数 涉及内存条的东西 我知道在 C 语言中有很多方法可以做到这一点 我正在尝试将基本程序移植到 C 语言 这只是使用
  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • 类型中的属性名称必须是唯一的

    我正在使用 Entity Framework 5 并且有以下实体 public class User public Int32 Id get set public String Username get set public virtual
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • C# 使用“?” if else 语句设置值这叫什么

    嘿 我刚刚看到以下声明 return name null name NA 我只是想知道这在 NET 中叫什么 是吗 代表即然后执行此操作 这是一个俗称的 条件运算符 三元运算符 http en wikipedia org wiki Tern
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么

随机推荐

  • Delphi:如何在不使用断言的情况下获取(当前代码行,当前单元,当前函数)?

    我正在尝试在我的程序上创建一个日志系统 它将在文本文件上记录调试消息 并且我想保存代码中调用日志消息的确切位置 但我不想使用 Assert 函数 因为它会创建异常 这个系统不仅仅用于记录异常 我还必须编写一些调试信息 使用断言的示例 pro
  • 能否获取windows平台上每个进程的L2缓存未命中计数?

    我想计算每个进程的内存带宽 任何人都可以告诉我如何获得每个进程的二级缓存未命中 谢谢 回族 有一个截屏 http software intel com file 33638在此英特尔性能计数器监视器 http software intel
  • Visual C# 2010 的 SQLite 安装问题

    我正在尝试使用 SQLite 在 Visual C 2010 中创建数据库 但是当我选择 SQLite 数据库文件 时 我没有机会指向数据库文件 我收到一个对话框 要求我提供连接字符串 我无法提供一个不会给出错误 无法加载文件或程序集 Mi
  • 使用 Itextsharp 填写 PDF 表单

    我正在尝试使用 ITextsharp 填写表单 并尝试使用以下代码来获取 pdf 中的所有字段 string pdfTemplate c Temp questionnaire pdf PdfReader pdfReader new PdfR
  • vim 复制并替换文本

    可以说我有这样的文字 test lorem test2 ipsum 我想复制 lorem 并粘贴到 ipsum 中 我尝试做yi 在 lorem 上 然后在 ipsum 上做了ci 但这用 ipsum 取代了我的 Pastebin 我以前的
  • 绑定时,knockoutjs 选择更改事件被触发

    我有这个淘汰码 http jsfiddle net nickbuus Rwabt http jsfiddle net nickbuus Rwabt 问题是 每当下拉选择框被填满时 就会调用更改事件
  • 为什么这种类型的双关不是未定义的行为?

    这是一个我认为会调用未定义行为的玩具示例 include
  • 突出显示活动选项卡 - CSS

    我有一个使用 CSS 的小型选项卡式导航设置 当鼠标悬停在选项卡上时 颜色会发生变化 很棒 但是 当我单击一个选项卡并导航到相应的页面时 我希望该选项卡 活动选项卡 保持突出显示 指示当前页面 我目前正在通过使用类 currenttab 然
  • 如何限制asp.net中的文件夹访问

    如何限制asp net中的文件夹访问 就像我不希望任何其他人通过链接在浏览器中看到我的上传文件夹http www example com Uploads http www example com Uploads 对于下一代 对我有用的答案是
  • 列表作为字典的键

    我有多个元组列表 例如 1 2 3 4 2 5 6 7 3 我希望将其作为字典的键 因此字典中的每个键都是元组列表 不幸的是 根据TypeError我越来越 unhashable type list 看来python不喜欢哈希列表 我的元组
  • VBScript DateDiff 月份

    我在获取两个日期之间的月份日期差异时遇到问题 这是一个示例 DateDiff m 2014 10 17 2014 10 30 上面的代码返回 0 个月 因为它还不到一个月 但 DateDiff m 2014 10 17 2014 11 01
  • MongoDB C# 使用 Guid 更新插入

    当尝试在 Mongo 中执行 upsert 操作时 我希望它生成 ID 的 GUID 而不是对象 ID 在本例中 我正在检查以确保具有特定属性的对象尚不存在 并且如果发生更新 实际上会引发异常 这是类定义的存根 public class E
  • 我是否需要同步 invokeAll 调用的结果?

    我正在增强现有的算法 该算法由多个独立步骤组成 以使用并发任务 每个任务都会创建多个对象来保存其结果 最后 我想要一个从控制方法返回的所有结果的列表 目前 我的代码看起来像这样 private final ExecutorService p
  • iOS 上的 Chrome 中未触发 .click 事件

    当用户单击移动设备上的菜单栏时 我使用以下代码在我的网站上显示下拉菜单 mobile menu click function nav toggleClass open mobile menu toggleClass open 它在桌面浏览器
  • 使用正则表达式解析有效的父目录

    给定字符串a b c d它代表一个完全限定的子目录我想为父树上的每个步骤生成一系列字符串 即a b c a b and a 使用正则表达式我可以做一个非贪婪的 这会给我匹配的a b and c或贪婪的 这会给我一场比赛a b c 有没有办法
  • 我可以随我的应用程序一起分发 JDK 吗?

    我正在开发一个名为妖娆 http enchanting robotclub ab ca 该应用程序基于Scratch http scratch mit edu 发出 Java 源代码并对其进行编译 以便上传到 LEGO Mindstorms
  • 用于生产时 Sklearn MultiLabelBinarizer() 错误

    编辑 我已更改代码 从 mlb 更改为 TfIdfVectorizer 我仍然面临一个问题 请看下面我的代码 from sklearn externals import joblib from sklearn preprocessing i
  • 配置 Gradle 以将主类和测试类包含到我的 .jar 存档中

    如何配置 Gradle 以将主类和测试类包含到我的 jar 存档中 目前构建的 jar构建 库目录缺少我的测试类 它仅包括来自以下来源的类项目根目录 src main java com packageGradle 将它们编译成后的 jar
  • 如何在应用程序未运行时模拟位置

    我正在构建一个具有地理围栏功能的 iOS 8 应用程序 我需要对此进行测试 并且我知道我可以使用预配置的位置或提供 GPX 文件来模拟 Xcode 中的位置 然而 由于这个应用程序需要在被杀死时根据区域监控进行操作 用户在多任务处理中向上滑
  • 如何使用自定义工厂方法注册开放泛型?

    TL DR 我可以用 Autofac 创建一个通用工厂 以便我可以注入IProduct