以下各节解答了您在实现 LINQ 时可能遇到的一些常见问题。
其他问题在疑难解答 (LINQ to SQL) 中进行了解答。
问:我无法连接到数据库。
答:请确保您的连接字符串是正确的,以及您的 SQL Server 实例正在运行。另请注意,LINQ to SQL 要求启用命名管道协议。有关更多信息,请参见通过演练学习 (LINQ to SQL)。
问:我的数据库连接可以保持打开状态多长时间?
答:一个连接通常会一直保持打开状态,直至您使用完查询结果为止。如果您需要花时间处理所有结果并且愿意对结果进行缓存,请将 ToList<(Of <(TSource>)>) 应用于查询。在每个对象仅处理一次的常见方案中,流模型比 DataReader 和 LINQ to SQL 都更为适合。
连接使用的准确详细信息与以下内容有关:
-
使用连接对象构造 DataContext 时的连接状态。
-
连接字符串设置(例如,启用多活动结果集 (MARS))。有关更多信息,请参见多活动结果集 (MARS)。
问:是否可以在不先查询数据库的情况下更新表数据?
答:虽然 LINQ to SQL 没有基于集的更新命令,但是可以使用以下方法之一进行更新而无需先进行查询:
-
使用 ExecuteCommand 发送 SQL 代码。
-
创建对象的新实例,并初始化所有影响更新的当前值(字段)。然后使用 Attach 将对象附加到 DataContext 并修改您要更改的字段。
问:我的查询返回了意外的结果。如何检查所发生的情况?
答:LINQ to SQL 提供了几种工具用于检查其生成的 SQL 代码。其中最重要的工具就是 Log。有关更多信息,请参见调试支持 (LINQ to SQL)。
问:我有一个存储过程,其返回值由 MAX() 进行计算。在将该存储过程拖动到 O/R 设计器图面时,返回值不正确。
答:LINQ to SQL 提供了两种方法用于通过存储过程返回数据库生成的值:
-
通过命名输出结果。
-
通过显式指定输出参数。
下面是错误输出的示例。因为 LINQ to SQL 无法映射结果,所以始终返回 0:
create procedure proc2
as
begin
select max(i) from t where name like 'hello'
end
下面是使用输出参数获得正确输出的示例:
create procedure proc2
@result int OUTPUT
as
select @result = MAX(i) from t where name like 'hello'
go
下面是通过命名输出结果获得正确输出的示例:
create procedure proc2
as
begin
select nax(i) AS MaxResult from t where name like 'hello'
end
有关更多信息,请参见使用存储过程来自定义操作 (LINQ to SQL)。
问:在我试图进行序列化时收到下面的错误消息:“...类型‘System.Data.Linq.ChangeTracker+StandardChangeTracker’未标记为可序列化。”
答:LINQ to SQL 中的代码生成支持 DataContractSerializer 序列化,而不支持 XmlObjectSerializer 或 BinaryFormatter。有关更多信息,请参见序列化 (LINQ to SQL)。
问:我的一个数据库表具有一个默认为 SQL Getdate() 的 DateCreated 列。在我试图使用 LINQ to SQL 插入新记录时,该值会设置为 NULL。我希望其设置为数据库默认值。
答:LINQ to SQL 会自动为标识(自动增加)和 rowguidcol(数据库生成的 GUID)以及时间戳列处理这种情况。在其他情况下,您应手动设置 IsDbGenerated=true 和 AutoSync=Always/OnInsert/OnUpdate 属性。
问:是否可以指定其他加载选项而不覆盖原先的选项?
答:可以。不会覆盖原先的选项,如下面的示例中所示:
Dim dlo As New DataLoadOptions() dlo.LoadWith(Of Order)(Function(o As Order) o.Customer) dlo.LoadWith(Of Order)(Function(o As Order) o.OrderDetails)
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Order>(o => o.Customer);
dlo.LoadWith<Order>(o => o.OrderDetails);
问:LINQ to SQL 如何防范 SQL 注入式攻击?
答:对于通过串联用户输入而组成的传统 SQL 查询,SQL 注入已成为重大风险。LINQ to SQL 通过在查询中使用 SqlParameter 来避免这种注入。用户输入会转换为参数值。此方法可防止通过客户输入使用恶意命令。
问:如何在从 DBML 文件创建对象模型时消除某些属性中的 setter?
答:对于此高级方案,请执行以下步骤:
-
在 .dbml 文件中,通过将 IsReadOnly 标志更改为 True 来修改属性。
-
添加一个分部类。为只读成员创建一个带参数的构造函数。
-
检查默认的 UpdateCheck 值 (Never) 以确定该值对于您的应用程序是否正确。
警告: 如果使用 Visual Studio 中的对象关系设计器,则可能会覆盖您的更改。
问:System.Data.Linq 是否标记为供部分受信任的代码使用?
答:是,System.Data.Linq.dll 程序集包括在那些使用 AllowPartiallyTrustedCallersAttribute 属性标记的 .NET Framework 程序集之中。如果没有此标记,则 .NET Framework 中的程序集将仅供完全受信任的代码使用。
在 LINQ to SQL 中,允许部分受信任的调用方使用的主要方案是允许从 Web 应用程序(其中的 trust 配置为 Medium)访问 LINQ to SQL 程序集。
有关更多信息,请参见ASP.NET 代码访问安全性。
问:我的实体中的数据来自多个表。如果映射这些数据?
答:您可以在数据库中创建一个视图并将实体映射到该视图。LINQ to SQL 会为视图生成 SQL,与它为表生成 SQL 相同。
说明: |
---|
这种情况下的视图用法有一些限制。当基础视图支持在 Table<(Of <(TEntity>)>) 上执行的操作时,此方法最为安全。只有您知道要执行的操作。例如,大多数应用程序是只读的,还有相当多的应用程序仅通过对视图使用存储过程来执行 Create/Update/Delete 操作。 |
问:是否具有对 DataContext 池有所帮助的构造?
答:请不要试图重用 DataContext 的实例。每个 DataContext 都会保持对应一个特定编辑/查询会话的状态(包括标识缓存)。若要获取基于数据库当前状态的新实例,请使用新的 DataContext。
仍然可以使用基础 ADO.NET 连接池。有关更多信息,请参见 SQL Server 连接池 (ADO.NET)。
问:我使用 DataContext 的一个实例存储数据库中的值。但是,相同数据库上的另一个 DataContext 未反映更新的值。第二个 DataContext 实例似乎返回缓存的值。
答:此行为是有意安排的。LINQ to SQL 会继续返回您在第一个实例中看到的相同实例/值。在进行更新时使用开放式并发。原始数据用于检查当前数据库状态,以确定该数据实际上仍未更改。如果该数据已更改,则会发生冲突,您的应用程序必须解决该冲突。您的应用程序可以选择将原始状态重置为当前数据库状态并尝试再次更新。有关更多信息,请参见如何:管理更改冲突 (LINQ to SQL)。
您也可以将 ObjectTrackingEnabled 设置为 false,这样可以关闭缓存和更改跟踪。然后便可以在每次查询时检索最新值。
下面的信息揭示您在 LINQ to SQL 应用程序中可能遇到的一些问题,并提供建议以避免这些问题或减少这些问题的影响。
其他问题在常见问题 (LINQ to SQL) 中进行了解答。
LINQ to SQL 不支持某些标准查询运算符方法(例如,ElementAt<(Of <(TSource>)>))。因此,编译后的项目仍然可能产生运行时错误。有关更多信息,请参见标准查询运算符转换 (LINQ to SQL)。
如果查询涉及到内存中的集合以及 LINQ to SQL Table<(Of <(TEntity>)>),则该查询可能在内存中执行,具体取决于指定这两个集合的顺序。如果该查询必须在内存中执行,则需要检索数据库表中的数据。
此方法的效率十分低下,并可能占用大量内存和处理器时间。请尽量避免这种多域查询。
若要指定一个输入文件名,请将该名称作为输入文件添加到命令行。不支持在连接字符串中包含文件名(使用 /conn 选项)。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。
对象关系设计器会在项目的 app.config 文件中创建一个连接字符串。在类库项目中,不使用 app.config 文件。LINQ to SQL 使用在设计时文件中提供的连接字符串。更改 app.config 中的值不会更改应用程序连接到的数据库。
LINQ to SQL 不支持或识别级联删除操作。如果要在表中删除一个具有约束的行,必须执行以下操作之一:
-
在数据库的外键约束中设置 ON DELETE CASCADE 规则。
-
使用您自己的代码先删除阻止删除父对象的子对象。
否则,将引发 SqlException 异常。
有关更多信息,请参见如何:从数据库中删除行 (LINQ to SQL)。
如果收到错误消息“表达式 [表达式] 不可查询;是否缺少程序集引用?”,请确保满足以下要求:
-
应用程序面向的是 .NET Compact Framework 3.5。
-
您具有对 System.Core.dll 和 System.Data.Linq.dll 的引用。
-
您具有用于 System.Linq 和 System.Data.Linq 的 Imports (Visual Basic) 或 using (C#) 指令。
在调试 LINQ to SQL 项目的过程中,可能会遍历某个实体的关系。这样做会使这些项进入缓存,而 LINQ to SQL 会检测到这些项的存在。如果随后试图执行 Attach 或 InsertOnSubmit,或者执行一个生成具有相同键的多个行的类似方法,则会引发 DuplicateKeyException。
不支持映射到 [n]text 和其他 [n][var]char 的操作数的串联。映射到两个不同类型集的字符串的串联会引发异常。有关更多信息,请参见 System.String 方法 (LINQ to SQL)。
在对 SQL Server 2000 数据库使用 Take<(Of <(TSource>)>) 或 Skip<(Of <(TSource>)>) 时,必须使用标识成员 (IsPrimaryKey)。查询必须针对单个表(即,不是联接),或必须为 Distinct、Except、Intersect 或 Union 操作,且不得包含 Concat<(Of <(TSource>)>) 操作。有关更多信息,请参见标准查询运算符转换 (LINQ to SQL) 中的“SQL Server 2000 支持”部分。
此要求不适用于 SQL Server 2005。
如果在按 boolean 表达式进行分组的 GroupBy 查询(如 group x by (Phone==@phone))中有一个列值为 null,则会引发此异常。因为表达式的类型为 boolean,所以会将键的类型推理为 boolean 而不是 nullable boolean。在转换后的比较生成 null 值时,系统会试图将一个 nullable boolean 值赋给一个 boolean,从而引发该异常。
若要避免发生这种情况(假定您希望将 null 视为 false),请使用如下方式:
GroupBy="(Phone != null) && (Phone=@Phone)"
每次调用对象构造函数时都会调用生成的方法 OnCreated(),这包括以下这种情况,即 LINQ to SQL 调用构造函数以生成原始值的副本。如果您在自己的分部类中实现 OnCreated() 方法,请考虑此行为。