在开发过程中,经常需要与数据库进行交互,而LINQ to SQL 提供了一种优雅的方式来处理这些交互。以下是一些在实际开发中发现的提高代码速度和效率的技巧。
LINQ to SQL允许指定一个属性为延迟加载,这意味着它通常不会作为对实体的常规查询操作的一部分被检索。这对于二进制和大型文本字段特别有用,例如员工对象上的一个照片属性,这个属性很少使用,并且会在客户端消耗大量内存,更不用说SQL和应用程序之间的流量了。
然而,有时可能希望在一次查询中返回所有这些二进制数据,例如返回公司照片内网页面的所有照片。以下是如何实现的示例:
var db = new NorthwindContext();
var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Employee>(e => e.Photo);
db.LoadOptions = loadOptions;
通过这种方式,可以在需要时加载大型属性,而不是在每次查询时都加载它们,从而节省内存和网络流量。
有时,能够监听这些事件的发生并执行自己的逻辑是有用的,例如在某些场景下进行审计或记录。实现这一点的最简单方法是在数据上下文中实现一些特别命名的方法,执行操作,然后使用ExecuteDynamic方法将调用返回给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);
}
}
通过这种方式,可以在创建、更新或删除实体时执行自定义逻辑,例如记录操作或审计更改。
有时,LINQ to SQL无法生成想要的TSQL,因为它不支持该功能,或者因为它对优化查询有不同的想法。在任何一种情况下,Translate方法都允许向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());
通过这种方式,可以完全控制生成的TSQL,以满足特定的查询需求。
当使用存储过程时,LINQ to SQL设计器和SQLMetal工具需要一种方法来确定返回类型。为了在不实际运行存储过程本身的情况下做到这一点,它们使用SET FMTONLY命令集将其设置为ON,以便SQL Server只解析存储过程。
不幸的是,这种解析不适用于动态SQL或临时表,因此必须手动将返回类型从标量整数更改为已知的实体类型之一。可以使用以下命令开始,让它运行,尽管随后会有警告。
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);