Создание общего списка базы данных для перечисления - PullRequest
4 голосов
/ 26 июня 2019

Я пишу простую библиотеку ядра .NET для репликации данных из одной базы данных SQL в другую, используя EF Core.Вместо того, чтобы реплицировать код для каждого DbSet, я пытаюсь найти способ создать общий список, который я могу перечислить и создать некоторую логику.

Я попытался создать кортеж для хранения информации об источникеи таблица назначения, но не может определить универсальный DbSet.

Я также создал собственный класс, используя универсальные шаблоны, чтобы установить тип 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>.

Ответы [ 2 ]

0 голосов
/ 26 июня 2019

Поскольку в 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();
}
0 голосов
/ 26 июня 2019

Обычно вы делаете это, используя List<Something>, где Something - это общий базовый класс или интерфейс, который поддерживаются всеми типами. На данный момент, ближайший к вам object, но вы могли бы иметь возможность добавить неуниверсальный базовый класс / интерфейс к вашему Table<TEntity>. Вопрос, однако, в следующем: будет ли это полезно? в лучшем случае вы могли бы выставить Name; Вы не можете с пользой говорить о DbSet<T> без <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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...