LINQ to SQL tips and tricks #2

A few more useful and lesser-known tips for using LINQ to SQL.

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 of what makes an optimal query.

In either case, the Translate method allows you to deliver your own TSQL to LINQ to SQL, to execute, with 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 to figure out what the return type is. To do this without actually running the stored procedure, they use the SET FMTONLY command so that SQL Server parses the stored procedure but does not execute it.

Unfortunately, this parsing does not extend to dynamic 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 of the subsequent warning.

SET FMTONLY OFF

If your stored procedure can not safely handle calls 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.

If you use discriminator subclassing, you need to either ensure your type is cast to its concrete type or use my L2ST4 templates for now as .NET 3.5 SP1 doesn’t emit the necessary KnownType attributes to make this automatically happen (fixed in .NET 4.0). Add a simple method to serialize in-memory like this:

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:

var source = myQuery.First();
var cloned = Clone(source);

Be aware that this comes with a little overhead in the serialization and de-serialization process.

If this is a problem for you, why not grab the templates and make your entities implement ICloneable!

Check out part 3 of LINQ to SQL tips

[)amien

4 responses

  1. Avatar for Steve

    I honestly read the first line as "when LINQ to SQL doesn't want to cock up the TSQL" and thought wow, that's a divergence from the new professional style :) I guess that says more about me as a reader...

    Steve 13 April 2009
  2. Avatar for Brent Miller

    Brad Abrams (and the .NET team) recommends people don't implement ICloneable: http://blogs.msdn.com/brada/archive/2003/04/09/49935.aspx

    Brent Miller 13 April 2009
  3. Avatar for Damien Guard

    @Brent that guidance was specifically issued because it is not clear from the name whether copies are deep or shallow and that people consuming your framework wouldn't know which the object has chosen.

    In your own internal classes this is not so much of a problem but yes, if you like, you can create your own Clone/Copy methods without using ICloneable instead.

    Damien Guard 13 April 2009
  4. Avatar for BGood

    Damian,

    Thanks for posting your Linq entity cloning code. I hope you don't mind, but (with proper attribution) I translated to VB and referenced it in a thread on MSDN:

    https://social.msdn.microsoft.com/Forums/en-US/linqtosql/thread/07452e17-9d7c-40ee-beef-d197acd0c59a?prof=required

    -BGood

    BGood 15 June 2010