Published
Monday, August 27, 2007 4:33 PM
by
mtaulty
I thought I'd write a simple stored procedure and add it to Northwind;
create procedure dbo.GetCustomersByCountry
(
@country nvarchar(30)
)
as
set nocount on
select c.* from dbo.Customers as c
where c.country = @country
And then see about calling it using ObjectQuery<T>.
I added this to my Northwind database and then ran edmgen.exe against it but, as far as I can tell, the only reference to my stored procedure from running this was in my SSDL file. That is, it didn't seem to get automatically mapped for me into my EDM.
I tried the same thing using the designer but I didn't seem to be able to find a way of using the tool to produce the mapping of the function that's present in the storage model (i.e. SSDL);
<Function Name="GetCustomersByCountry" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="country" Type="nvarchar" Mode="in" />
</Function>
to appear anywhere else (e.g. in the CSDL :-)).
So, I set about trying to hack it in the XML editor. Here's the SSDL file;
<Function Name="GetCustomersByCountry" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="country" Type="nvarchar" Mode="in" />
</Function>
Here's the CSDL file;
<FunctionImport EntitySet="Customers" Name="GetCustomersByCountry" ReturnType="Collection(Northwind.Customers)">
<Parameter Name="country" Type="String" MaxLength="30" Mode="in"/>
</FunctionImport>
Here's the MSL file that glues them together;
<FunctionImportMapping FunctionImportName="GetCustomersByCountry" FunctionName="Northwind.Store.GetCustomersByCountry">
<ResultMapping>
<EntityTypeMapping TypeName="Northwind.Customers">
</EntityTypeMapping>
</ResultMapping>
</FunctionImportMapping>
With those manual changes in place, I went and regenerated the C# code from the CSDL, SSDL, MSL by using the /mode:EntityClassGeneration mode taking care not to fluff it and overwrite the changes that I'd just made to the .CSDL, .SSDL, .MSL files.
That generated code contains my NorthwindContext class which derives from ObjectContext and uses the protected (why protected?) method ExecuteFunction in order to call my Stored Procedure. It looks like this;
public global::System.Data.Objects.ObjectResult<Customers> GetCustomersByCountry(string country)
{
global::System.Data.Objects.ObjectParameter countryParameter;
if ((country != null))
{
countryParameter = new global::System.Data.Objects.ObjectParameter("country", country);
}
else
{
countryParameter = new global::System.Data.Objects.ObjectParameter("country", typeof(string));
}
return base.ExecuteFunction<Customers>("GetCustomersByCountry", countryParameter);
}
So, with that in place I can go ahead and use one of those NorthwindContexts to get this executed;
using System;
using Northwind;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
using (NorthwindContext ctx = new NorthwindContext("Name=NorthwindEntities"))
{
foreach (Customers c in ctx.GetCustomersByCountry("UK"))
{
Console.WriteLine(c.CustomerID);
}
}
}
}
}
and it's working in the sense of executing a stored procedure, getting a result-set back as an EntitySet. Cool :-)