转换自引用实体,检测泛型类型

时间:2016-10-24 19:06:58

标签: c# entity-framework generics 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实体框架的每个实体,将创建一个名为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(因为它是一个实体),并通过对自身的引用实现IMutation

public class Test : Entity, IMutation<Test>
{
    public ICollection<Mutation<Test>> Mutations { get; set; } = new List<Mutation<Test>>();
}

实体框架获取它,并创建两个表:

  • Test包含属性IdName
  • Mutation<Test>包含属性Id(来自IEntity的PK)和EntityId(FK引用Test - 实体)

这一切都很棒。数据库架构等等。

所以我想要做的是,总是,当一个实体改变IMutation<EntityName>时,会创建一个新的数据集。 可以覆盖DbContext的SaveChanges。很好,所以我试了一下:

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 && 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 && x.GetGenericTypeDefinition() == typeof(IMutation<>)))
                            .Select(s => (IMutation<IEntity>)s);

但我已经在检查我的实体是否实现了IMutation。 也许有人有一个想法,我怎么能解决这个问题?

1 个答案:

答案 0 :(得分:2)

很难使用非协变且没有非通用对应物的通用接口(例如IEnumerable<T> - &gt; IEnumerableIQueryable<T> - &gt; 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);
}
// ...