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

使用linq读取byte[]或float到实体

  •  2
  • rst  · 技术社区  · 6 年前

    我有一个表,其中一列是二进制的,另一列是浮点的。我需要使用sql选择以下内容

    SELECT ISNULL(myBinary, myFloat) FROM table
    

    很好,我得到一个列,所有二进制

    0x3F800000
    0xE5C13DBAB611123B47A7
    0x9946C3BA9946C3BA9946
    0xDE0E1D3C8B7A143C6DB7
    0x3F800000
    

    等。

    现在我想使用linq to entities进行这个查询,但是我找不到可以编译的代码

    context.table.select(s => new MyObject()
      { 
         Result = s.myBinary ?? s.myFloat // <--- '??' operator cannot be applied to operands of type 'byte[]' and 'float'
      });
    
     class MyObject { public Object Result {get; set;} }
    

    我如何获得这些价值观?BitConverter也不工作(在浮点上)

    更新

    为什么我问:所以如果我选择两列分开,我得到更多的执行时间

    set statistics time on
    SELECT TOP (5000) ISNULL([x], [y])
      FROM [table];
    set statistics time off
    set statistics time on
    SELECT TOP (5000) [x], [y]
      FROM [table];
      set statistics time off
    

    收益率(即使执行多次,也总是相同的)

    (5000 rows affected)
    
     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 7 ms.
    
    (5000 rows affected)
    
     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 113 ms.
    

    注:在一条评论中我写了因子40,那是用另一种测量方法。统计时间约为因子10。

    如果我增加行数,我得到

    (50000 rows affected)
    
     SQL Server Execution Times:
       CPU time = 47 ms,  elapsed time = 820 ms. (with ISNULL)
    
    (50000 rows affected)
    
     SQL Server Execution Times:
       CPU time = 16 ms,  elapsed time = 1365 ms.
    

    更少的行(大约1000或更少),执行时间是不可测量相等的,所以大约1毫秒。但是:我期望每查询5000到10000行。

    3 回复  |  直到 6 年前
        1
  •  3
  •   Ivan Stoev    6 年前

    作为解决方案,我可以提供一个自定义存储函数,映射到内置的 ISNULL SQL函数。

    如果您使用的是代码优先模型,则需要 EntityFramework.Functions 包裹。

    但是由于您使用的是EDMX,所以该过程有点复杂,需要手工编辑EDMX文件。这项技术部分被 How to: Define Custom Functions in the Storage Model MSDN主题。

    使用XML(文本)编辑器打开EDMX文件。定位 Schema 的子元素 edmx:StorageModels 元素并在其中添加以下内容:

    <Function Name="IsNull" BuiltIn="true" IsComposable="true" ReturnType="binary">
      <Parameter Name="expr1" Type="binary" />
      <Parameter Name="expr2" Type="float" />
    </Function>
    

    请注意,如msdn链接中所述:

    如果您使用Update Model向导更新您的模型,则将在下面的过程中建议对.EDMX文件的SSDL部分进行更改。

    因此,请确保将其保存在安全的地方,并在从数据库更新edmx时重新包含它。

    然后在某个静态类中添加一个方法,并用 DbFunction 属性:

    public static class CustomDbFunctions
    {
        const string Namespace = "EFTest.MyDbContextModel.Store";
    
        [DbFunction(Namespace, "IsNull")]
        public static byte[] IsNull(byte[] expr1, double expr2) => throw new NotSupportedException();
    }
    

    (更新 Namespace 字符串以匹配 命名空间 的属性 <edmx:StorageModels><Schema> 元素)。

    仅此而已。现在,您应该能够在linq to entities查询中使用上述函数:

    class MyObject { public byte[] Result { get; set;} }
    
    context.MyTable.Select(e => new MyObject
    { 
        Result = CustomDbFunctions.IsNull(e.myBinary, e.myFloat)
    });
    

    ef6很乐意将其转换为所需的sql 无空 功能。

        2
  •  2
  •   DavidG    6 年前

    你试图让它工作的方式的问题是,你需要借助一些讨厌的黑客来确定你的回报值是否是 byte[] 或A float 。这就是为什么你一直试图让ef查询返回 object 。也许可以通过破解 浮动 变成一个 字节[] ,但我建议使用一种更简单、更符合逻辑的方法:返回两个值并让应用程序决定要做什么。例如,返回如下对象:

    public class FloatOrByte // Do not call it this!
    {
        public byte[] MyBinary { get; set; }
        public float MyFloat { get; set; }
    }
    

    然后按如下方式返回:

    var result = context.table.Select(s => new FloatOrByte
    { 
        MyBinary = s.myBinary,
        MyFloat = s.myFloat
    };
    

    现在可以检查空值:

    if(result.MyBinary != null)
    {
        // Do stuff with the byte value
    }
    else
    {
        // Do stuff with the float value
    }
    
        3
  •  0
  •   koshy    6 年前

    请更改类定义以接受空值。

    public class FloatOrByte 
    {
        public byte[] MyBinary { get; set; }
        public float? MyFloat { get; set; }
    }
    

    然后使用dbcontext类获取值。问题是ef无法将空合并运算符转换为sql。

    var result = context.table.Select(s => s);
    
    var processResult = result.Tolist().Select(s=> new MyObject{
    
        Result = (s.MyBinary == null) ? (object)s.MyFloat : (object)s.MyBinary 
    
     });