代码之家  ›  专栏  ›  技术社区  ›  Andrus

不在DbContext中使用Find from子类

  •  0
  • Andrus  · 技术社区  · 4 年前

        class MyStudents: Students { }
    
        public TEntity FindNormalized<TEntity>(params object[] keyValues)
            where TEntity : class
        { 
            return Find<TEntity>(keyValues);
        }
    
        void FindNormalized<TEntity>(TEntity entity, params object[] keyValues)
            where TEntity : class
        {
            var res = FindNormalized<TEntity>(keyValues);
            if (res == null)
                throw new KeyNotFoundException(entity.GetType().Name + " entity key not found " + keyValues[0]);
            CopyPropertiesTo(res, entity);
        }
    
        static void CopyPropertiesTo<T, TU>(T source, TU dest)
        { // https://stackoverflow.com/questions/3445784/copy-the-property-values-to-another-object-with-c-sharp
            var sourceProps = typeof(T).GetProperties().Where(x => x.CanRead).ToList();
            var destProps = typeof(TU).GetProperties()
                    .Where(x => x.CanWrite)
                    .ToList();
    
            foreach (var sourceProp in sourceProps)
                if (destProps.Any(x => x.Name == sourceProp.Name))
                {
                    var p = destProps.First(x => x.Name == sourceProp.Name);
                    p.SetValue(dest, sourceProp.GetValue(source, null), null);
                }
        }
    

    与子类一起使用

       FindNormalized<MyStudents>(1);
    

    引发异常

    包含在上下文的模型中。

       FindNormalized<Students>(1);
    

    如何解决这个问题,以便它也可以与子类类型一起使用?

        string GetTableAttrib(Type t)
        {
            foreach (Type t2 in SelfAndBaseClasses(t))
            {
                IEntityType entityType = Model.FindEntityType(t2);
                if (entityType == null)
                    continue;
                return entityType.GetTableName();
            }
            throw new ApplicationException(t.FullName + " no table attribute");
        }
    
        /// <summary>
        /// Enumerate inheritance chain - copied from DynamicLinq
        /// </summary>
        static IEnumerable<Type> SelfAndBaseClasses(Type type)
        {
            while (type != null)
            {
                yield return type;
                type = type.BaseType;
            }
        }
    

    0 回复  |  直到 4 年前
        1
  •  1
  •   King King    4 年前

    EFCore Find TEntity 不是实体类型,而是从实体类型派生的类型。所以要解决这个问题,我们首先需要得到最派生的类型,也就是传入对象的祖先类型 MyStudents Students 第一个用作的类型参数 Find<> 方法。因为这种类型在设计时是未知的,所以我们需要使用反射来调用它 查找 DbContext.Find .

    所以解决方案可以很简单,首先我们需要方法从传入的类型(继承自实体类型)中获取最派生的实体类型:

    public static class DbContextExtensions {
         public static Type GetMostDerivedEntityType(this DbContext dbContext, Type type){
             while(type != null && dbContext.Model.FindEntityType(type) == null) {
                 type = type.BaseType;
             }
             return type;
         }
    }
    

    下一步只需在 FindNormalized 方法在使用之前首先查找最派生的实体类型 查找 :

    //I suppose that this is in the context (class) of your custom DbContext
    void FindNormalized<TEntity>(TEntity entity, params object[] keyValues)
        where TEntity : class
    {
        var actualEntityType = this.GetMostDerivedEntityType(typeof(TEntity)) ?? 
                               throw new InvalidOperationException($"The type {typeof(TEntity)} is not an entity type or a derive from an entity type");
        //use the actualEntityType instead
        var res = Find(actualEntityType, keyValues);
        if (res == null)
            throw new KeyNotFoundException(entity.GetType().Name + " entity key not found " + keyValues[0]);
        //copy the properties
        CopyPropertiesTo(res, entity);
    }
    

    查找 接受了 Type (实体类型)直接而不是使用包装器 它是泛型的(你可以添加一个重载的包装器来包装非泛型的 查找 直接在上面的代码中使用)。

    我根本没有测试过代码。试试看,如果有什么错误就告诉我。