我有一个表,其中一列为二进制,另一列为浮点。我需要使用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行。
答案 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
});