There are many little tips and tricks for LINQ to SQL which can make our lives easier, or improve the speed and efficiency of the generated code. Below are several tips and tricks I have found during my time as a LINQ developer.
*Note: Some of these tips are for querying directly from the table. While I'm a big advocate of always using stored procedures, there are circumstances where that may not be possible or desirable.*
Loading a Delay-loaded Property
LINQ to SQL lets you specify that a property is delay-loaded meaning that it is not normally retrieved as part of normal query operations against that entity. This is particularly useful for binary and large text fields such as a photo property on an employee
object that is rarely used and would cause a large amount of memory to be consumed on the client not to mention traffic between the SQL and application.
There are times however when you want all these binaries returned in a single query, say for example returning all the photos for the company photo intranet page:
var db = new NorthwindContext();
var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Employee>(e => e.Photo);
db.LoadOptions = loadOptions;
Intercepting Create, Update and Delete operations
There are times it is useful to be able to listen in to when these events happen and perform your own logic, perhaps auditing or logging for some scenarios. The easiest way to do this is to implement some specially-named methods on your data context, perform your action and then to dispatch the call back to LINQ to SQL.
The format of these specially-named methods is [Action][Entity]
and then you should pass back control to LINQ to SQL using ExecuteDynamic[Action]
where [Action]
is either Insert
, Update
or Delete
. One example of such usage might be:
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);
}
}
Take Full Control of the TSQL
There are times when LINQ to SQL refuses to cook up the TSQL you wanted either because it doesn’t support the feature or because it has a different idea what makes an optimal query.
In either case, the Translate
method allows you to deliver your own TSQL to LINQ to SQL to process as if it were its own with execution, materialization and identity mapping still honored. For example:
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());
Complex Stored Procedures
When working with stored procedures, the LINQ to SQL designer and SQLMetal tools need a way of figuring out what the return type will be. In order to do this without actually running the stored procedure itself, they use the SET FMTONLY
command set to ON
so that SQL Server will just parse the stored procedure instead.
Unfortunately, this parsing does not extend to tdynamic SQL or temporary tables so you must change the return type from the scalar integer to one of the known entity types by hand. You could use the following command at the start to let it run regardless given the subsequent warning.
SET FMTONLY OFF
If your stored procedure cannot safely handle being called at any time with null
parameters, set the return type by hand instead.
Cloning an Entity
There are many reasons you might want to clone an entity – you may want to create many similar ones, you could want to keep it around longer than the DataContext
it came from – whatever your reason implementing a Clone
method can be a pain but taking advantage of the DataContractSerializer
can make light work of this providing your DBML is set to enable serialization.
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);
}
}
And then to clone simply:
var source = myQuery.First();
var cloned = Clone(source);
Be aware that this comes with a little overhead in the serialization and deserialization process.