从类型获取DbSet

11

我正在尝试为MVC 6应用程序制作一个通用的表格查看器/编辑器。

目前我使用

Context.GetEntityTypes();

返回一个表格列表。

现在我需要获取特定类型的数据。我的当前实现是:

// On my context
public IQueryable<dynamic> GetDbSetByType(string fullname)
{
    Type targetType = Type.GetType(fullname);

    var model = GetType()
        .GetRuntimeProperties()
        .Where(o =>
            o.PropertyType.IsGenericType &&
            o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
            o.PropertyType.GenericTypeArguments.Contains(targetType))
        .FirstOrDefault();

    if (null != model)
    {
        return (IQueryable<dynamic>)model.GetValue(this);
    }

    return null;
}

使用这段代码在我的控制器中

[HttpGet("{requestedContext}/{requestedTable}/data")]
public IActionResult GetTableData(string requestedContext, string requestedTable)
{
    var data = Request.Query;
    var context = GetContext(requestedContext);

    if (context == null)
    {
        return new ErrorObjectResult("Invalid context specified");
    }
    var entity = context.GetEntity(requestedTable);

    if (entity == null)
    {
        return new ErrorObjectResult("Invalid table specified");
    }

    var set = context.GetDbSetByType(entity.ClrType.AssemblyQualifiedName);

    if (set == null)
    {
        return new ErrorObjectResult("Invalid table specified - DbSet could not be found");
    }

    var start = Convert.ToInt32(data["start"].ToString());
    var count = Convert.ToInt32(data["length"].ToString());
    var search = data["search[value]"];

    return new ObjectResult(set.Skip(start).Take(count));
}
目前情况下,这将返回长度为 count 且从位置 start 开始的数据。但是我无法对 IQueryable<dynamic> 特定属性执行查询。
问题在于:
  1. 这似乎是一件微不足道的事情,所以我几乎确定我错过了什么 - 这应该很容易做到。
  2. 如果不是1,则如何将我的 object set 转换回 DbSet<T>,以便我可以执行我的查询?如果我设置断点并检查,我可以看到所有的数据都在那里。
注意: 这是 EF7 其他信息:
  1. requestedTable 是完全限定类型,例如:<mysystem>.Models.Shared.Users
编辑 (2016/5/5) 我最终只使用了普通的 SQL - 如果有人设法使其正常工作,请告诉我!

2
如果GetType()实际上获取了您的类型(也就是说,所引用的类型实际上在可用程序集中定义),并且是在dbContext的sets中注册的类名,那么您不能只使用context.Set<targettype>吗?这是一个DbSet并继承IQueryable<T>。 - DevilSuichiro
2个回答

3

使用通用方法并使用DbContext.Set<TEntity>()会使这个过程更简单。你可以像这样在运行时创建一个通用方法:

public IActionResult GetTableData(string requestedContext, string requestedTable)
{
    var context = GetContext(requestedContext);

    if (context == null)
    {
        return new ErrorObjectResult("Invalid context specified");
    }
    var entity = context.GetEntity(requestedTable);

    if (entity == null)
    {
        return new ErrorObjectResult("Invalid table specified");
    }

    var boundMethod = s_getTableDataMethodInfo.MakeGenericMethod(entity.ClrType);
    return boundMethod.Invoke(this, new object[] { context }) as IActionResult;
}

private static readonly MethodInfo s_getTableDataMethodInfo
    = typeof(MyController).GetTypeInfo().GetDeclaredMethod("GetTableDataForEntity");

private IActionResult GetTableDataForEntity<TEntity>(DbContext context)
    where TEntity : class
{
    var data = Request.Query;
    var start = Convert.ToInt32(data["start"].ToString());
    var count = Convert.ToInt32(data["length"].ToString());
    var search = data["search[value]"];

    return new ObjectResult(context.Set<TEntity>().Skip(start).Take(count));
}

2
我知道这是一个相当老的问题,并且随着时间的推移已经发生了很多变化。现在,在EF Core 5和6中,可以非常简单地实现所需的结果。
在后台,EF存储了所有映射类型的列表。可以通过 DbContext.DbSet<T>访问它。
代码示例如下:
private IQueryable<T> GetDbSetByType<T>() where T : class
{
    return DbContext.Set<T>().ToList();
}

当然,更复杂的查询可以跟随。为了简单起见,在这里我省略了它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接