September 16, 2010 08:21 AM

A Perfect Storm: LINQ to SQL, Dependency Injection, and ASP.NET Providers

Dev Pro
InstantDoc ID #125981

Recently I ran into a scenario where LINQ to SQL, dependency injection, and ASP.NET providers were put together in a way that created a perfect storm that was difficult to troubleshoot. I’ll share some of the circumstances around the creation of that perfect storm, plus my solution, with the hope that it will spare someone else the difficulty of dealing with the same problem.

LINQ to SQL’s DataContext and Stale Results

As you know from a previous article, I’m a big fan of PLINQO because it removes the rough-edges from LINQ to SQL (L2S) and makes it a very viable solution for high-performance sites that need easy-to-manage object mapping. However, I’m not a fan of how LINQ to SQL handles the caching of query results internally. Specifically, I’m not a fan of how a LINQ to SQL DataContext will actually connect to the database, execute a query, and then discard the results of that query if it has already pulled back results for the same query previously.

Of course, it’s important to point out that I’m not opposed to LINQ to SQL caching results. In fact, I think it’s a great idea because LINQ to SQL DataContexts are DESIGNED to be very short lived (i.e., they should, ideally, be scoped to a single HTTP Request when used in a web-app for instance). My beef is just with the fact that caching is in play, yet L2S will still query the database only to DISCARD the results.

That said, as long as you understand that LINQ to SQL DataContexts are designed to be short lived (see Myth #10), then everything is fine. Unless, of course, you were to accidently scope one of the DataContexts into something long-term like, say, an ASP.NET Membership Provider.

ASP.NET’s Provider Model

Even though ASP.NET’s provider model for custom Membership, Roles, and Personalization has been around forever (it was released with ASP.NET 1.1), custom providers are still a key component of many ASP.NET sites. In my estimation that’s because custom providers are so incredibly easy to implement.

For example, if you want to create a custom ASP.NET MembershipProvider that will let website users authenticate against your own database, all you need to do is:

  • Inherit from MembershipProvider
  • Have a Parameterless constructor (more on this in a second)
  • Add logic to the ValidateUser method (which takes in a username and password and returns true or false).
  • Wire up the proper directives in your web.config

From there, ASP.NET uses your MembershipProvider implementation as the implementation for your application – which means that when users need to log into your site, they can be authenticated against your database or against whatever logic you’ve defined.

Dependency Injection

However, the problem is that most custom provider implementations are going to need access to a database in order to do lookups, pull back role assignments, verify usernames and passwords, and so on. LINQ to SQL can be used very effectively in these cases because it makes it very easy to pull back custom ‘User’ objects which can then be evaluated for things like authentication and role assignment.

That said, it’s a worst-practice to just use a LINQ to SQL DataContext directly within any consuming class because it makes your code brittle, hard to test, and subject to poor coupling. Consequently, a better approach is to use a Repository—where a Repository object wraps the LINQ to SQL DataContext (and any association with the underlying data access) from consumers. In this way, whenever you need access to a certain kind of repository within a class or object (in smaller applications you might have only a single repository, whereas in a larger application you might have a UserRepository, a ReportsRepository, an AdminRepository, and so on), you just pass a concrete instance of the needed repository’s interface into your classes or controllers via dependency injection.

Typically, constructor injection is the preferred method for injecting dependencies because requiring instances of things like FileReaders, PermissionsManagers, and DataRepositories within a constructor makes it painfully obvious that the class in question must have these objects (and encapsulated logic) available in order to function properly. (As opposed to just having the class magically and mysteriously create instances of these classes internally, which leads to poor coupling because this approach means that you don’t have a clear understanding of what kinds of object dependencies you have in place.)



ARTICLE TOOLS


Comments
    There are no comments to display. Be the first one!
You must log on before posting a comment.

Are you a new visitor? Register Here