| | |
Chapter XV | | |
The Series
WCF by example is a series of articles that describe how to design and develop a WPF client connected to a server component using WCF. For persistence purposes, in the server side, we have already covered how to implement NHibernate, InMemory and RavenDB. The series introduction describes the scope of the articles and discusses the architect solution at a high level. The source code for the series is found at CodePlex.
Chapter Overview
This chapter discusses what it takes to implement EF5 Code First using SQL Compact on AzureWebServices and then get a WPF client running in your local machine to interact with the services deployed on Azure.
In relation to the Azure deployment, I used VS2010; if you are using VS2012 you probably find the deployment easier as the IDE is fully integrated with Azure. I am not covering in this article the steps for deployment if you have installed the Azure SDK, instead, the article describes how to manually deploy the server components using an FTP client.
For those new to the series, the persistence components of the eDirectory solution are designed around the unit of work and repository patterns. The first sections in this chapter cover the implement of those patterns using EF5 Code First.
The following is an example of the code used in the server components:
public CustomerDto UpdateCustomer(CustomerDto dto)
{
01 return ExecuteCommand(locator => UpdateCustomerCommand(locator, dto));
}
private CustomerDto UpdateCustomerCommand(IRepositoryLocator locator, CustomerDto dto)
{
02 var instance = locator.GetById<Customer>(dto.Id);
03 instance.Update(locator, dto);
return Customer_to_Dto(instance);
}
Line 01: Entry point for the customer service to update an existing instance
Line 02: Using the Dto.Id property we use the Locator to resolve the customer instance
Line 03: Then we delegate to the domain class to update itself passing the locator and the Dto instances
For a more detailed discussion of the patterns used in the solution, you may want to look at the following chapters:
Entity Framework Implementation - Overview
I wanted to achieve the following goals implementing EF in the eDirectory solution:
- Minimal changes to the domain entities
- If feasible, do not make code changes in the service implementation
- The final solution should not break the other existing implementations
The following were the most relevant changes taken to get EF working:
- Replace the specialised Iesi collections and use .NET collections instead
- Inner Mapping classes were added inheriting from EntityTypeConfiguration in the entity classes
- The introduction of a new
FlushModifications
method in the RepositoryLocator
The first change, the one regarding the collections, was easy to resolved. The one regarding the Mapping inner classes is a way to replace the mapping configuration we had in NHibernate. I don't like to have persistence declaration within the entity but it was an easy way to manage mapping declarations for non-public members, which is the case of the collections in the eDirectory solution. The last one is relevant as EF manages identity columns in a different fashion than NHibernate does when a new entity is created. When NHibernate always provides an Id value once the Save
method is invoked, EF does not do so when the Add
method is invoked. The FlushModifications
provides a mechanism so all modifications are persisted so the new entities get the Id property populated, the process takes place without commiting the current transaction.
EF Repository
This is the third time we implement this pattern within the series; we have already discussed the NHibernate and the RabenDB implementations. In any case, a quick remainder of what a Repository means within the eDirectory solution; in first place it is a generic class that exposes basic CRUD operations for a domain entity, but it also exposes a FindAll
method that returns a IQueryable
instance. I have used the EF linq implementation: AsQueryable
to provided such a functionality. The following code is the final EF implementation:
public class RepositoryEF<TEntity>
: IRepository<TEntity> where TEntity : class
{
private readonly IDbSet<TEntity> _dbSet;
public RepositoryEF(IDbSet<TEntity> dbSet)
{
_dbSet = dbSet;
}
#region Implementation of IRepository<TEntity>
public TEntity Save(TEntity instance)
{
return _dbSet.Add(instance);
}
public void Update(TEntity instance)
{
}
public void Remove(TEntity instance)
{
_dbSet.Remove(instance);
}
public TEntity GetById(long id)
{
return _dbSet.Find(id);
}
public IQueryable<TEntity> FindAll()
{
return _dbSet.AsQueryable();
}
#endregion
}
The IDbSet
provides all the functionality that is required, implementing the IRepository
pattern using EF was very straight forward.
EF Repository Locator
The IRepositoryLocator
main responsibility is the creation of entity repositories within the transaction, the first time a repository is used the locator creates it. In the case of the EF implementation, it also exposes the FlushModifications
that it was mentioned above. The code, again, is straight forward:
public class RepositoryLocatorEF
: RepositoryLocatorBase
{
private readonly DbContext _dbContext;
public RepositoryLocatorEF(DbContext dbContext)
{
_dbContext = dbContext;
}
#region Overrides of RepositoryLocatorBase
public override void FlushModifications()
{
base.FlushModifications();
_dbContext.GetObjectContext().SaveChanges(SaveOptions.DetectChangesBeforeSave);
}
protected override IRepository<TEntity> CreateRepository<TEntity>()
{
return new RepositoryEF<TEntity>(_dbContext.Set<TEntity>());
}
#endregion
}
So the locator keeps a reference to a DbContext
that helps creating repository instances but it also helps in getting the ObjectContext
that allows us to save the changes in the middle of a transaction without committing. By the way, the GetObjectContext
is just an extension method that you can find at the DbContextExtensions
under the TransManager folder.
EF Transaction Manager
This class is the one that required the most code, nevertheless, it was not too bad, in 70 LOC we have a full transactional EF component in place. As its name indicates, this class is responsible for managing all the entity changes within a transaction; we use this class within a using
statement and then we invoke the ExecuteCommand
that gives access to a RepositoryLocator
; the use of lambdas delegating to helper methods is a must when using this pattern. When the using statement is complete, the transaction manager looks after committing all the changes. If an exception takes place, a rollback is actioned without any additional code statements in the business logic. Following is the code:
public class TransManagerEF
: TransManagerBase
{
private readonly DbContext _dbContext;
private DbTransaction _transaction;
public TransManagerEF(DbContext dbContext)
{
_dbContext = dbContext;
var locator = new RepositoryLocatorEF(_dbContext);
Locator = locator;
}
public override void BeginTransaction()
{
base.BeginTransaction();
if (_dbContext.GetObjectContext().Connection.State != ConnectionState.Open)
{
_dbContext.GetObjectContext().Connection.Open();
}
_transaction = _dbContext.GetObjectContext().Connection.BeginTransaction();
}
public override void CommitTransaction()
{
base.CommitTransaction();
_dbContext.GetObjectContext().SaveChanges(SaveOptions.DetectChangesBeforeSave);
_dbContext.GetObjectContext().AcceptAllChanges();
_transaction.Commit();
}
public override void Rollback()
{
base.Rollback();
_transaction.Rollback();
}
...
}
Again a DbContext
instance is the key EF component used here, in fact, the same instance is passed to the RepositoryLocator
created with this TransactionManager
. You can see how full control over the connection is taken using the ObjectContext
, the DbContext
instance is not good for what we need. The ObjectContext
provides all the advanced functionality that is required to manage the transaction instead. Worth to mention is the DbTransation
instance that allows to rollback the changes if required.
EF Transaction Manager Factory
The only role for this class is the creation of TransactionManager
instances. The code is the following:
public class TransManagerFactoryEF
: ITransFactory
{
public IModelCreator ModelCreator { get; private set; }
public TransManagerFactoryEF(IModelCreator modelCreator)
{
ModelCreator = modelCreator;
}
#region Implementation of ITransFactory
public ITransManager CreateManager()
{
return new TransManagerEF(new eDirectoryDbContext(ModelCreator));
}
#endregion
}
The main difference with other implementations is the IModelCreator
, this is an EF Code First requirement; it helps in the EF initialisation and in the mapping between domain entities and the database tables. In first place we have a new class eDirectoryDbContext
:
public class eDirectoryDbContext : DbContext
{
public IModelCreator ModelCreator { get; private set; }
public eDirectoryDbContext(IModelCreator modelCreator): base("eDirectory")
{
ModelCreator = modelCreator;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
ModelCreator.OnModelCreating(modelBuilder);
}
}
This is the common pattern in EF, it permits to initialise the persistence layer. One difference with other common implementation is that it does not declare a DbSet property for each entity/aggregation; instead we delegate to the IModelBuilder
to do so. In this way this class is very generic and does not have a direct dependency to our domain/entities.
It is worth noting how the constructor delegates to the base class passing the connection name in the App/Web configuration file. You may want to articulate a more flexible mechanism in your own implementation so this is not hard coded in your solution.
The IModelBuilder
implementation is straight forward:
public class ModelCreator:IModelCreator
{
#region Implementation of IModelCreator
public void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new Customer.Mapping());
modelBuilder.Entity<Address>();
}
#endregion
}
It is just pretty much a fluent declaration of our mappings, a very short one though; this class resides on the Domain project and it gets injected at run time on the TransManagerFactoryEF
. Then the first time a DbContext
is used, this class is invoked and configures EF so it can work with our database. EF is quite smart linking the Poco classes to the database, in our case, it just needs a little help.
In first place, you can use DataAnnotation attributes, the EntityBase uses the [Key] attribute to indicate which is the PK in all our entities. Then you can use the generic Entity
method in the ModelBuilder
(as it was done with the Address class), or you can also delegate to an EntityTypeConfiguration
class. This is the case for the Customer
entity:
[JsonObject(IsReference = true)]
public class Customer
:EntityBase
{
protected Customer()
{
AddressSet = new HashSet<Address>();
}
...
protected virtual ICollection<Address> AddressSet { get; set; }
...
public class Mapping : EntityTypeConfiguration<Customer>
{
public Mapping()
{
HasMany(c => c.AddressSet).WithRequired(a => a.Customer);
}
}
}
The Mapping
class is internal so it can access the AddressSet
collection that is not publicly exposed. The customised mapping declares the collection using the "hidden" collection and its navigation mapping with the Address entity. As I said before, you may not like to include this sort of dependency in your entities, another approach is to use partial classes to achieve the same result.
Worth to notice, we don't really need to declare the Address
entity in the ModelBuilder
, EF can work out the configuration mappings from the aggregate root entities, in this case, declaring the Customer
is sufficient for EF to know about the Address
.
WPF Client Configuration with EF and SQL Compact
To get the WPF client to use EF to connect to a SQL Compact database you have to add the following section to your configuration file:
<connectionStrings>
<add name="eDirectoryDbContext" providerName="System.Data.SqlServerCe.4.0" connectionString="Data Source=|DataDirectory|\eDirectory.sdf;default lock timeout=60000" />
</connectionStrings>
Watch out for the connection name, it is the same one that we use declaring the eDirectoryDbContext
but without the "Context" suffix. As you can see we indicate that the database is to be found at the DataDirectory
, that is, the output folder for a client application or the App_Data folder in a web application. Then add the EntityFramework.SqlServerCompact
package using NuGet, it will also add the following dependencies:
Then check that the following lines were added to your configuration file:
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
<parameters>
<parameter value="System.Data.SqlServerCe.4.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
In this example, the SQL Compact database was already created. It should not be difficult to let EF Code First to generate the database if needed. You may also install the SQL Server Compact Tools
VS Extension. It provides some nice functionality for creating and managing compact databases.
At this point you should try to test the application locally and when you are sure things are working, then you should be ready to deploy to the web.
Deployment to Azure WebSites
You may know that currently Azure Web Sites offer free hosting for small web site deployments, you may want to check for pricing and features, deployment using VS2010 or VS2012 is very straight forward if the Azure SDK is installed in your machine. You may check out this video from Scott Hanselman or a full article regarding Azure WebSites deployment.
But you can also deploy the application using a FTP client, if you do so, you don't need to install the SDK at all. This article contains two set of binaries, one with the WPF client configured to execute connected to the WcfByExamples server in Azure. The other set is the server side artefacts so you can try to deploy it by yourself.
WPF Client
Lets try to see how to get the client working with the Azure WebSite, firstly, download the WPF Client binaries and execute the application, if you get to the following window and you can see data, then you are connected to the Azure web site:
If you have a look at the client configuration file, you can see the WCF endpoits configured to connect to the Azure web site:
="1.0"="utf-8"
<configuration>
<appSettings>
<add key="SpringConfigFile" value="file://WcfConfiguration.xml" />
</appSettings>
<system.serviceModel>
<client>
<endpoint address="http://wcfByExample.azurewebsites.net/CustomerWcfService.svc/CustomerServices"
binding="basicHttpBinding"
contract="eDirectory.Common.ServiceContract.ICustomerService"
name="BasicHttpBinding_ICustomerService">
</endpoint>
<endpoint address="http://wcfByExample.azurewebsites.net/AddressWcfService.svc/AddressServices"
binding="basicHttpBinding"
contract="eDirectory.Common.ServiceContract.IAddressService"
name="BasicHttpBinding_IAddressService">
</endpoint>
</client>
</system.serviceModel>
</configuration>
The client configuration is very simple and because it uses the basic Http binding you should have not trouble to get it working in most networks.
Server Deployment
As mentioned earlier, with the Azure SDK the deployment is easy from VS; here instead, I am going to explain how to deploy the server solution manually, you need to do the following:
- Download the eDirectory server binaries contained within this article
- Install a FTP client, I used FileZilla
- Create an account in Azure so you can create a web site
- Deploy the server binaries
- Point the WPF client to your site
Create Web Site in Azure
Once you have an Azure account, you need to create a web site using the 'quick create' option:
Then, give it a name:
Once it is created, we need to set the FTP user and password. Select 'Deployment Credential' option on the right side and enter a user name and password:
At this point, you should be able to gather the following FTP details in the dashboard on the right side:
With that it should be easy to connect using the FTP client:
Copy all the server files, including the App_Data and bin folders, to the wwwroot folder in the Azure site. When you are finished, you can configure the client to point to your site. Just change the eDirectory.WPF.exe.config
so the end points are renamed using your site name:
Enable NHibernate Instead
You may want to change the server side to use NHibernate instead, it is just one liner change. Open the web.config file and change the appSetting 'SpringConfigFile' so is set to ServerNhConfiguration.xml instead:
Conclusion
Finally I got the time to implement EF on the eDirectory series. I was surprised how little mapping was required to get the solution working once I put in place the main EF components. On the other hand, I struggled with the EF API when I was trying to implement the transaction manager; it seems that the DbContext to be the recommended object to use but then it is in the ObjectContext where all the advance functionality is available.
I hope you like the combination of SQL Compact and ORM solutions, I think is a winner for small/medium applications. It is very easy to deploy on the web and it performs very well.
The last but not the least, I hoped the article convinces how easy is the deployment of your apps to Azure these days ... and you get 10 web sites for free, small ones but for many it may well be a good start point