【问题标题】:ASP .Net Core get all classes that Implement interface and call their methodsASP .Net Core 获取所有实现接口并调用其方法的类
【发布时间】:2020-11-30 20:22:02
【问题描述】:

我有一个 asp.net core 3.1 网络应用程序

我有一个 接口,它由 3 个类实现来配置数据库映射。我想在应用程序配置设置期间自动调用该方法。

以下是我的接口及其实现。

public interface IMongoMapper
    {
        void Configure();
    }
 class TenantMap : IMongoMapper
{
    public void Configure()
    {
        BsonClassMap.RegisterClassMap<Entities.Tenant>(cm =>
        {
            cm.AutoMap();
        });
    }
}
class CourseMap : IMongoMapper
    {
        public void Configure()
        {
            BsonClassMap.RegisterClassMap<Course>(cm =>
            {
                cm.AutoMap();
            });
        }
    }

如何获取所有实现接口并调用Configure的类 方法合适吗?

【问题讨论】:

  • 展示如何在应用程序启动中获取这些类的实例
  • 除了DI我不知道路。是否可以通过 DI 调用方法?
  • 您可以使用反射来获取所有实现该接口的类型,然后使用 Activator.CreateInstance() 并在创建的实例上执行配置方法
  • 这些类似乎包含(简单)配置。为什么你需要通过你的DI容器注册和解决它们?我希望这些类在启动时创建和使用,此时您通常没有(也不需要)容器)。
  • 您能详细说明为什么此时需要使用反射吗?为什么不能实例化 IMongoMapper 实例列表、循环它们并调用它们的 Configure 方法?

标签: c# asp.net-core .net-core dependency-injection


【解决方案1】:

您可以使用scope.ServiceProvider.GetServices&lt;IMongoMapper&gt;(); 获取所有实现IMongoMapper 接口的类。

您可以使用扩展方法并在startup 类中的Configure 方法中调用它。

public static void IntializeMapping(this IApplicationBuilder app)
{
    using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
        var mappers = scope.ServiceProvider.GetServices<IMongoMapper>();
        foreach (var map in mappers)
        {
             map.Configure();
        }
    }
}

并在startup 类中使用它

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.IntializeMapping();
}

更新

根据Microsoft documentation 更好的方法是使用这个

public static async Task Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();
    using (var scope = host.Services.CreateScope())
    {
        try
        {
            var mappers = scope.ServiceProvider.GetServices<IMongoMapper>();
            foreach (var map in mappers)
            {
                 map.Configure();
            }
        }
        catch (Exception ex)
        {
             var logger = service.GetService<ILogger<Program>>();
             logger.LogError(ex, "An error occurred mapping");
        }
    }
    await host.RunAsync();
}           
 public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                }); 

微软文档 在较早的教程中,您可能会在 Startup.csConfigure 方法中看到类似的代码。我们建议您仅使用Configure 方法来设置请求管道。应用启动代码属于Main方法。

现在,当您第一次运行应用程序时,将创建数据库并使用测试数据播种。每当您更改数据模型时,您都可以删除数据库,更新种子方法,并以相同的方式重新开始使用新数据库。在后面的教程中,您将了解如何在数据模型发生更改时修改数据库,而无需删除并重新创建它。

【讨论】:

  • 如果我通过 IServiceCollection 进行扩展并在配置服务方法中使用会怎样。在 Configure 方法和 ConfigurationService 方法中使用有什么区别
  • aspcore 配置与配置服务
  • @HaseebKhan 我更新了我的答案,希望对你有所帮助:)
  • @HaseebKhan 祝你好运;)
  • @FarhadZamani .. 这需要他们在 configureservice 中注册正确吗?
【解决方案2】:

假设您有示例中提到的派生类的空构造函数,您可以执行以下代码,您可以通过反射获取接口并检查可以分配的类型和!c.IsInterface,以便它不会返回界面本身:

  var result = typeof("Any Class").Assembly.GetTypes().Where(c => typeof(IMongoMapper).IsAssignableFrom(c) && !c.IsInterface);
  foreach (var i in result)
  {
    ((IMongoMapper)Activator.CreateInstance(i)).Configure();
  }

【讨论】:

  • 第一个代码示例回答了这个问题。它发现实现接口的类型并在每个类型上调用Configure 方法。但不清楚为什么它将IServiceCollection 作为参数并返回它。该方法与IServiceCollection无关。它不使用参数。你可以删除它。
  • 如果您想在配置服务中使用它以将其添加为扩展方法,我正在添加额外的代码,如果您想继续链接,它会返回它..
  • 如果它是一个对服务做一些事情的扩展,那就更有意义了。但目前尚不清楚为什么它会成为与services 一起使用的扩展,如果它对它们不做任何事情。扩展只是调用方法和传递参数的简写。没有理由将参数传递给未使用的方法。
  • 如果有帮助,请不要忘记将其标记为有效答案 ;)
【解决方案3】:

你可以通过反射来做到这一点。

我刚刚尝试了下面的代码,它适用于 dotnet core 3.1。 它适用于接口和实现类:

  • 在同一个程序集中
  • 在单独的程序集中
var asm = Assembly.GetAssembly(typeof(YourClass));
var mapperTypes = asm.GetTypes().Where(x => x.GetInterface(nameof(IMongoMapper)) != null);
foreach(var mapperType in mapperTypes)
{
    var mapper = (IMongoMapper)Activator.CreateInstance(mapperType);
    mapper.Configure();
}

您还可以插入创建对象实例所需的任何参数:

IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
    _configuration = configuration;
}

//other code

foreach(var mapperType in mapperTypes)
{
    var mapper = (IMongoMapper)Activator.CreateInstance(mapperType, _configuration);
    mapper.Configure();
}

还有这个问题有很多例子(有些不再在 dotnet core 中工作):Getting all types that implement an interface

【讨论】:

    【解决方案4】:

    在 Startup.cs 中

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IPersonals, Personals>();
        }
    

    【讨论】:

      猜你喜欢
      • 2021-07-08
      • 2016-11-21
      • 1970-01-01
      • 2011-08-16
      • 2020-09-27
      • 2010-09-06
      • 1970-01-01
      • 2017-08-21
      相关资源
      最近更新 更多