DevelopMENTAL Madness

Monday, July 28, 2008

LINQ to SQL: Reusing DataContext

I have been working on a project which uses a data access layer (DAL) to wrap some LINQ to SQL calls to the database. While I've played with LINQ to SQL a bit, this was my first time working on a production application. So as I wrote some of the business logic I wrapped some updates in a transaction (as any good database dev would do). Here's pretty much what it looked like:

public void Swap(Product x, Product y)
{
x.DisplayIndex += y.DisplayIndex;
y.DisplayIndex = x.DisplayIndex - y.DisplayIndex;
x.DisplayIndex -= y.DisplayIndex;

using( System.Transactions.TransactionScope txCtx = new System.Transactions.TransactionScope() ) {
x.Save();
y.Save();

txCtx.Complete();
}
}

Here's what the save method looked like:

public Product Save(Product product)
{
MyAppDataContext context = new MyAppDataContext("connection string");

Product oldProduct = context.Products.Single( p => p.ProductId == product.ProductId);

if(oldProduct == null){
context.Products.InsertOnSubmit(product);
} else {
oldProduct.Name = product.Name;
oldProduct.Price = product.Price;
oldProduct.DisplayIndex = product.DisplayIndex;
}
context.SubmitChanges();

return product;
}

This worked fine until I moved my changes out to staging. Where I promptly was given an exception:

System.Transactions.TransactionManagerCommunicationException was unhandled by user code
Message="Communication with the underlying transaction manager has failed."
Source="System.Transactions"

As it turns out, my development machine had Network DTC access turned on (Administrative Tools --> Component Services --> Component Services --> Computers --> (right click) --> properties... --> MSDTC --> Security Configuration... --> Network DTC Access

It had been turned on while I had been doing some tinkering with an SSIS project. So because my machine allowed network DTC calls, I had not recieved any errors. But because it was turned off (the default) on the staging web server it caused an error.

The reason for the error is that each call to my Save method used a different DataContext, each with its own Connection object. The first call to Save opened a Connection and saved the changes. Then when the second call did the same thing it tried to enlist the second connection in a transaction with the first. In order to do this DTC was needed. This worked on my machine, because it was enabled, but not on the web server which was disabled.

After talking to the developer who wrote the DAL I tried to think of how I could come up with a way to use the same DataContext. The EnterpriseLibrary already does this, so there should be a solution somewhere. I started looking into Thread Local Storage (TLS) and found this article, indicating the pitfalls of using TLS in ASP.NET. After reading the article I determined that HttpContext.Current.Items was the place to store my DataContext for the lifetime of the page request. The reason is that there are several cases where the thread context will change in the lifetime of a page request (async methods, page_load, application events).

So I decided I needed a DataContextFactory. To accomplish this, I chose to add a partial class to the DAL with a single method GetDataContext(). If you look at the designer file (C# designer.cs, VB.NET designer.vb) for a dbml file you'll find that the DataContext class generated for your dbml file is a partial class. So my partial class looks like this:

public partial class MyAppDataContext{
public static MyAppDataContext GetDataContext(){
if(HttpContext.Current == null)
return new MyAppDataContext("connection string");

if(HttpContext.Current.Items.Contains("DataContext")){
return (MyAppDataContext) HttpContext.Current.Items["DataContext"];
}

MyAppDataContext context = new MyAppDataContext("connection string");
HttpContext.Current.Items.Add("DataContext", context);
return context;
}
}

After, I did this my call to TransactionScope works. Plus, I was able to eliminate the additional database call in my save method because I am now using the same DataContext throughout the entire lifecycle of the page request. The save method now looks like this:

public Product Save(Product product)
{
MyAppDataContext context = new MyAppDataContext("connection string");

if(product.ProductId <= 0){
context.Products.InsertOnSubmit(product);
context.SubmitChanges();

return product;
}

It is important to note that the code for the Save method is auto generated. So this code could be used within the company for either a web application or a winforms app. For this reason, I needed a solution which would work in both situations. Or that would at least allow me to provide an alternate solution in the event the application requires one. Plus, I wasn't really happy about adding a reference to the System.Web assembly in my DAL.

So I created the following interface and provider:

using System;

namespace DataAccess
{
public interface IThreadLocalStorageProvider
{
void SaveItem ( String key, Object value );
Object GetItem ( String key );
Boolean ContainsItem ( String key );
}

public class ThreadLocalStorageProviderFactory
{
private static IThreadLocalStorageProvider _provider;

public static IThreadLocalStorageProvider Current
{
get
{
return _provider;
}
set
{
_provider = value;
}
}
}
}

By creating a provider with a static reference to an interface, the application can define during startup how it will provide thread local storage without any changes required to the DAL. So for our web application we came up with this handy little class:

namespace RioTintoWebSolution
{
public class WebThreadLocalStorageProvider : IThreadLocalStorageProvider
{
#region IThreadLocalStorageProvider Members

public void SaveItem ( string key, object value )
{
HttpContext.Current.Items.Add( key, value );
}

public object GetItem ( string key )
{
return HttpContext.Current.Items[key];
}

public bool ContainsItem ( string key )
{
return HttpContext.Current.Items.Contains( key );
}

#endregion
}
}

And in the Appilcation_Start event in global.asax.cs we now do this:

protected void Application_Start(object sender, EventArgs e)
{
ThreadLocalStorageProviderFactory.Current = new WebThreadLocalStorageProvider();
}

And our DataContext factory method looks like this:

public partial class MyAppDataContext
{
public static MyAppDataContext GetDataContext ()
{
// if TLS isn't available just return a new DataContext
if ( ThreadLocalStorageProviderFactory.Current == null )
return new MyAppDataContext( "connection string" );

MyAppDataContext context = null;

// check to see if DataContext already exists in TLS
if ( ThreadLocalStorageProviderFactory.Current.ContainsItem( "DataContext" ) )
{
// get DataContext
context = ThreadLocalStorageProviderFactory.Current.GetItem( "DataContext" ) as MyAppDataContext;

// verify DataContext
if ( context != null )
return context;
}

// if we got here, then just create a new DataContext
context = new MyAppDataContext( "connection string" );

// cache the DataContext
ThreadLocalStorageProviderFactory.Current.SaveItem( "DataContext", context );

// return to caller
return context;
}
}

And finally, just to be safe I also decided to verify that the DataContext get cleaned up, I added the following to our Appliction_EndRequest event:

protected void Application_EndRequest(object sender, EventArgs e)
{
if ( ThreadLocalStorageProviderFactory.Current.ContainsItem( "DataContext" ) )
{
DataContext ctx = ThreadLocalStorageProviderFactory.Current.GetItem( "DataContext" ) as DataContext;
if ( ctx != null && ctx.Connection.State == ConnectionState.Open )
{
ctx.Dispose();
}
}
}

Finally, I wanted to confirm that what I was doing wasn't going to bite me. So I searched around the net abit and found this post by Rick Strahl. Rick's solution is more 'generic', but at the same time I prefer the ProviderFactory pattern over the utility class with specialized methods (ie. GetWebRequestScopedDataContext and GetThreadScopedDataContext). Everybody has their preferences, and I'm certainly not trying to disparage Nick in any way. His method, is excellent, and he makes it easy to use multiple different DataContexts within the same thread. The reason I prefer the ProviderFactory is becuase it is more generic, so I don't have to change my method calls when I move my DAL to a winforms app. And I can still add the same features he uses to create more flexibility.

Labels: , , , ,

Thursday, July 24, 2008

Microsoft VS Middle School Power Toy 1.0

I just read an announcement today about Microsoft Visual Studio Middle School Power Toy 1.0. I've always been interested in passing on my love for programming to my kids. I have a 9-year-old who I'd like to introduce to programming in a simple way, but I'm not sure this is quite the tool for the job.

While I'm sure this could be a good tool for a child who is already interested in programming and want's to start writing code, I'm not sure this is the kind of tool to introduce programming. As I read the overview, the features discuss learning algorithms and program flow - not real interesting stuff. IMHO, I believe that you've gotta show them what kind of stuff you can do - big picture things. Then once you've got them, start them on something like this as a way to learn the basics in a hands on, intuitive way.

But being introduced to sorting algorithms is not a way to get them interested. Of course, that may not be the goal of this tool. And my daughter may not quite be middle school yet. So that may be the problem there. But I would certainly be interested about anyone who knows of a good way to introduce programming to kids - something that will help them get the bug.

I know for me, when I was young I got my hands on a book which I have no idea what the language was. I was able to get 2 programs running on my dad's computer, both dealing with random numbers - a magic eight ball program and a "pick a door" game. After that I forgot about programming until I caught the bug again with Excel macros. From there I dug into VBA and now here I am.

Anyway, if anyone who stumbles on this has some good recommendations, I'd love to hear them.

Thursday, July 17, 2008

Using ExtJs with WCF

Been wanting to get to this part of development for a while now and since I struggled a bit to get this working I thought it would make a good post. With the release of the .NET Framework 3.5 WCF seems to be growing in popularity (or maybe it's just me), but since WCF seems to be the replacement for WebSerivices I figure I need to get to know it.

In my last post on WCF I didn't use any client, other than just pointing the browser window at the service address for an operation with no parameters and a return type of "text/plain". Now that I've done some work with a client, I've found this was not very helpful other than proving that the service was up and could serve up text. I hope this will prove more helpful.

Setting up the service

Let's start by getting a service up and running that we can access from our web page. First, create a new ASP.NET Web Application project in VS 2008. Then add a new "AJAX-enabled WCF Service" item to your project. I'll name mine Ajax.svc.

I don't know about VB.NET (I don't have it installed on my machine), but I found that the item template wasn't listed with the web templates in the "Add new item" dialog box. You can find the item template in the root category "Visual C#", but since I usually use the categories to filter out the long list of items it took me a while to find it.

Once you've created the svc item you'll have 2 new files in your project:
  • Ajax.svc - this is the target for the service. When you reference operations from your client you'll use the path ~/{path to parent folder}/Ajax.svc
  • Ajax.svc.cs - this is where you'll write the service definition. Think of this file as your "code behind" file and define the actual service implementation here. As an aside you can define an interface with the service definition with the attributes applied. You could then have different implementations of the service which all adhere to the defined contract. But we'll just keep things to a minimum here.
You'll also notice, if you look at the project references that you have a couple new references added to your project:
  • System.Runtime.Serialization
  • System.ServiceModel
  • System.ServiceModel.Web

Creating the service definition

In my first WCF post we created a typical "Hello World" service, which took no inputs and returned plain/text output. Here I'd like to accept several parameters and return a custom object. I'd also like to have an operation which will return a collection of objects. This way I hope to document a few of the tricks required to create a more real-world example.

Multiple params/simple return value

The first example uses multiple inputs and returns a single, scalar value. Add the following to the Ajax class in your Ajax.svc.cs file:

[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebInvoke(
BodyStyle=System.ServiceModel.Web.WebMessageBodyStyle.WrappedRequest,
RequestFormat=System.ServiceModel.Web.WebMessageFormat.Json,
ResponseFormat=System.ServiceModel.Web.WebMessageFormat.Json,
UriTemplate="/Add")]
public int Add(int a, int b)
{
return a + b;
}

Sorry for not using namespace shortcuts here, but when I'm looking at a howto nothing bugs me more than having to track down the proper namespaces. You shouldn't have to worry about these as the item template for your web service should automatically add the using statements at the head of the file for you.

The method must be public and must have the System.ServiceModel.OperationContract attribute to be exposed as part of the service. We'll focus here on the System.ServiceModel.WebInvoke attribute. You have the option of using the System.ServiceModel.WebGet attribute as well, but I wanted to use WebInvoke here to point out that WebGet will limit you to simple types (int, string, etc) and the http GET method. Using WebInvoke allows you to define parameters using complex, custom types and allows you to use both GET and POST. You can also explicitly limit the operation to just one of http method using the "Method" argument of the WebInvoke attribute.
  • BodyStyle (System.ServiceModel.Web.WebMessageBodyStyle) - You can specify Bare, WrappedRequest, WrappedResponse or Wrapped. The key here is the number and type of your input/output. Bare is used for one or zero simple types. So if your input has no parameters or a single, simple type AND your return value is either "void" or a simple type then you can use Bare. If you only need to wrap one of either the request or response then use the WebMessageBodyStyle specific to what you need (ie. multiple and/or complex params = WrappedRequest; complex return type = WrappedResponse). If you need to wrap both the response AND the request, then "Wrapped" will take care of that.
  • RequestFormat/ResponseFormat (System.ServiceModel.Web.WebMessageFormat) - You really have two options, Json or Xml. If you want to return plain text this will essentially set the request.ContentType for you to whatever you select. I believe the default is probably Xml, but I haven't verified that. Incorrectly using this parameter will result in an error message similar to the following: Operation 'Add' of contract 'Ajax' specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.
  • Method (System.String) - The default is POST, but you can also set this to GET as long as your RequestFormat can be "Bare".
  • UriTemplate (System.String) - This is what the name of the operation will be when it is exposed with the service. It doesn't have to match the name of the class method. When you reference the operation it will be in the format ~/{path to parent folder}/{service name}.svc/{UriTemplate}. Also, if you want to map your parameters to querystring /post parameters you can do that here as well like this: Add?a={a}&b={b}. And the parameters will map to your method signature. If you are going to use UriTemplate, then you will also want to change replace the <enablewebscript/> behavior in your web.config file with <webhttp/>. Otherwise you'll get this error: Endpoints using 'UriTemplate' cannot be used with 'System.ServiceModel.Description.WebScriptEnablingBehavior'.

Complex Types

Next, let's add a complex type we can use to meet the goals I stated at the beginning of this post:

[System.Runtime.Serialization.DataContract]
public class Person
{
public Person(String first, String last, Int32 age)
{
FirstName = first;
LastName = last;
Age = age;
}

[System.Runtime.Serialization.DataMember]
public String FirstName { get; set; }
[System.Runtime.Serialization.DataMember]
public String LastName { get; set; }
[System.Runtime.Serialization.DataMember]
public Int32 Age { get; set; }
}

Nothing fancy here, just annotate the class as DataContract and each property to be exposed as DataMember. However, I'd like to mention a small, but important point here. If the class implements System.Runtime.Serialization.ISerializable you don't need to annotate your class with DataContract and DataMember. Just make sure the class properly implements the ISerializable.GetObjectData method and has a constructor with the same signature as GetObjectData.

If you try mixing DataContract with Serializable you'll get an System.Runtime.Serialization.InvalidDataContractException error message like this:

Type 'ExtJsWCF.Person' cannot be ISerializable and have DataContractAttribute attribute.

Now, back to our remaining service operations. Go ahead and add the following to our Ajax service class definition:

[OperationContract]
[WebInvoke(
BodyStyle = WebMessageBodyStyle.Wrapped,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/GetPerson")]
public Person GetPerson(String first, String last, Int32 age)
{
return new Person(first, last, age);
}

[OperationContract]
[WebInvoke(
BodyStyle = WebMessageBodyStyle.Wrapped,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/GetRelatives")]
public IList<Person> GetRelatives(Person person)
{
System.Collections.Generic.List<Person> people = new System.Collections.Generic.List<Person>();

people.Add(new Person("Joe", person.LastName, 41));
people.Add(new Person("John", person.LastName, 33));
people.Add(new Person("Jane", person.LastName, 30));

return people;
}

[OperationContract]
[WebInvoke(
BodyStyle = WebMessageBodyStyle.Wrapped,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/SaveRelatives")]
public Boolean SaveRelatives(Person[] people)
{
return true;
}


At this point we should be able to compile our service. Now we're ready for our client.

Setting up the client (ExtJs)

At this point I'm going to go into specifics about ExtJs. Much of what I'll address should be applicable to most clients, but I'll leave those specifics to you.

If you haven't already, you'll want to download the ExtJs sdk. I am using 2.1. Once you've unzipped the archive, you can copy the following files and directories to the root of our application:
  • /ext-all.js
  • /adapter/
  • /resources/
Now open Default.aspx and add the following to the <head> element:

<link href="extjs/resources/css/ext-all.css" rel="stylesheet" type="text/css" />
<script src="extjs/adapter/ext/ext-base.js" type="text/javascript"></script>
<script src="extjs/ext-all.js" type="text/javascript"></script>

<style>
.myForm {
margin: 3em;
}
.myResponse
{
color: Red;
font-weight: bold;
}
</style>
<script type="text/javascript">
var mask;

Ext.onReady(function(){
Ext.get('btnSum').on('click', addValues);
Ext.get('btnGetPerson').on('click', getPerson);
Ext.get('btnGetFamily').on('click', getFamily);

mask = new Ext.LoadMask(Ext.getBody(), {
msg: "Loading..."
});

});

function addValues()
{
mask.show();

var aVal = Ext.get('txtA').dom.value;
var bVal = Ext.get('txtB').dom.value;

var params = { a : aVal, b : bVal };

Ext.lib.Ajax.defaultPostHeader = 'application/json';

Ext.Ajax.request({
url: '/Ajax.svc/Add',
method: 'POST',
params : Ext.encode(params),
success: function(response,options){
mask.hide();
Ext.get('txtResult').dom.value = response.responseText;
Ext.get('additionResponse').dom.innerHTML = response.responseText;
},
failure: function(response,options){
mask.hide();
Ext.MessageBox.alert('Failed', 'Unable to add values');
}
});
}

function getPerson()
{
//status display
mask.show();

//get form values
var fVal = Ext.get('txtFirst').dom.value;
var lVal = Ext.get('txtLast').dom.value;
var aVal = Ext.get('txtAge').dom.value;

//create Json object
var params = { first: fVal, last: lVal, age: aVal };

//change post header from application-x-urlencoded
Ext.lib.Ajax.defaultPostHeader = 'application/json';

//create request
Ext.Ajax.request({
url: '/Ajax.svc/GetPerson',
params: Ext.encode(params),
method: 'POST',
success: function(response,options){
// response callback

//update status display
mask.hide();

// display literal output from response
Ext.get('getPersonResponse').dom.innerHTML = response.responseText;

// decode text into Json object
var data = Ext.decode(response.responseText);

// display data from object
Ext.MessageBox.alert('First: ' + data.GetPersonResult.FirstName
+ '<br>Last: ' + data.GetPersonResult.LastName
+ '<br>Age: ' + data.GetPersonResult.Age);
},
failure: function(response,options){
mask.hide();
Ext.MessageBox.alert('Failed', 'Unable to get person');
}
});
}


function getFamily()
{
//status display
mask.show();

//get form values
var fVal = Ext.get('txtFirst').dom.value;
var lVal = Ext.get('txtLast').dom.value;
var aVal = Ext.get('txtAge').dom.value;

//create Json object
var params = { person: { FirstName: fVal, LastName: lVal, Age: aVal } };

//change post header from application-x-urlencoded
Ext.lib.Ajax.defaultPostHeader = 'application/json';

//create request
Ext.Ajax.request({
url: '/Ajax.svc/GetRelatives',
params: Ext.encode(params),
method: 'POST',
success: function(response,options){
// response callback

//update status display
mask.hide();

// display literal output from response
Ext.get('getPeopleResponse').dom.innerHTML = response.responseText;

// decode text into Json object
var data = Ext.decode(response.responseText);

// display data from object
var store = new Ext.data.JsonStore({
data : data,
root : 'GetRelativesResult',
fields: [
{name: 'FirstName'},
{name: 'LastName'},
{name: 'Age'}
]
});

Ext.get('persongrid').dom.innerHTML = '';

var family = new Ext.grid.GridPanel({
store : store,
colums : [
{id: 'FirstName', header: 'Name', dataIndex: 'FirstName'},
{id: 'LastName', header: 'Last Name', dataIndex: 'LastName'},
{id: 'Age', header: 'Age', dataIndex: 'Age'}
],
title : 'Family',
autoHeight: true
});

family.render('persongrid');
},
failure: function(response,options){
mask.hide();
Ext.MessageBox.alert('Failed', 'Unable to get family');
}
});
}

</script>

An add this to the <body> element of the page:

<div>
<div id="addition" class="myForm">
<div>A: <input id="txtA" type="text" size="3" maxlength="3" /></div>
<div>
B: <input id="txtB" type="text" size="3" maxlength="3" />
<input id="btnSum" type="button" value="sum" />
</div>
<div>Answer: <input id="txtResult" type="text" size="5" /></div>
<div>Http Response text:<div id="additionResponse" class="myResponse"></div></div>
</div>

<div id="personform" class="myForm">
<div>First Name: <input id="txtFirst" type="text" /></div>
<div>Last Name: <input id="txtLast" type="text" /></div>
<div>
Age: <input id="txtAge" type="text" maxlength="2" size="3" />
<input type="button" id="btnGetPerson" value="Get" />
<input type="button" id="btnGetFamily" value="Get Family" />
</div>
<div>Http Response text:<div id="getPersonResponse" class="myResponse"></div></div>
</div>

<div class="myForm">
<div id="persongrid"></div>
<div>Http Response text:<div id="getPeopleResponse" class="myResponse"></div></div>
</div>
</div>


Now load the page in your browser and give it a whirl!

Here's what you'll notice as you play with the page:
  • The Json in the request body needs to be encoded by placing quotes around each identifier as well as any string values.
  • The response from the Add method is just plain text, no quotes, no Json notation.
  • When the response is wrapped the root name of the object is the name of the WCF operation plus the text "Result". Make sure and account for that when working with the objects. I'm still looking for a way to control that value, but for now I'm just using the javascript String.Replace function like this: new String(response.responseText).replace(/MethodNameResult/, "whatIWantHere");
  • The Json response comes across the wire encoded, so you'll need to decode the object to work with it.
  • Date values are serialized by the service as "/Date(1235468858)/" where 1235468858 is the number of ticks from January 1, 1970. The problem with this is in order for this date to work (at least with ExtJs) it needs to be "/new Date(1235468858)/". To achive this you can use a regex to insert it. Here's what I'm using: var exp = response.responseText.replace(new RegExp('(^|[^\\\\])\\"\\\\/Date\\((-?[0-9]+(-[0-9]+)?)\\)\\\\/\\"', 'g'), "$1new Date($2)");
  • When sending an array to the service from the client an important thing to know is that sending just the array isn't enough. It seems natural just to send the array. But if you were to send the encoded array, you'll get the following error: OperationFormatter encountered an invalid Message body. Expected to find an attribute with name 'type' and value 'object'. Found value 'array'. Make sure that you encode your array as a property of a Json object, like this: Ext.encode({parameterName: myArray});.

Conclusion

This post is a result of hours of frustration and digging, so I hope it helps someone save some time and avoid the traps I ran into.

Labels: ,