读取byte []或使用Linq到Entry浮动

时间:2018-06-22 10:44:46

标签: c# entity-framework linq asp.net-web-api

我有一个表,其中一列为二进制,另一列为浮点。我需要使用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 ms。但是:我希望每个查询大约能容纳5000到10000行。

3 个答案:

答案 0 :(得分:3)

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

如果您使用的是Code First模型,则需要 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链接中所述:

  

如果使用“更新模型向导”更新模型,则按照以下过程中的建议对.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字符串以匹配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 ISNULL函数。

答案 1 :(得分:2)

您尝试使其工作的方式存在的问题是,您将需要借助一些讨厌的技巧来确定您的返回值是byte[]还是float。因此,您一直试图获取EF查询以返回object。可以通过将float入侵byte[]来使其正常工作,但是我建议一种更简单,更合乎逻辑的方法:返回两个值,然后由应用决定要做什么。例如,返回类似此对象的内容:

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
}

答案 2 :(得分:0)

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

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

然后使用DBContext类获取值。问题在于EF无法将null-coalescing运算符转换为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 

 });