创建用于枚举的DBSET的通用列表



我正在编写一个简单的.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();
}

最新更新