从类型获取DbSet

本文关键字:DbSet 获取 类型 | 更新日期: 2023-09-27 18:26:49

我正在尝试为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是完全限定类型EG:<mysystem>.Models.Shared.Users

编辑(2016/5/5)

我最终只是用纯SQL完成了这一切——如果有人能做到这一点,请告诉我!

从类型获取DbSet

使用泛型方法和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));
}

我知道这是一个相当古老的问题,随着时间的推移已经发生了很多变化。如今在EF Core 5&6可以非常简单地实现期望的结果。

EF在后台存储一个列表,其中包含映射的所有类型。这可以通过DbContext.DbSet<T>访问。

这看起来像这样:

private IQueryable<T> GetDbSetByType<T>() where T : class
{
    return DbContext.Set<T>().ToList();
}

当然,更多复杂的查询可能随之而来。为了简单起见,我在这里省略了这一点。