LINQ to SQL - Return Scalar Value from a Stored Procedure

One of the best things about LINQ to SQL is that is really does have solid support for stored procedures.  However, it's not quite as friendly as dynamic LINQ to SQL with scalar values because you can't take advantage of anonymous types.  Instead you must return an actual known type.  Furthermore, this can't be a primitive so you can't return string or bool directly.  You also can't use a class that doesn't have a default (parameterless) constructor so that also rules out nullable types like Nullable<bool>.

Consider a trival stored procedure that looks like this:

CREATE PROCEDURE [dbo].[GetFirstName]
    @ID int
AS
BEGIN
    SELECT FirstName
    FROM   Contacts
    WHERE  ContactID = @id
END

We just want to return a single scalar string here. So we have to create a dummy class that looks like this:

public class DummyClass
{
    public string FirstName { get; set; }
}

Then our DataContext method can look like this:

[Function(Name = "dbo.GetFirstName")]
public ISingleResult<DummyClass> GetFirstName(int id)
{
    IExecuteResult result = this.ExecuteMethodCall(this, (MethodInfo)MethodInfo.GetCurrentMethod(), id);
    return (ISingleResult<DummyClass>)result.ReturnValue;
}

And add to that, we'll need an extra consuming method that defensively checks for an empty resultset:

public static string GetFirstName(int id)
{
    using (MyDataContext dataContext = new MyDataContext())
    {
        var result = dataContext.GetFirstName(id).FirstOrDefault();
        if (result != null)
        {
            return result.FirstName;
        }
        else
        {
            return null;
        }
    }
}

So all this works just fine but it seems like a lot of work to go through just to get a simple scalar value from a stored procedure.  Not to mention that I don't want to have to go through this every single time I need a scalar value from a stored procedure in my app.  Visual Studio will essentially generate the DummyClass for me but what if I'm hand-crafting my LINQ to SQL and am not currently using Visual Studio code generation?  I don't want to have to go through that every time.  So what can we do to make this slightly more developer-friendly?

First off, I want to avoid code duplication if at all possible.  Not only if I return multiple scalars at different places in my app but also I'd prefer to avoid the duplication if these scalars are different data types (e.g., sometimes strings, sometimes Boolean, etc.).  What we can do is to create a simple generic class called Scalar that looks like this:

public class Scalar<T>
{
    public T Value { get; set; }
}

This means I can re-write my DataContext method like this:

[Function(Name = "dbo.GetFirstName")]
public ISingleResult<Scalar<string>> GetFirstName(int id)
{
    IExecuteResult result = this.ExecuteMethodCall(this, (MethodInfo)MethodInfo.GetCurrentMethod(), id);
    return (ISingleResult<Scalar<string>>)result.ReturnValue;
}

So since I'm using generics here, I never have to write another dummy class again - regardless of whether I've got a string or Boolean or whatever.  I can always just re-use Scalar.

Additionally, I had to write a consuming method that checked to see if DummyClass was null and if not return its value; otherwise return null.  Now that I know I'm always going to use the Scalar class for all my scalar queries, I can move this into a re-usable extension method like this:

public static T GetScalar<T>(this Scalar<T> scalar)
{
    if (scalar != null)
    {
        return scalar.Value;
    }
    else
    {
        return default(T);
    }
}

So basically, all I've had to do is create that trivial Scalar class and that 1 extension method which consisted of 1 IF statement and now anytime I need a scalar value from a stored procedure in my app, it becomes as trivial as writing my data context method with the re-usable Scalar and this:

public static string GetFirstName(int id)
{
    using (MyDataContext dataContext = new MyDataContext())
    {
        return dataContext.GetFirstName(id).FirstOrDefault().GetScalar();
    }
}

Now, the one catch - the one thing to notice here is that the property name in Scalar is "Value".  That means that I'd have to change my stored procedure to alias my scalar data like this:

SELECT FirstName as Value

Although this is a downside, I believe it's well worth it in order to realize all the other benefits of quick implementation and avoiding duplicate and unnecessary code.

Tweet Post Share Update Email RSS