C# Oracle CHAR数据类型不适用于实体框架

C# Oracle CHAR数据类型不适用于实体框架,c#,.net,oracle,entity-framework,C#,.net,Oracle,Entity Framework,我在使用WHERE子句从Oracle数据库返回数据时遇到了一个问题,该子句的目标是CHAR列 我提供了以下步骤,这些步骤应允许重新创建问题: 数据库设置 运行以下SQL以创建数据库表并插入模拟数据: 创建表格订单号CHAR10字节; 在订单中插入值“123456”; 从订单中选择替换订单号“” 订单号123456 如您所见,存储在表中的值用空格填充到10个字符,并替换为,以使它们变得明显 实体框架设置 所以,问题是当我想使用变量orderFilter时,语句不会返回记录。我可以通过如下方式填充o

我在使用WHERE子句从Oracle数据库返回数据时遇到了一个问题,该子句的目标是CHAR列

我提供了以下步骤,这些步骤应允许重新创建问题:

数据库设置

运行以下SQL以创建数据库表并插入模拟数据:

创建表格订单号CHAR10字节; 在订单中插入值“123456”; 从订单中选择替换订单号“”

订单号123456

如您所见,存储在表中的值用空格填充到10个字符,并替换为,以使它们变得明显

实体框架设置

所以,问题是当我想使用变量orderFilter时,语句不会返回记录。我可以通过如下方式填充orderFilter来实现这一点,但我认为我不应该这样做:

var orderFilter = "123456".PadRight(10);
var order2 = context.Orders.FirstOrDefault(o => o.WorksOrder == orderFilter);

// This now works
Debug.WriteLine("OrderNumber:" + order2.WorksOrder);
这似乎是因为使用绑定变量时生成的SQL没有在绑定变量上设置正确的数据类型。如果启用跟踪,我们将看到以下SQL:

2015年4月22日12:15:12+01:00时打开的连接

从订单中选择Extent1.ORDER\u NUMBER作为订单号 Extent1,其中Extent1.ORDER\u NUMBER=:p\u linq\u 0和ROWNUM
我可以解释发生了什么事。解决方案是更改CHAR列,我注意到您说过,在与它进行比较时,您无法意识到其中的微妙之处

Tom Kyte在他的优秀著作《专家Oracle数据库体系结构》(Expert Oracle Database Architecture)中关于CHAR数据类型的部分就是这个答案的来源。以下内容基于第二版第499-502页

简短回答

字符文字会得到提升,而可变长度绑定变量则不会

解释

以下所有内容都是使用SQLPlus在12.1.0.2数据库上运行的,但没有显示12c独有的内容

使用CHAR和VARCHAR2列创建并填充表格:

CREATE TABLE ORDERS (ORDER_NUMBER_CHAR       CHAR(10 BYTE),
                     ORDER_NUMBER_VARCHAR2   VARCHAR2(10 BYTE));

INSERT INTO ORDERS(ORDER_NUMBER_CHAR, 
                   ORDER_NUMBER_VARCHAR2)
VALUES ('123456',
        '123456');
在使用WHERE子句中的VARCHAR2列的第一个查询中,它按预期工作,并返回一行

SELECT *
  FROM orders
 WHERE order_number_varchar2 = '123456';
在下一个查询中,WHERE子句使用CHAR列。此查询返回一行,这意味着发生了隐式转换,其中CHAR6文本被提升为CHAR10

SELECT *
  FROM orders
 WHERE order_number_char = '123456';
由于字符串的类型不同,因此必须发生隐式提升 此查询显示的长度,不返回任何行

SELECT *
  FROM orders
 WHERE order_number_char = order_number_varchar2;
下一个示例显示,VARCHAR2绑定变量的升级方式与字符文本的升级方式不同,因此此查询不返回任何行

variable vc2 VARCHAR2(10 BYTE);
exec :vc2 := '123456'; 

SELECT *
  FROM orders
 WHERE order_number_char = :vc2;
如果使用了正确的CHAR bind变量,那么将找到该记录,最终查询将按预期返回该行

variable the_char CHAR(10 BYTE);
exec :the_char := '123456'; 

SELECT *
  FROM orders
 WHERE order_number_char = :the_char;
您还应该知道,如果CHAR列的大小发生变化,应用程序将受到影响,即如果它增加到20,则需要相应地更改PadRight方法

最后,我认为引用Tom对CHAR数据类型的总结也是值得的

正是由于这些原因,固定宽度的存储往往会 表和相关索引比正常值大得多,再加上 bind变量的问题是我完全避免使用CHAR类型 情况。我甚至不能为这件事辩护 一个字符的字段,因为在这种情况下,它实际上没有 物质差异。VARCHAR21和CHAR1在所有方面都是相同的 方面。没有令人信服的理由在这种情况下使用CHAR类型 为了避免任何混乱,我“只是说不”,即使是对于 CHAR1字段


尝试与实体字段上的Trim进行比较……

问题在于EF6将参数作为varchar2类型传递给oracle。 1.使用

安装程序包EntityFramework.Functions-版本1.4.1

将函数RightPadstring str、int length、string padChar映射添加到Oracle RPAD函数

设置实体属性的MaxLength 编写如下代码,您可以获得数据,并且数据库索引正在工作

使用StartWith而不是=符号。因为在发布程序之前扩展db上的列长度时,StartWith可以使旧程序正常工作
您仍然需要更改Entity Property的MaxLength并在以后发布

我最近并没有真正使用该数据类型,但我曾经使用过,这是修剪或填充的预期行为。我同意没有名字的@a_horse_,请看这是预期的行为。感谢Patrick的回复。如果是预期的,为什么文字字符串和使用变量之间不一致。FirstOrDefaulto=>o.OrderNumber==123456;vs.FirstOrDefaulto=>o.OrderNumber==orderFilter;如果有一种方法可以在上下文中将variable.PadRightFixedLengthFieldMeta.Length全局应用于任何固定长度的字符列,而不必记住每个查询都需要填充参数,那就太好了。谢谢Ian。我同意CHAR数据类型令人敬畏。我一直在使用遗留数据库&我们的DBA似乎喜欢CHARS:我想问的问题是
那么,ppose是如何让实体框架传递正确的绑定变量类型,以便进行升级的呢?我假设EntityTypeConfiguration在某个地方有一个错误,阻止了正确的绑定变量类型的使用。谢谢你的回答。是时候将我所有的字符切换到VARCHARS了。有同样的问题。retval=DB.TBL_PRIVATE_CONTACTS.where=>A.ContactInfo.Trim==HHH.ToList;在本例中,ContactInfo是oracle数据库中的CHAR6列。
variable vc2 VARCHAR2(10 BYTE);
exec :vc2 := '123456'; 

SELECT *
  FROM orders
 WHERE order_number_char = :vc2;
variable the_char CHAR(10 BYTE);
exec :the_char := '123456'; 

SELECT *
  FROM orders
 WHERE order_number_char = :the_char;
int length = GetColumnMaxLength(dbContext, "Vessel","VesselName");
dbContext.Set<Vessel>().Where(m=>m.VesselName.StartWith(RPad(vesselName, length,""))