使用实体框架在运行时动态选择列

Posted

技术标签:

【中文标题】使用实体框架在运行时动态选择列【英文标题】:Dynamically select columns in runtime using entity framework 【发布时间】:2014-01-31 19:44:39 【问题描述】:

我有这样的现有功能

public int sFunc(string sCol , int iId)

    string sSqlQuery = "  select  " + sCol + " from TableName where ID = " +  iId ;
    // Executes query and returns value in column sCol

该表有四列存储整数值,我正在使用上述函数分别读取它们。

现在我将其转换为实体框架。

public int sFunc(string sCol , int iId)

     return Convert.ToInt32(TableRepository.Entities.Where(x => x.ID == iId).Select(x => sCol ).FirstOrDefault());

但是上面的函数返回错误

输入的字符串格式不正确

因为它返回列名本身。

我不知道如何解决这个问题,因为我是 EF 的新手。

任何帮助将不胜感激

谢谢

【问题讨论】:

那么,您希望在运行时 确定要选择哪些列?我说的对吗? @TiesonT。没错 【参考方案1】:

这可能有助于解决您的问题:

public int sFunc(string sCol, int iId)

    var _tableRepository = TableRepository.Entities.Where(x => x.ID == iId).Select(e => e).FirstOrDefault();
    if (_tableRepository == null) return 0;

    var _value = _tableRepository.GetType().GetProperties().Where(a => a.Name == sCol).Select(p => p.GetValue(_tableRepository, null)).FirstOrDefault();

    return _value != null ? Convert.ToInt32(_value.ToString()) : 0;

此方法现在适用于动态输入法参数sCol

【讨论】:

感谢您的帮助,但这给了我一个错误(TableRepository.Entities.Entity 不包含 'sCol' 的定义) 这完美地解决了问题,让我得到了我想要的。谢谢 @ArekBal 你想说什么?是的,它与实体框架有关,因为我在集合 TableRepository.Entities 上提供 Linq,这是 EntityObject 的集合,请解释您的问题或为您提供问题的链接,以便我可以为您找到一些东西。是的,它不是动态 linq plz ref Naveen 答案。 这个仍然会访问数据库询问所有列。 请注意,如果您正在处理实体框架,这是一个非常糟糕的答案。这仍然会从数据库中检索所有数据,并且还要求您具有固定的实体类型。【参考方案2】:

您必须尝试使用​​动态 LINQ。详情HERE

【讨论】:

如果您提供链接网页的摘要(以防链接因某种原因中断)或使用网页上详述的工具添加代码示例,则此答案可能会大大改善。跨度> 您提供的链接上没有关于主题的解释。【参考方案3】:

8年后的OP对OP没有用,但是这个问题有很多观点,所以我认为对其他人有正确的答案可能会有所帮助。

如果您使用实体框架,则应该进行 Linq 投影 (Select()),因为这会导致在 db 端进行正确、高效的查询,而不是拉入整个实体。

但是,使用 Linq Select(),您通常必须提供 lambda,因此将列/属性名称放在字符串中是这里的主要困难。

最简单的解决方案是使用动态 LINQ (EntityFramework.DynamicLinq Nuget package)。这个包提供了原始 Linq 方法的替代方案,这些方法将字符串作为参数,并将这些字符串转换为适当的表达式。

例子:

async Task<int> GetIntColumn(int entityId, string intColumnName)

    return await TableRepository.Entities
        .Where(x => x.Id == entityId)
        .Select(intColumnName) // Dynamic Linq projection
        .Cast<int>()
        .SingleAsync();

我还把它变成了一个async 调用,因为现在所有的数据库调用都应该异步执行。当你调用这个方法时,你必须await它才能得到结果(即:var res = await GetIntColumn(...);)。

通用变体

将其更改为IQueryable 上的扩展方法可能更有用,并将列/属性类型变为泛型类型参数,因此您可以将其与任何列/属性一起使用:

(前提是您的所有实体都有一个通用接口来指定 Id 属性。)

public static async Task<TColumn> GetColumn<TEntity, TColumn>(this IQueryable<TEntity> queryable, int entityId, string columnName)
    where TEntity : IEntity

    return await queryable
        .Where(x => x.Id == entityId)
        .Select(columnName) // Dynamic Linq projection
        .Cast<TColumn>()
        .SingleAsync();

这样调用:var result = await TableRepository.Entities.GetColumn&lt;Entity, int&gt;(id, columnName);

接受列列表的通用变体

您可以进一步扩展它以支持动态选择多个列:

public static async Task<dynamic> GetColumns<TEntity>(this IQueryable<TEntity> queryable, int entityId, params string[] columnNames)
    where TEntity : IEntity

    return await queryable
        .Where(x => x.Id == entityId)
        .Select($"new(string.Join(", ", columnNames))")
        .Cast<dynamic>()
        .SingleAsync();

这样调用:var result = await TableRepository.Entities.GetColumns(id, columnName1, columnName2, ...);

由于返回类型及其成员在编译时是未知的,所以我们必须在这里返回dynamic。这使得处理结果变得困难,但如果您只想将其序列化并将其发送回客户端,则可以达到此目的。

【讨论】:

【参考方案4】:

你可以这样做:

        var entity = _dbContext.Find(YourEntity, entityKey);
        // Finds column to select or update
        PropertyInfo propertyInfo = entity.GetType().GetProperty("TheColumnVariable");

【讨论】:

欢迎来到 Stack Overflow!请注意,您正在回答一个非常古老且已经回答的问题。这是How to Answer 的指南。【参考方案5】:

不要将字符串列名作为参数传递,而是尝试传入 lambda 表达式,例如:

sFunc(x => x.FirstColumnName, rowId);
sFunc(x => x.SecondColumnName, rowId);
...

这最终将为您提供列名的智能感知,因此您可以避免列名输入错误时可能出现的错误。

在此处了解更多信息:C# Pass Lambda Expression as Method Parameter

但是,如果你必须保持相同的方法签名,即支持其他/遗留代码,那么你可以试试这个:

public string sFunc(string sCol , int iId)

    return TableRepository.Entities.Where(x => x.ID == iId).Select(x => (string) x.GetType().GetProperty(sCol).GetValue(x));

您可能需要稍微调整一下,我没有快速测试的方法。

【讨论】:

感谢您的帮助,现在我无法更改函数参数。所以你的第二个选择适合我。我已经尝试过了,但出现错误(LINQ to Entities 无法识别方法 'System.Object GetValue)。 我从GetValue 中删除了第二个参数。尝试更新的代码。顺便说一句,您可能想要添加一些错误处理,即发生的情况是找不到给定名称的属性 - 可能是拼写错误的列名。【参考方案6】:

你可以使用反射,像这样(未经测试的代码):

public string sFunc(string sCol , int iId)

  var row = TableRepository.Entities.Where(x => x.ID == iId);
  var type = typeof(row);
  var propInfo = type.GetProperty(sCol);

  if (propInfo != null)
  
    string value = (string)propInfo.GetValue(row);

    return value;
  

  return null;

【讨论】:

和 Ankush Madankar 的回答一样糟糕。根本不做动态选择子句。只是反思。函数的第一行告诉它它有多糟糕。我很高兴您使用 GetProperty 并在那里检查了 null ...

以上是关于使用实体框架在运行时动态选择列的主要内容,如果未能解决你的问题,请参考以下文章

实体框架在运行时更改连接

带有 Firebird 的实体框架引发动态 SQL 错误

在运行时动态创建 Core Data 实体

实体框架在运行时创建多个表 C#

ASP.NET MVC 5 实体框架替代方案或在运行时指定架构的能力?

实体框架运行转换代码时引发异常