C# 首先将实体框架代码与List.Contains方法一起使用时出现无法解释的行为

C# 首先将实体框架代码与List.Contains方法一起使用时出现无法解释的行为,c#,sql-server,entity-framework,ef-code-first,C#,Sql Server,Entity Framework,Ef Code First,问题 我们在一些查询中遇到超时错误,在调查和测试之后,我们将问题缩小到由实体框架代码首先创建的动态Sql查询 背景 我们使用的是实体框架v4.3.1,代码优先模型 用VS 2010 Premium编写的代码 我们在数据库中有一个表,定义如下,ID为PK CREATE TABLE [dbo].[Reference]( [DocumentID] [varchar](100) NOT NULL, [DetailID] [varchar](50) NULL, [Transacti

问题

我们在一些查询中遇到超时错误,在调查和测试之后,我们将问题缩小到由实体框架代码首先创建的动态Sql查询

背景

我们使用的是实体框架v4.3.1,代码优先模型 用VS 2010 Premium编写的代码

我们在数据库中有一个表,定义如下,ID为PK

CREATE TABLE [dbo].[Reference](
    [DocumentID] [varchar](100) NOT NULL,
    [DetailID] [varchar](50) NULL,
    [TransactionSet] [char](5) NULL,
    [Qualifier] [varchar](80) NULL,
    [ReferenceID] [nvarchar](30) NULL,
    [Description] [nvarchar](80) NULL,
    [ID] [int] IDENTITY(1,1) NOT NULL
我们有一个实体类,定义如下,与表相对应:

public class Reference
{
    public string DocumentID { get; set; }
    public string DetailID { get; set; }
    public string TransactionSet { get; set; }
    public string Qualifier { get; set; }
    public string ReferenceID { get; set; }
    public string Description { get; set; }
    public int ID { get; set; }
}
为了简洁起见,我们将数据上下文定义如下:

public class DataContext : DbContext
{
    public DataContext() : base("Name=DataContext")
    {
        this.Configuration.AutoDetectChangesEnabled = false;
        Database.SetInitializer<DataContext>(null);
    }

    public DbSet<Reference> References { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        modelBuilder.Entity<Reference>().HasKey(pk => pk.ID);
    }
}
请注意,由于代码使用列表的Contains方法,因此EF将其转换为sql查询中的IN子句。in子句中的每个字符串都以表示unicode字符串的“N”作为前缀。由于表中的DocumentID列是VARCHAR而不是NVARCHAR,因此Sql必须进行隐式转换才能执行查询。经过调查和测试,我们发现在这种情况下,它不会选择相关的索引,这会导致表扫描,并且通常会出现超时异常

我们发现,通过从查询中删除N前缀,使用了正确的索引,查询的运行速度提高了几个数量级

因此,为了使EF不在字符串前面加N,我在OnModelCreating方法的数据上下文类中添加了以下两行:

modelBuilder.Entity<Reference>().Property(r => r.DocumentID).HasColumnType("VARCHAR");
modelBuilder.Entity<Reference>().Property(r => r.Qualifier).HasColumnType("VARCHAR");
我以为我的问题已经解决了,但是当我将这段代码部署到测试环境中时,N个前缀没有被删除,我也不知道为什么

我可以通过手动创建Sql查询来解决这个问题,但是我在这个程序中还有一些查询需要进行类似的更改,如果我只需要更改OnModelCreating方法,而不是手动创建每个查询,那么就更容易了

有没有人想过为什么会发生这种情况

顺便说一句,我的本地计算机使用Sql Server 2008,而测试环境和生产环境使用Sql 2005。我不确定这是否会影响EF生成的查询,因为我的印象是查询是在提交到数据库之前生成的

更新

如果我针对测试数据库在我的盒子上执行代码,它将正常工作。如果代码在测试盒上执行,即使它们都在访问同一个数据库,也不会执行

更新2

代码是否成功运行取决于执行它的位置

EXE Location  |  EXE Executed From  |  Database Server (Version)  |  Result
--------------------------------------------------------------------------
My Box        |  My Box             |  My Box (2008)              |  Success
Test Server   |  My Box             |  Test DB Server (2005)      |  Success
Test Server   |  Test Server        |  Test DB Server (2005)      |  Fail

根据上面的结果,在我的本地机器上与测试机器上有所不同似乎是合乎逻辑的。但我不知道怎么诊断。我能想到的唯一区别是,我的机器安装了.Net 4.5,而测试服务器没有安装。

我已经能够通过以下方式纠正这种行为:


从EF6开始,您可以在整个DbContext范围内使用

谢谢,我将尝试使用注释。我认为如果我在OnModelCreating方法中这样做,我不需要注释。不幸的是,这只取得了部分成功。带有Qualifier列的查询部分不包含N前缀,但IN子句中的值仍然具有N前缀。我的本地计算机、测试环境和生产环境上的SQL server版本不同这一事实是否会产生任何影响?对此表示抱歉。我将使用EF4.3.1设置一个环境,并找出我遗漏了什么。如果我针对测试数据库在我的框中执行代码,它将正常工作。如果代码在测试盒上执行,即使它们都访问同一个数据库,也不会执行。我无法复制此行为。我相信你对环境差异的怀疑起了一定作用。不幸的是,我手头没有SqlSever2005的实例。我会继续寻找并报告任何发现。
modelBuilder.Entity<Reference>().Property(r => r.DocumentID).HasColumnType("VARCHAR");
modelBuilder.Entity<Reference>().Property(r => r.Qualifier).HasColumnType("VARCHAR");
SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[DocumentID] AS [DocumentID], 
    [Extent1].[DetailID] AS [DetailID], 
    [Extent1].[TransactionSet] AS [TransactionSet], 
    [Extent1].[Qualifier] AS [Qualifier], 
    [Extent1].[ReferenceID] AS [ReferenceID], 
    [Extent1].[Description] AS [Description]
FROM [dbo].[Reference] AS [Extent1]
WHERE ([Extent1].[DocumentID] IN ('A2014011300028343869701A020','A2014011300028343869701A021')) AND ('AFN' = [Extent1].[Qualifier])
EXE Location  |  EXE Executed From  |  Database Server (Version)  |  Result
--------------------------------------------------------------------------
My Box        |  My Box             |  My Box (2008)              |  Success
Test Server   |  My Box             |  Test DB Server (2005)      |  Success
Test Server   |  Test Server        |  Test DB Server (2005)      |  Fail
public class Reference
{
    [Column(TypeName = "varchar"), MaxLength(100)]
    public string DocumentID { get; set; }
    [Column(TypeName = "varchar"), MaxLength(50)]
    public string DetailID { get; set; }
    [Column(TypeName = "char"), MaxLength(5)]
    public string TransactionSet { get; set; }
    [Column(TypeName = "varchar"), MaxLength(80)]
    public string Qualifier { get; set; }
    [MaxLength(30)]
    public string ReferenceID { get; set; }
    [MaxLength(80)]
    public string Description { get; set; }
    public int ID { get; set; }
}