代码之家  ›  专栏  ›  技术社区  ›  Elham Azadfar

如何在实体框架中传递字符串作为泛型dbset的类型参数?

  •  0
  • Elham Azadfar  · 技术社区  · 6 年前

    考虑下面的示例代码,将c泛型类型作为参数传递的最简洁方法是什么?

    public dynamic MyDbSet(string typeName)
    {
        var typeofDbSet = typeof(DbSet<>);
        Type[] typeArgs = { Type.GetType(typeName) };
        var type = typeofDbSet.MakeGenericType(typeArgs);
        dynamic dbsetOfT = Activator.CreateInstance(type);
    
        return dbsetOfT;
    
        //Call it
        //var notificationTypes = MyDbSet("NotificationType");
        //var list = notificationTypes.ToList();
    }
    

    或者像这样的:

    public dynamic MyDbSet2(string typeName)
    {
        var keyValuePairs = new Dictionary<string, dynamic>
        {
            {nameof(NotificationType), Set<NotificationType>().AsQueryable()}
        };
    
        return keyValuePairs[typeName];
        //Call it
        //var notificationTypes = MyDbSet2("NotificationType");
        //var list = notificationTypes.ToList();
    }
    
    0 回复  |  直到 6 年前
        1
  •  1
  •   Sohaib Jundi    6 年前

    Activator.CreateInstance(type) 会失败的 DbSet 没有公共的无参数构造函数。
    除非您真的需要将类型名作为字符串传递,否则最好的方法是创建泛型函数,获取构造函数并调用它。可能是这样的:

    public DbSet<T> MyDbSet<T>() where T : class
    {
        return (DbSet<T>)typeof(DbSet<T>).GetConstructor(BindingFlags.NonPublic |
           BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke(null);
    }
    

    那么,你可以顺路叫它 MyDbSet<NotificationType>()

    更新:
    由于需要传递名称,因此可以执行以下操作:

    public dynamic MyDbSet(string typeName)
    {
        return typeof(DbSet<>).MakeGenericType(Type.GetType(typeName)).
            GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
            null, Type.EmptyTypes, null).Invoke(null);
    }
    

    那么,你可以顺路叫它 MyDbSet("<namespace>.NotificationType") 是的。指定命名空间是必需的,否则 Type.GetType 找不到类型并返回 null

        2
  •  1
  •   Sohaib Jundi    6 年前

    请尝试此扩展代码:

    //Use var list = _context.MyDbSet("ConsoleAppEF.Student").ToList();
    public static IQueryable MySet(this SchoolContext context, string typeName)
    {
        var T = Type.GetType(typeName);
        // Get the generic type definition
        MethodInfo method = typeof(SchoolContext)
            .GetMethod("MySet", BindingFlags.Public | BindingFlags.Instance);
    
        // Build a method with the specific type argument you're interested in
        method = method.MakeGenericMethod(T);
    
        return method.Invoke(context, null) as IQueryable;
    }
    
    public static IQueryable<T> Set<T>(this SchoolContext context)
    {
        // Get the generic type definition 
        MethodInfo method = typeof(SchoolContext)
            .GetMethod(nameof(SchoolContext.Set), BindingFlags.Public | BindingFlags.Instance);
    
        // Build a method with the specific type argument you're interested in 
        method = method.MakeGenericMethod(typeof(T));
    
        return method.Invoke(context, null) as IQueryable<T>;
    }
    
    public static IList ToList1(this IQueryable query)
    {
        var genericToList = typeof(Enumerable).GetMethod("ToList")
            .MakeGenericMethod(new Type[] { query.ElementType });
        return (IList)genericToList.Invoke(null, new[] { query });
    }
    

    示例dbcontext:

    public class SchoolContext : DbContext
    {
    
        public SchoolContext() : base("SchoolContext")
        {
    
        }
    
        public DbSet<Student> Students { get; set; }
    
        public virtual new DbSet<TEntity> MySet<TEntity>() where TEntity : BaseEntity
        {
            return base.Set<TEntity>();
        }
    }
    

    示例实体:

    public class Student : BaseEntity
    {
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
    }
    
    public class BaseEntity
    {
        public int Id { get; set; }
    }
    

    使用它:

    class Program
    {
        static void Main(string[] args)
        {
            Seed();
            var _context = new SchoolContext();
            var list = _context.MySet("ConsoleAppEF.Student").ToList1();
        }
    
        private static void Seed()
        {
            var _context = new SchoolContext();
            var students = new List<Student>
            {
                new Student{FirstMidName="Carson",LastName="Alexander"},
            };
    
            students.ForEach(s => _context.Students.Add(s));
            _context.SaveChanges();
        }
    }