#c# #entity-framework #generics #asp.net-core #entity-framework-core
#c# #entity-framework #универсальные #asp.net-core #entity-framework-core
Вопрос:
Однажды я получил интерфейс для всех объектов:
public interface IEntity
{
int Id { get; set; }
}
Для некоторых объектов будет существовать таблица изменений, в которую будет записываться, что было сделано для какого объекта (CRUD)
public interface IMutation<TEntity> where TEntity : IEntity
{
ICollection<Mutation<TEntity>> Mutations { get; set; }
}
Для каждого объекта, который реализует IMutation
Entity Framework, будет создана таблица с именем Mutation<EntityName>
Итак, Mutation<EntityName>
это тоже объект.
public class Mutation<TEntity> : Entity where TEntity : IEntity
{
public int EntityId { get; set; }
public TEntity Entity { get; set; }
}
Я реализовал интерфейс IEntity
для класса, который унаследуют некоторые объекты.
public class Entity : IEntity
{
public int Id { get; set; }
}
Объект Test
наследуется от Entity
(потому что это объект) и реализует имитацию со ссылкой на себя
public class Test : Entity, IMutation<Test>
{
public ICollection<Mutation<Test>> Mutations { get; set; } = new List<Mutation<Test>>();
}
Entity Framework получает это и создает две таблицы:
Test
с помощью свойствId
иName
Mutation<Test>
с помощью свойствId
(PK изIEntity
) иEntityId
(FK, ссылающийся наTest
объект)
все это отлично работает. DB-schema и так далее.
Итак, что я хочу сделать, всегда, когда изменяется одна сущность, которую реализует taht IMutation<EntityName>
, должен быть создан новый набор данных. Существует возможность переопределить SaveChanges
DbContext. Неплохо, поэтому я попробовал это:
public override int SaveChanges()
{
IEnumerable<EntityEntry> entries = ChangeTracker.Entries(); // gets me all entries that were changed
IEnumerable<IEntity> mutationEntries =
entries.Select(s => s.Entity).Where(
w =>
w.GetType()
.GetInterfaces()
.Any(
x =>
x.GetTypeInfo().IsGenericType amp;amp; x.GetGenericTypeDefinition() == typeof(IMutation<>)))
.Select(s => (IEntity)s);
// so here now I got the entries that implement IMutation<?> <-- call this now ?-type
// what I'd now want to do is:
foreach(var entry in mutationEntries)
{
IMutation<?> mutationEntry = (IMutation<?>)entry;
mutationEntry.Mutations.Add(new Mutation<?>{ /* later on, add here CRUD, Id, user who changed,... */ });
}
return base.SaveChanges();
}
Проблема сейчас в том, что я никогда не знаю, какой мой?-Тип есть. Я знаю, что это должно быть из Type IEntity
.
Но когда я пытаюсь разобрать объект на IMutation<IEntity>
, я получаю сообщение об ошибке, в котором говорится, что он не может выполнить приведение из IMutation<Test>
в IMutation<IEntity>
. (Но Test
реализует IEntity
)
Попробовал это таким образом:
IEnumerable<IMutation<IEntity>> mutationEntries =
entries.Select(s => s.Entity).Where(
w =>
w.GetType()
.GetInterfaces()
.Any(
x =>
x.GetTypeInfo().IsGenericType amp;amp; x.GetGenericTypeDefinition() == typeof(IMutation<>)))
.Select(s => (IMutation<IEntity>)s);
Но я уже проверяю, реализует ли моя сущность IMutation
.
Может быть, у кого-нибудь есть идея, как я мог бы решить эту проблему?
Ответ №1:
Сложно работать с универсальными интерфейсами, которые не являются ковариантными и не имеют аналогов, отличных от универсальных (таких как IEnumerable<T>
-> IEnumerable
, IQueryable<T>
-> IQueryable
и т.д.).
Единственным оставшимся выбором в таком случае является отражение или динамическая отправка.
Например, вы могли бы добавить метод, подобный этому:
private void ProcessMutationEntity<TEntity>(TEntity entity)
where TEntity : IEntity, IMutation<TEntity>
{
entity.Mutations.Add(new Mutation<TEntity> { EntityId = entity.Id, Entity = entity});
}
и затем используйте DLR для его вызова (используя код из первого примера):
// ...
foreach (var entry in mutationEntries)
{
ProcessMutationEntity((dynamic)entry);
}
// ...
Комментарии:
1. Пожалуйста, измените свою ProcessMutationEntity-функцию на
private void ProcessMutationEntity<TEntity>(TEntity entity) where TEntity : IEntity, IMutation<TEntity> { entity.Mutations.Add(new Mutation<TEntity> { EntityId = entity.Id, Entity = entity}); }
— ты гениален, чувак! Наконец-то это работает. Вы наверняка сэкономили мне еще 5 часов.2. Вот так:) Рад, что помогло. Приветствия.