使用反射查找 Type() 的所有 类,对它们进行排序,并调用命名方法
Using reflection to find all Classes of a Type(), ordering them, and invoking a named method
我借鉴了 EFC 并尝试为 SQL 命令开发类似于 modelBuilder.ApplyConfigurationsFromAssembly()
的东西,我需要以特定顺序对我的数据库 运行。
我想调用任何 class 的所有方法,其中 class 继承自我的 Interface
。但我想按组或顺序调用它们。
我的界面是:
public interface IDatabaseSqlCommands
{
public ExecutionGroups ExecutionGroup { get; }
public Task Configure(DbContext context);
}
一个嵌套的例子 class 是:
private sealed class SqlCommands : IDatabaseSqlCommands
{
public ExecutionGroups ExecutionGroup { get; } = ExecutionGroups.G1;
public async Task Configure(DbContext ctx)
{
await ctx.Database.ExecuteSqlRawAsync(
@"
CREATE TRIGGER Customers_Timestamp
AFTER UPDATE ON Customers
BEGIN
UPDATE Customers
SET Version = Version + 1
WHERE rowid = NEW.rowid;
END
");
}
}
所以使用 System.Reflection
我能够找到接口并使用以下方法调用接口:
public async Task EnsureSqlCreatedAsync()
{
// We are only interested in non-abstact private nested classes which are sealed
var classCollection = Assembly.GetExecutingAssembly()
.DefinedTypes.Where(
t => !t.IsAbstract &&
t.IsClass &&
t.IsSealed &&
t.IsNestedPrivate);
foreach (var type in classCollection)
{
// Class must have an empty constructor
if (type.GetConstructor(Type.EmptyTypes) == null)
continue; // Constructor is not empty
foreach (var @interface in type.GetInterfaces())
{
if (@interface.GetTypeInfo() == typeof(IDatabaseSqlCommands))
{
var instance = Activator.CreateInstance(type);
await ((IDatabaseSqlCommands)instance).Configure(this);
return;
}
}
}
}
所以一切正常,所有实例都被调用,但我现在想做的是
- 找到所有具有该接口的 class;和
- 按ExecutionGroup
的顺序调用Configure()
ExecutionGroups
的定义是:
public enum ExecutionGroups
{
G1=1, G2=2, G3=3, G4=4, G5=6
}
我无法计算出 Linq 按 ExecutionGroup 的值对我的 classes 进行排序,以便我的 Sql 按依赖顺序执行。
您可以像这样将循环转换为 LINQ 查询(并在那里进行排序):
var commands = classCollection
.Where(type =>
type.GetConstructor(Type.EmptyTypes) != null &&
type.GetInterfaces().Any(i => i.GetTypeInfo() == typeof(IDatabaseSqlCommands)))
.Select(type => (IDatabaseSqlCommands) Activator.CreateInstance(type))
.OrderBy(c => c.ExecutionGroup);
foreach (var command in commands) {
await command.Configure(this);
}
我借鉴了 EFC 并尝试为 SQL 命令开发类似于 modelBuilder.ApplyConfigurationsFromAssembly()
的东西,我需要以特定顺序对我的数据库 运行。
我想调用任何 class 的所有方法,其中 class 继承自我的 Interface
。但我想按组或顺序调用它们。
我的界面是:
public interface IDatabaseSqlCommands
{
public ExecutionGroups ExecutionGroup { get; }
public Task Configure(DbContext context);
}
一个嵌套的例子 class 是:
private sealed class SqlCommands : IDatabaseSqlCommands
{
public ExecutionGroups ExecutionGroup { get; } = ExecutionGroups.G1;
public async Task Configure(DbContext ctx)
{
await ctx.Database.ExecuteSqlRawAsync(
@"
CREATE TRIGGER Customers_Timestamp
AFTER UPDATE ON Customers
BEGIN
UPDATE Customers
SET Version = Version + 1
WHERE rowid = NEW.rowid;
END
");
}
}
所以使用 System.Reflection
我能够找到接口并使用以下方法调用接口:
public async Task EnsureSqlCreatedAsync()
{
// We are only interested in non-abstact private nested classes which are sealed
var classCollection = Assembly.GetExecutingAssembly()
.DefinedTypes.Where(
t => !t.IsAbstract &&
t.IsClass &&
t.IsSealed &&
t.IsNestedPrivate);
foreach (var type in classCollection)
{
// Class must have an empty constructor
if (type.GetConstructor(Type.EmptyTypes) == null)
continue; // Constructor is not empty
foreach (var @interface in type.GetInterfaces())
{
if (@interface.GetTypeInfo() == typeof(IDatabaseSqlCommands))
{
var instance = Activator.CreateInstance(type);
await ((IDatabaseSqlCommands)instance).Configure(this);
return;
}
}
}
}
所以一切正常,所有实例都被调用,但我现在想做的是
- 找到所有具有该接口的 class;和
- 按ExecutionGroup 的顺序调用
Configure()
ExecutionGroups
的定义是:
public enum ExecutionGroups
{
G1=1, G2=2, G3=3, G4=4, G5=6
}
我无法计算出 Linq 按 ExecutionGroup 的值对我的 classes 进行排序,以便我的 Sql 按依赖顺序执行。
您可以像这样将循环转换为 LINQ 查询(并在那里进行排序):
var commands = classCollection
.Where(type =>
type.GetConstructor(Type.EmptyTypes) != null &&
type.GetInterfaces().Any(i => i.GetTypeInfo() == typeof(IDatabaseSqlCommands)))
.Select(type => (IDatabaseSqlCommands) Activator.CreateInstance(type))
.OrderBy(c => c.ExecutionGroup);
foreach (var command in commands) {
await command.Configure(this);
}