Tuesday, December 16, 2008

LINQ of the Day

Today's LINQ covers a several points: Providing an object data source, suitable for binding. Making a query reusable, and pre-compiling it for best performance. Selecting useful objects.

Here's the code:

public static List<IndependentRep> IndependentRepList()
{
using (FSP dc = FSP.DC())
{
return GetIndependentRepQuery(dc).ToList();
}
}

private static Func<FSP, IQueryable<IndependentRep>>
GetIndependentRepQuery = CompiledQuery.Compile(
(FSP dc) => from u in dc.Pm_Logins
where (!u.RecordDeleted) &&
(u.LoginType == Util.c_Login_IndependentSalesRep)
select new IndependentRep
{
UserName = u.UserName,
RepContact = new Contact
{
FirstName = u.FirstName,
LastName = u.LastName,
OfficePhone = u.OfficePhone,
MobilePhone = u.MobilePhone,
Email = u.Email,
CompanyName = u.CompanyName
},
Status = u.Status
});



Providing an Object Data Source



You'll notice that there are two static functions here, a public static function that calls a private static function. The public function has no arguments, so it's ideal for binding with an object data source. It could pull parameters for session data or the application cache, if the scope of the data needed to be constrained based on context, but we're not doing that here. All we do is grab a data context and call the private function, then return the results as a list.



Reusable Pre-Compiled Queries



This might look a bit intimidating at first, but it's well worth the effort to learn and use. The CompiledQuery.Compile method provides a way to compile a LINQ query and cache the expression tree, so that it can be re-used without the overhead of re-evaluating the query each time. The query is compiled on first use, and (as far as I know) has the lifetime of the AppDomain.



The Compile method is overloaded to allow you to pass zero to three arguments to the query. We're not passing any arguments to the query itself in this case, but if we were, they would come between the first and last arguments listed in "Func". "Func" is a generic delegate template, so at this point we're specifying types. The first type is always the data context, and the last argument is always the return type of the query. By leaving the return type as IQueryable<T>, callers can always add more LINQ methods to further refine the query before it goes to the database. (And remember, we're doing the ultimate in deferred query execution here; the query never gets executed inside this function.)



CompiledQuery.Compile actually takes only one argument, and that's a lambda expression that defines the query itself. The arguments to the lambda expression are all of the types listed in "Func" except the return type. In defining the arguments for the lambda expression, we're providing both types and parameter names, and the parameter names are used inside the query. To the right of the lambda operator ("=>"), we have a normal LINQ query, with the exception that you must return a named class type that matches the return type in "Func". That class can be either an entity known to your data context, or it can be a class defined in your application.



Selecting Useful Objects



Look at the select clause in the query. Select always returns one object, in this case a new instance of type IndependentRep. IndependentRep is an application-defined class with three members:



public class IndependentRep
{
public string UserName
{ get; set; }
public Contact RepContact
{ get; set; }
public string Status
{ get; set; }
}



As we initialize the object in the select clause, we can assign values to UserName and Status directly. We can also create a new instance of a Contact class, and populate that as well with data from the query. In this case, we're generating a simple class for use in databinding, but you could also have the select clause generate a pre-populated business entity, a class with its own business rules and perhaps its own persistence and update methods.



Putting LINQ into production means being comfortable with selecting from the many features of LINQ, and combining these into coherent and useful units of work. This is a real-life example.

Friday, December 12, 2008

LINQ of the Day

Here's an interesting problem. A table has multiple foreign key references to another table. Each foreign key reference has a different meaning, so they're unique. I need a list of the items referenced, with context information so that I know the usage of the reference. If Table One references Table Two twice in the same row, that's two distinct usages, and should generate two separate records for output.

That's just enough difference to make life interesting, because the output needs information from both tables as well as a constant identifying context and usage. In T-SQL, this is a fairly simple set of four joins, combined with a union so we get all our output in one result set.

Here's the LINQ to do this:

IQueryable<MultiplierCustomer> StdCustomers =
dc.Pm_VisualCustMultipliers
.Where(vcm => vcm.RecordDeleted == false)
.Where(vc => vc.StandardMultiplier == multiplierID)
.Join(dc.Vm_Customers,
(vcm => vcm.VisualCustomerID),
(vc => vc.ID),
((vcm, vc) => new MultiplierCustomer
{
VisualCustomerID = vcm.VisualCustomerID,
CompanyName = vc.NAME,
Brand = vcm.Website,
MultiplierType = "Standard"
}));
IQueryable<MultiplierCustomer> QSCustomers =
dc.Pm_VisualCustMultipliers
.Where(vcm => vcm.RecordDeleted == false)
.Where(vc => vc.QSMultiplier == multiplierID)
.Join(dc.Vm_Customers,
(vcm => vcm.VisualCustomerID),
(vc => vc.ID),
((vcm, vc) => new MultiplierCustomer
{
VisualCustomerID = vcm.VisualCustomerID,
CompanyName = vc.NAME,
Brand = vcm.Website,
MultiplierType = "QuickShip"
}));
IQueryable<MultiplierCustomer> PartsCustomers =
dc.Pm_VisualCustMultipliers
.Where(vcm => vcm.RecordDeleted == false)
.Where(vc => vc.PartsMultiplier == multiplierID)
.Join(dc.Vm_Customers,
(vcm => vcm.VisualCustomerID),
(vc => vc.ID),
((vcm, vc) => new MultiplierCustomer
{
VisualCustomerID = vcm.VisualCustomerID,
CompanyName = vc.NAME,
Brand = vcm.Website,
MultiplierType = "Parts"
}));
IQueryable<MultiplierCustomer> StdBreakCustomers =
dc.Pm_VisualCustMultipliers
.Where(vcm => vcm.RecordDeleted == false)
.Where(vc => vc.StdBreakMultiplier == multiplierID)
.Join(dc.Vm_Customers,
(vcm => vcm.VisualCustomerID),
(vc => vc.ID),
((vcm, vc) => new MultiplierCustomer
{
VisualCustomerID = vcm.VisualCustomerID,
CompanyName = vc.NAME,
Brand = vcm.Website,
MultiplierType = "Standard Break"
}));
List<MultiplierCustomer> usageList =
StdCustomers
.Concat(QSCustomers)
.Concat(PartsCustomers)
.Concat(StdBreakCustomers)
.ToList();



What's neat is that it really does build the SQL that you'd want, doing a UNION ALL on the individual LINQ queries, so that you send one query to the database. Here's the generated SQL:



SELECT [t10].[VisualCustomerID], [t10].[NAME] AS [CompanyName], [t10].[Website] AS [Brand], [t10].[value] AS [MultiplierType]
FROM (
SELECT [t7].[VisualCustomerID], [t7].[NAME], [t7].[Website], [t7].[value]
FROM (
SELECT [t4].[VisualCustomerID], [t4].[NAME], [t4].[Website], [t4].[value]
FROM (
SELECT [t0].[VisualCustomerID], [t1].[NAME], [t0].[Website], @p1 AS [value]
FROM [dbo].[pm_VisualCustMultipliers] AS [t0]
INNER JOIN [dbo].[vm_Customers] AS [t1] ON [t0].[VisualCustomerID] = [t1].[ID]
WHERE ([t0].[StandardMultiplier] = @p0) AND (NOT ([t0].[RecordDeleted] = 1))
UNION ALL
SELECT [t2].[VisualCustomerID], [t3].[NAME], [t2].[Website], @p3 AS [value]
FROM [dbo].[pm_VisualCustMultipliers] AS [t2]
INNER JOIN [dbo].[vm_Customers] AS [t3] ON [t2].[VisualCustomerID] = [t3].[ID]
WHERE ([t2].[QSMultiplier] = @p2) AND (NOT ([t2].[RecordDeleted] = 1))
) AS [t4]
UNION ALL
SELECT [t5].[VisualCustomerID], [t6].[NAME], [t5].[Website], @p5 AS [value]
FROM [dbo].[pm_VisualCustMultipliers] AS [t5]
INNER JOIN [dbo].[vm_Customers] AS [t6] ON [t5].[VisualCustomerID] = [t6].[ID]
WHERE ([t5].[PartsMultiplier] = @p4) AND (NOT ([t5].[RecordDeleted] = 1))
) AS [t7]
UNION ALL
SELECT [t8].[VisualCustomerID], [t9].[NAME], [t8].[Website], @p7 AS [value]
FROM [dbo].[pm_VisualCustMultipliers] AS [t8]
INNER JOIN [dbo].[vm_Customers] AS [t9] ON [t8].[VisualCustomerID] = [t9].[ID]
WHERE ([t8].[StdBreakMultiplier] = @p6) AND (NOT ([t8].[RecordDeleted] = 1))
) AS [t10]
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p1: Input NVarChar (Size = 8; Prec = 0; Scale = 0) [Standard]
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p3: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [QuickShip]
-- @p4: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p5: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [Parts]
-- @p6: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p7: Input NVarChar (Size = 14; Prec = 0; Scale = 0) [Standard Break]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1



Proving, once again, that there's no substitute for checking the generated SQL when you're doing something twitchy with LINQ.

Monday, December 8, 2008

It Ain't Rocket Surgery

Or is it? And why should the rocket need surgery, anyway? And don't we all just love debugging stuff that fails intermittently, but always works correctly on our own machines? So it happened with WCF-based services for AJAX recently.

This wasn't my first entanglement with ASP.NET's temporary files. The clue comes in various forms: You get a build error for a file that's right in front of you, and which compiles cleanly. You get a run-time error that says ASP.NET couldn't find a file that you know is there. Except that it has a funny name, like "App_Web_zxemnnhw.5.cs". That's an ASP.NET temporary file, and you'll find them in places like C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\breidert web\6d823db7\f71b84dd.

The problem is that ASP.NET decides that it can leave some source code to be compiled-on-demand, even at run time. It doesn't seem to affect code-behinds, or code (anywhere in the web project or web application) that is called directly from code-behinds. The issue sneaks in when code in the web application project is only referenced in web.config - things like providers, HTTP modules, or services. This time, the nasty pointy snarly teeth belonged to a WCF service. (Check it out; you can embed WCF services directly in your web application. Right-click on the project or on a folder inside the project, click "Add -> New Item...", and add a web service or a WCF service. This is in VS 2008, where all web apps are AJAX-enabled.)

What you get is a pair of files, one called "MyServiceThing.svc" and a code-behind, "MyServiceThing.svc.cs". You also get some new references and a <system.serviceModel> section in web.config that contains behaviors and bindings for your WCF service. (To use your new service, you'll need to code a service reference in your ScriptManager tag for ASP.NET AJAX.)

And there the problem begins, because there is no direct call or reference to your service code in your C# (or VB) code. ASP.NET figures that it can stash this code in its temporary files, and compile on demand. But wait! There's more! This is a development or staging machine, and you're going to publish to a web server, and maybe copy from there to a production machine. That's where the ASP.NET temporary files get lost, because they don't seem to tag along with the publishing and deployment process. (Note that sometimes the problem doesn't even take this much effort to throw errors in your face. Gotta love it when a project builds cleanly but publishes with errors.)

When you encounter problems with ASP.NET temporary files, there's a simple solution: Move the code to a separate project, and reference the project in your web app.

For WCF services, it gets a little trickier because of the interplay between hosting and ASP.NET AJAX. You need that .svc file, and it needs to stay in your web project. That is specifically an ASP.NET web "page", and it includes an ASP.NET declaration:

<%@ ServiceHost Language="C#" Debug="true" 
Service="NorthwindLINQ.MyServiceThing"
CodeBehind="MyServiceThing.svc.cs" %>


So to move the WCF service out of the web app, you only want to move the code-behind to a new project. Leave the .svc file where it is, delete the CodeBehind reference and file, and make sure the Service reference is the fully qualified class name of the service.


<%@ ServiceHost Language="C#" Debug="true" 
Service="NorthwindLINQ.Services.MyServiceThing" %>


Note that this declaration type is "ServiceHost". This is how WCF services get hosted in an ASP.NET application. Hosting is a critical aspect of WCF, and ASP.NET pretty much takes care of that for you. The constraint is that you're not building a general-purpose service that anyone can call; it's going to be restricted to your web app. On the other hand, it goes through the full ASP.NET pipeline, so it has access to authentication and authorization status, session data, etc.

Also, make sure your ScriptManager tag points to the .svc file:


<asp:ScriptManager  ID="ScriptManager" 
EnablePageMethods="true"
runat="server" EnablePartialRendering="true">
<Scripts>
<asp:ScriptReference Path="~/jscripts/myStuff.js" />
</Scripts>
<Services>
<asp:ServiceReference Path="~/someFolder/MyServiceThing.svc" />
</Services>
</asp:ScriptManager>


Check your web.config; you may not have to change the serviceModel there, but it's good to verify rather than trust. Things to watch for include the fully qualified class (service) name, and the reference to the service contract. Yes, it's OK for the address to be an empty string; ASP.NET takes care of that for you. ASP.NET AJAX supports only webHttpBinding, and you don't need a metadata binding.


<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="NorthwindLINQ.Services.NorthwindLINQAspNetAjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<services>
<service name="NorthwindLINQ.Services.MyServiceThing">
<endpoint address=""
behaviorConfiguration="NorthwindLINQ.Services.NorthwindLINQAspNetAjaxBehavior"
binding="webHttpBinding"
contract="NorthwindLINQ.Services.IMyServiceThing" />
</service>
</services>
</system.serviceModel>


Next comes the question of adding services in a separate project. WCF has its own way of doing this, and it's not really what we want. The project can be a normal class library. When you add a web service to a class library, Visual Studio creates an interface file and a class file (but no .svc file, since that's specific to ASP.NET). It also creates an app.config and puts the WCF binding in it. Get rid of the app.config file, since the bindings you want are already in web.config, and you don't want any other bindings making the service available to any other callers and/or hackers.

When you add a WCF service to ASP.NET, you don't get an interface file for the service contract. When you add a WCF service to a class library, you do get the interface file. I like interface files for service contracts. (You'll have to modify the contract name in web.config to point to the interface.) However you do it, though, you will probably want to modify the contract attributes. Here's a sample interface with service contract:


namespace NorthwindLINQ.Services
{
[ServiceContract(Namespace = "NorthwindLINQ.Web", Name = "ThingService")]
public interface IMyServiceThing
{
[OperationContract]
string DoWork(string thing);
}
}


And here's the associated class file:


namespace NorthwindLINQ.Services
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyServiceThing : IMyServiceThing
{
public string DoWork(string thing)
{
try
{
if (string.isNullOrEmpty(thing))
{
throw new Exception("Service tantrum");
}
return thing + " : Done!";
}
catch (Exception ex)
{
throw new Exception(string.Format("{0}: {1}", ex.GetType(), ex.Message));
}
}
}
}


The namespace and name parameters on the service contract are important; these are the namespace and class names that ASP.NET AJAX will use to construct a proxy for calling your service. The AspNetCompatibilityRequirements attribute is also important so that WCF and ASP.NET AJAX will work smoothly together.

Calling your service from Javascript is easy, finally:


function PageLoad()
{
NorthwindLINQ.Web.ThingService.DoWork("whatever", onCompleted, onError);
}
function onCompleted(results, context, methodName) {
alert(methodName + " : " + results);
}
function onError(errorInfo, context, methodName) {
alert(methodName + " : " + errorInfo);
}


onCompleted and onError will have either your results, or the error info, respectively. Note that you call the service using the namespace and class specified in the service contract.

And that's the rocket surgery to fix the ASP.NET temporary files problem for WCF services.

Update:You know what? It still doesn't fix everything! The .svc file that remains in the web project is still subject to ASP.NET temporary file madness.

So if you have temporary file problems, there's always "Clean Solution" followed by "Rebuild Solution." If that fails, then next time I'm calling a real rocket surgeon.