In this post, I’ll explain how to decouple your WCF data service from a specific data context. This is useful in many ways including changing out your context implementation at runtime or mocking out your context for testing purposes. The example code will use MEF (Managed Extensibility Framework), but any dependency injection framework, service locator implementation, or factory pattern could be used.
Getting Started
The first thing you will need to get started is an implementation of DbContext. I’ve chosen to go with a code first approach in this example because it gives me explicit control over the code in the context. Entity Framework 4.1, which is available as a Nuget package, provides a simple, purpose-driven API, which allows me to create a new DbContext and specify the entity types that it is responsible for in just a few lines of code. The base class and EF4.1 plumbing does all the hard work.
public class PersonContext : DbContext
{
public IDbSet<Person> People { get; set; }
public IDbSet<Address> Addresses { get; set; }
}
This implementation satisfies the most basic requirements of EF4.1 for a DbContext, but as you’ll see as we go along, we’ll want to flesh it out a bit more in order to support MEF’s requirements.
Give Your DbContext an Interface
public interface IDbContext : IDisposableFigure 2: IDbContext interface
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
int SaveChanges();
}
Initializing Your Context
The EF4.1 DbContext class can take a connection string in as a constructor parameter. For simple cases, this might be enough for you. However, if your context requires additional information to operate properly, you may want create a configuration class that you can inject into your context through its constructor. I’ve take this approach because it allows for greater flexibility in initialization of the context.
[Export]
public class DbContextConfiguration
{
public string Name { get; set; }
public string ConnectionString { get; set; }
}
[ImportingConstructor]Figure 4: PersonContext constructor
public PersonContext([Import("PersonContextConfiguration", typeof(DbContextConfiguration))] DbContextConfiguration configuration)
: base(configuration.ConnectionString)
{
_configuration = configuration;
}
Creating Your WCF Data Service
Writing a WCF data service couldn’t be easier. In a matter of a few mouse clicks, you can be serving up REST-based data. Microsoft has dramatically reduced the effort required to implement a service by providing a rich API that provides a lot of functionality under the covers.
Figure 5: Add New Item
Adding a new WCF Data Service through the Add New Item dialog results in an entry point to that API via the DataService<T> class.
1: public class PersonDataService : DataService< /* TODO: put your data source class name here */ >
2: {
3: // This method is called only once to initialize service-wide policies.
4: public static void InitializeService(DataServiceConfiguration config)
5: {
6: // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
7: // Examples:
8: // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
9: // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
10: config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
11: }
12: }
Figure 6: Boilerplate data service code
Simply replace the boilerplate TODO between the generic template brackets with a class that extends DbContext and you’re practically good to go. They even provide code snippets via comments that serve as placeholders for configuration changes that you can use to set access rules for the entity sets exposed by your DbContext.
*Note: in my example above, the DataServiceProtocolVersion.V3 is implemented in the October 2011 CTP release for data services. I began using it in order to integrate with the Entity Framework 4.1 DbContext API.
In our case, we will declare the data service this way:
public class PersonDataService : DataService<IDbContext> {…}
Override CreateDataSource
Next is the final piece of the puzzle. The DataService class provides a convenient way to control the instantiation of your service’s data source dependency. Namely - we will override the CreateDataSource method with an implementation such as this:
protected override IDbContext CreateDataSource()
{
context = compositionContainer.GetExportedValue<IDbContext>();
return context;
}
No comments:
Post a Comment