我正在编写一个简单的.NET核心库,将数据从一个SQL数据库复制到另一个使用EF Core。我没有为每个DBSET复制代码,而是试图找到一种创建通用列表的方法
我试图创建一个元组来保存有关源和目标表的信息,但无法定义通用DBSET。
我还使用generics创建了一个自定义类来设置DBSET类型,但由于每个类类型都不同,因此无法将其添加到列表中。
示例方法:
public void Execute()
{
var source = new SourceContext();
var destination = new DestinationContext();
Console.WriteLine("Processing table A");
destination.RemoveRange(destination.TableA);
destination.SaveChanges();
destination.AddRange(source.TableA.AsNoTracking().ToList());
destination.SaveChanges();
}
为了不复制其他表格的代码,请尝试使用元组,例如
var tables = new List<Tuple<string, DbSet<T>, DbSet<T>>>
{
Tuple.Create("Table A", source.TableA, destination.TableA),
Tuple.Create("Table B", source.TableB, destination.TableB)
};
问题是用通用DBSET定义元组,因为所添加的每个项目都有不同的类型。
考虑创建一个定义表的类,例如
internal class Table<TEntity> where TEntity : class
{
internal string Name {get; set;}
internal DbSet<TEntity> Source {get; set;}
internal DbSet<TEntity> Destination {get; set;}
internal Table(string name, DbSet<TEntity> source, DbSet<TEntity> destination)
{
Name = name;
Source = source;
Destination = destination;
}
}
但是如何在没有特定类型的情况下创建List
:
var tables = new List<T>
{
new Table<TableA>("Table A", source.TableA, destination.TableA),
new Table<TableB>("Table B", source.TableB, destination.TableB)
};
List
需要使用类型<T>
进行实例化。
通常这样做的方式是使用List<Something>
,其中Something
是所有类型都将支持的常见基层或接口。目前,您最接近的是object
,但是您可能可以将一些>>>>>>>> base-class/接口添加到您的Table<TEntity>
。但是,问题是:它有用吗?充其量您可以暴露Name
;如果没有<T>
,则不能有用讨论DbSet<T>
,除非是非生成IEnumerable
/IQueryable
;所以:
internal interface ITable
{
string Name {get;}
IQueryable Source {get;}
IQueryable Destination {get;}
Type Type {get;}
}
并使用List<ITable>
?
Table<T>
变为:
internal class Table<TEntity> : ITable where TEntity : class
{
internal string Name {get; set;}
internal DbSet<TEntity> Source {get; set;}
internal DbSet<TEntity> Destination {get; set;}
string ITable.Name => Name;
IQueryable ITable.Source => Source;
IQueryable ITable.Destination => Destination;
Type ITable.Type => typeof(T);
internal Table(string name, DbSet<TEntity> source, DbSet<TEntity> destination)
{
Name = name;
Source = source;
Destination = destination;
}
}
由于EF Core没有非传播Set
方法,因此 @DavidG提出的使Execute
通用的建议看起来像是要走的方式。您只需要对每种类型的跳板进行"跳板",并进行一点反射即可。
var source = new SourceContext();
var destination = new DestinationContext();
var tables = new List<(string, Type)>
{
("Table A", typeof(TableA)),
("Table B", typeof(TableB))
};
var executeMethodInfo = GetType().GetMethod("Execute");
foreach (var (displayName, entityType) in tables)
{
executeMethodInfo.MakeGenericMethod(entityType)
.Invoke(this, new object[] { displayName, source, destination });
}
通用Execute
方法看起来像这样:
public void Execute<T>(string displayName, SourceContext source, DestinationContext destination)
where T : class
{
Console.WriteLine($"Processing {displayName}");
destination.RemoveRange(destination.Set<T>());
destination.SaveChanges();
destination.AddRange(source.Set<T>().AsNoTracking().ToList());
destination.SaveChanges();
}