LINQ to SQL 技巧和窍门
作为 LINQ 开发者,我发现的几个技巧和窍门。
有很多小技巧可以使LINQ to SQL更易于使用,或者提高生成的代码的速度和效率。以下是我作为LINQ开发人员期间发现的一些技巧和窍门。
注意:这些技巧中的一些是针对直接从表中查询的。虽然我强烈提倡始终使用存储过程,但在某些情况下,这可能不可行或不可取。
加载延迟加载的属性
LINQ to SQL允许您指定属性是延迟加载的,这意味着它通常不会作为针对该实体的常规查询操作的一部分而被检索。这对于二进制和大型文本字段特别有用,例如employee
对象上的照片属性,该属性很少使用,并且会导致客户端消耗大量的内存,更不用说SQL和应用程序之间的流量了。
但是,有时您希望在一个查询中返回所有这些二进制文件,例如,为公司照片内网页面返回所有照片。
var db = new NorthwindContext();
var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Employee>(e => e.Photo);
db.LoadOptions = loadOptions;
拦截创建、更新和删除操作
有时,能够监听这些事件发生并执行自己的逻辑会很有用,例如,在某些情况下进行审计或记录。最简单的方法是在您的数据上下文中实现一些特殊命名的方法,执行您的操作,然后将调用调度回LINQ to SQL。
这些特殊命名的方法的格式是[Action][Entity]
,然后您应该使用ExecuteDynamic[Action]
将控制权返回给LINQ to SQL,其中[Action]
是Insert
、Update
或Delete
。 这样的用法示例可能是
partial class NorthwindContext {
partial void InsertEmployee(Employee instance) {
instance.CreatedBy = CurrentUser;
instance.CreatedAt = DateTime.Now;
ExecuteDynamicInsert(instance);
}
partial void UpdateEmployee(Employee instance) {
AuditEmployeeOwnerChange(instance);
instance.LastModifiedAt = DateTime.Now;
ExecuteDynamicUpdate(instance);
}
partial void DeleteEmployee(Employee instance) {
AuditDelete(instance, CurrentUser);
ExecuteDynamicDelete(instance);
}
}
完全控制TSQL
有时,LINQ to SQL拒绝生成您想要的TSQL,要么是因为它不支持该功能,要么是因为它对优化查询有不同的想法。
在任何一种情况下,Translate
方法都允许您将自己的TSQL传递给LINQ to SQL进行处理,就像它是LINQ to SQL自己的TSQL一样,同时仍然保留执行、实体化和标识映射。
var db = new PeopleContext();
if (db.Connection.State == System.Data.ConnectionState.Closed)
db.Connection.Open();
var cmd = db.GetCommand(db.Persons.Where(p => p.CountryID == 1));
cmd.CommandText = cmd.CommandText.Replace("[People] AS [t0]", "[People] AS [t0] WITH (NOLOCK)");
var results = db.Translate<Person>(cmd.ExecuteReader());
复杂的存储过程
在使用存储过程时,LINQ to SQL设计器和SQLMetal
工具需要一种方法来确定返回类型。为了做到这一点,而无需实际运行存储过程,它们使用SET FMTONLY
命令设置为ON
,以便SQL Server仅解析存储过程。
不幸的是,此解析不扩展到tdynamic SQL或临时表,因此您必须手动将返回类型从标量整数更改为已知的实体类型之一。 您可以使用以下命令在开始时让它运行,即使随后出现警告。
SET FMTONLY OFF
如果您的存储过程无法安全地处理随时使用null
参数调用,请手动设置返回类型。
克隆实体
您可能有很多理由想要克隆实体——您可能想要创建许多类似的实体,您可能想要比它来自的DataContext
更长时间地保留它——无论您的原因如何,实现Clone
方法可能很痛苦,但利用DataContractSerializer
可以轻松完成这项工作,前提是您的DBML设置为启用序列化。
public static T Clone<T>(T source) {
var dcs = new System.Runtime.Serialization.DataContractSerializer(typeof(T));
using (var ms = new System.IO.MemoryStream()) {
dcs.WriteObject(ms, source);
ms.Seek(0, System.IO.SeekOrigin.Begin);
return (T)dcs.ReadObject(ms);
}
}
然后克隆只需
var source = myQuery.First();var cloned = Clone(source);
请注意,这在序列化和反序列化过程中会带来一些开销。