skip to main | skip to sidebar
Showing posts with label MVC Framework. Show all posts
Showing posts with label MVC Framework. Show all posts

Tuesday, November 30, 2010

ASP.NET MVC: Creating a URL From Route Values

I’m currently building some JSON services for a client using ASP.NET MVC. One of the things I want to implement is HATEOS (read the link) so I need to be able to build the URLs (or URIs) of the endpoints that I can then embed into the JSON I return to the client.

I wanted to be able to take some route values, controller=”abc”, action=”xyz”, id=10, and return a virtual path. Unfortunately System.Web.Routing makes this a bit of trial, but you can make it work. I’ve wrapped my implementation up in a little UriBuilder class. Here’s a test showing it working:

[Test]
public void CreateUriFromRouteValues_should_create_the_correct_virtual_path()
{
var routes = new RouteCollection();
routes.MapRoute(
"ProgRock",
"{band}/{album}/{track}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });

var uriBuilder = new UriBuilder(() => routes, () => new FakeHttpContext());

var uri = uriBuilder.CreateUriFromRouteValues(new
{
band = "Yes",
album = "Fragile",
track = "Roundabout",
info = "great keyboard solo"
});

uri.ShouldEqual("/Yes/Fragile/Roundabout?info=great%20keyboard%20solo");
}

And here’s the implementation:

public interface IUriBuilder
{
string CreateUriFromRouteValues(object values);
}

public class UriBuilder : IUriBuilder
{
private readonly Func<RouteCollection> getRouteCollection;
private readonly Func<HttpContextBase> getHttpContext;

public UriBuilder(Func<RouteCollection> getRouteCollection, Func<HttpContextBase> getHttpContext)
{
this.getRouteCollection = getRouteCollection;
this.getHttpContext = getHttpContext;
}

public string CreateUriFromRouteValues(object values)
{
var routeValues = new RouteValueDictionary(values);

var routeData = new RouteData();
var requestContext = new RequestContext(getHttpContext(), routeData);
var virtualPathData = getRouteCollection().GetVirtualPath(requestContext, routeValues);
if (virtualPathData == null)
{
throw new ApplicationException("virtualPathData is null");
}
return virtualPathData.VirtualPath;
}
}

In order to unit test it, you have to fake the HttpContext. Here’s my fake implementation:
 
public class FakeHttpContext : HttpContextBase
{
public override HttpRequestBase Request
{
get { return new FakeRequest(); }
}

public override HttpResponseBase Response
{
get { return new FakeResponse(); }
}
}

public class FakeRequest : HttpRequestBase
{
public override string ApplicationPath
{
get { return "/"; }
}
}

public class FakeResponse : HttpResponseBase
{
public override string ApplyAppPathModifier(string virtualPath)
{
return virtualPath;
}
}

You can also use the fake HttpContext if you want to be able to create URLs outside an ASP.NET application where HttpContext.Current is null.

In the application, I register my route and httpContext provider delegates like this:

public class MvcInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<Func<HttpContextBase>>()
.Instance(() => new HttpContextWrapper(HttpContext.Current)),
Component.For<Func<RouteCollection>>()
.Instance(() => RouteTable.Routes)
);
}
}

Posted by Mike Hadlow at 2:45 pm 3 comments
Labels:

Thursday, November 04, 2010

Using the Task Parallel Library with ASP.NET MVC for scalable web applications

Get the full sample solution from  GitHub: https://github.com/mikehadlow/Suteki.AsyncMvcTpl

Most applications spend most of their time doing IO operations. Communicating with a database or web service over the network or reading and writing files can take orders of magnitude longer than running instructions on the CPU. Web applications are no different, but have a additional complexity in that they are multi-threaded by nature, each request being served on a new thread. IIS only maintains a certain sized threadpool for handing requests and once all the threads are consumed, requests start to queue. You can mitigate this by doing asynchronous IO.

Using the new Task Parallel Library in .NET 4.0 makes building asynchronous controller actions much easier, and this post will show you how.

Here’s a common scenario. I have a controller action that makes a couple of calls to a service class:

[HttpGet]
public ViewResult Index()
{
 var user = userService.GetCurrentUser();
 userService.SendUserAMessage(user, "Hi From the MVC TPL experiment");
 return View(user);
}

The two methods I call on my UserService in turn make calls on two further classes:

public User GetCurrentUser()
{
 const int currentUserId = 10;
 return userRepository.GetUserById(currentUserId);
}
public void SendUserAMessage(User user, string message)
{
 emailService.SendEmail(user.Email, message);
}

Yes, this is an entirely contrived and simplified example, but the essentials are in place.

The UserRepository in turn uses ADO.NET to make a call to SQL Server to retrieve a row from the database:

public class UserRepository
{
 public User GetUserById(int currentUserId)
 {
 using (var connection = new SqlConnection("Data Source=localhost;Initial Catalog=AsncMvcTpl;Integrated Security=SSPI;"))
 {
 connection.Open();
 using (var command = connection.CreateCommand())
 {
 command.CommandText = "select * from [user] where Id = 10";
 command.CommandType = CommandType.Text;
 using (var reader = command.ExecuteReader())
 {
 if (reader.HasRows)
 {
 reader.Read();
 return new User((int)reader["Id"], (string)reader["Name"], (string)reader["Email"]);
 }
 throw new ApplicationException("No row with Id = 10 in user table");
 }
 }
 }
 }
}

The EmailService sends an email using the Standard System.Net.Mail.SmtpClient:

public void SendEmail(string emailAddress, string message)
{
 var mailMessage = new MailMessage("info@suteki.co.uk", emailAddress)
 {
 Subject = "Hello!",
 Body = "An important message from Nigeria :)"
 };
 var smtpClient = new SmtpClient("smtp.suteki.co.uk");
 smtpClient.Send(mailMessage);
}

Now, both the call to the database and the email dispatch are IO intensive. Most of the time, the IIS thread that the action executes on will be idle, waiting for first the database and then the email call to return. Even though the thread is mostly idle, it is still not available to process other requests. At some reasonable load, the requests will start to queue and eventually IIS will give up entirely and issue a 503 ‘server busy’ error.

However, both the SqlDataReader.ExecuteReader and the SmtpClient.Send methods have asynchronous versions. Under the bonnet these use operating system IO completion ports which means that the thread handling the request can be returned to the IIS threadpool while the IO operation completes.

The trick is to make sure that the entire stack from the action method down to the BCL call (the BeginExecuteReader and SendAsync calls) knows how to work asynchronously. Prior to the Task Parallel Library, this would have meant implementing BeginXXX and EndXXX methods at every layer (the Asynchronous Programming Model), so we would have had UserService.BeginGetCurrentUser and UserService.EndGetCurrentUser etc, and then on the UserRepository, BeginGetUserById / EndGetUserById. The code starts to look pretty formidable as well.

With the Task Parallel Library, we can represent any async operation as a Task or Task<T>. The first job is to wrap the BCL SqlDataReader and SmtpClient calls as tasks. For BCL classes that implement BeginXXX and EndXXX  methods we can just use the task factory’s FromAsync method, and that’s what we do here with the UserRepository’s GetUserById method:

public Task<User> GetUserById(int currentUserId)
{
 var connection =
 new SqlConnection("Data Source=localhost;Initial Catalog=AsncMvcTpl;Integrated Security=SSPI;Asynchronous Processing=true");
 connection.Open();
 var command = connection.CreateCommand();
 command.CommandText = "select * from [user] where Id = 10";
 command.CommandType = CommandType.Text;
 var readerTask = Task<SqlDataReader>.Factory.FromAsync(command.BeginExecuteReader, command.EndExecuteReader, null);
 return readerTask.ContinueWith(t =>
 {
 var reader = t.Result;
 try
 {
 if (reader.HasRows)
 {
 reader.Read();
 return new User((int)reader["Id"], (string)reader["Name"], (string)reader["Email"]);
 }
 throw new ApplicationException("No row with Id = 10 in user table");
 }
 finally 
 {
 reader.Dispose();
 command.Dispose();
 connection.Dispose();
 }
 });
}

Now GetUserById returns a Task<User> instead of a User. Effectively we are saying that the caller will get a user at some point in the future.

Once the reader has been wrapped in a task we can consume it with a continuation supplied to the ContinueWith method. The continuation can return a value, in our case a User, that then pops out of the ContinueWith method as the promise of a future user; a Task<User>.

SmtpClient doesn’t use APM, so turning its async operation into a task is a little more tricky, luckily this has already been done for us with the TPL team’s Parallel Extensions Extras library. I’m going to use that library’s SendTask extension method here:

public Task SendEmail(string emailAddress, string message)
{
 var mailMessage = new MailMessage("info@suteki.co.uk", emailAddress)
 {
 Subject = "Hello!",
 Body = "An important message :)"
 };
 var smtpClient = new SmtpClient("smtp.suteki.co.uk");
 return smtpClient.SendTask(mailMessage, null);
}

Because the synchronous version of this method returned void, we simply return Task here.

Now the power of the TPL really starts to work for us. We hardly have to change our UserService methods at all:

public Task<User> GetCurrentUser()
{
 const int currentUserId = 10;
 return userRepository.GetUserById(currentUserId);
}
public Task SendUserAMessage(User user, string message)
{
 return emailService.SendEmail(user.Email, message);
}

We are simply passing Task<User> and Task back up the stack, no need to implement Begin/End methods. This is very powerful.

Finally we come to the action method. This is where the code gets gnarly again. We have leave the lovely world of TPL and shoehorn it into the crappy MVC async controller:

public class HomeController : AsyncController
{
 readonly UserService userService = new UserService();
 [HttpGet]
 public void IndexAsync()
 {
 AsyncManager.OutstandingOperations.Increment();
 userService.GetCurrentUser().ContinueWith(t1 =>
 {
 var user = t1.Result;
 userService.SendUserAMessage(user, "Hi From the MVC TPL experiment").ContinueWith(t2 =>
 {
 AsyncManager.Parameters["user"] = user;
 AsyncManager.OutstandingOperations.Decrement();
 });
 });
 
 }
 public ViewResult IndexCompleted(User user)
 {
 return View(user);
 }
}

Jeff Prosise has a very nice post about the ASP.NET MVC async controller. You’d probably want to read that first. But simply put, you inherit from AsyncController and rather than calling your action ‘Index’ you split it into two and call it IndexAsync and IndexCompleted, the router understands this convention and correctly routes Home/Index to the IndexAsync action.

AsyncController has an AsyncManager that keeps track of async callbacks using its Increment/Decrement methods. When the count gets back to zero IndexCompleted is called. You can marshal state to the completed method using the AsyncManager’s Parameters dictionary as shown.

Once again we are using ContinueWith to supply continuations to grab the result of GetCurrentUser and then wait for the SendUserAMessage to complete.

Note that I haven’t considered exception handling in this example. You need to be very careful that you catch and notify exceptions that happen in async operations.

Now for my gripe. It would have been really slick if the MVC team had used TPL to implement async controller methods. I should be able to keep my single Index action but return a Task<ViewResult> instead. My async Index action would then look like this:

[HttpGet]
public Task<ViewResult> Index()
{
 return from user in userService.GetCurrentUser()
 from _ in userService.SendUserAMessage(user, "Hi From the MVC TPL experiment")
 select View(user);
}

Update: Craig Cav has implemented an async controller that does just this. You can read his post about it here. He’s branched my example and demonstrates the Task based async controller here.

(削除) Oh yes, tasks are monadic so you can compose them with Linq (削除ここまで). No they are not, but the Linq extensions can be found in the TaksParallelExtensions library. See my post here for more info.

Should I care about this?

As with most scalability optimisations this makes your code more complex, harder to understand and harder to debug. I’ve been happily building web applications without doing this for years. You should only ever use this technique if you have an immediate scalability concern and you know that it is caused by a threadpool full of threads blocked by long running IO operations.

Having said that, if you do have these kinds of issues, then using the TPL like this makes them easier to solve than the older being/end spaghetti that you previously would have had to write.

It’s also worth noting that doing this kind of thing will become easier still with C#5’s new async operator. At that point it might we worth doing this kind of async IO as a matter of course.

Posted by Mike Hadlow at 12:26 pm 10 comments

Wednesday, October 27, 2010

RavenDb, ASP.NET MVC and Windsor working together

I’m putting together a new project, TardisBank.com, using RavenDb, ASP.NET MVC and Windsor. It’s up on github here:

http://github.com/mikehadlow/Suteki.TardisBank

Here’s how I wire up Windsor and RavenDb.

First, in Global.asax.cs I initialise the container and ask it to load and run installers from all the application assemblies:

public class MvcApplication : System.Web.HttpApplication, IContainerAccessor
{
 ...
 protected void Application_Start()
 {
 AreaRegistration.RegisterAllAreas();
 RegisterGlobalFilters(GlobalFilters.Filters);
 RegisterRoutes(RouteTable.Routes);
 InitialiseContainer();
 InitialiseControllerFactory();
 }
 protected void InitialiseContainer()
 {
 if (container == null)
 {
 container = new WindsorContainer()
 .Install(
 FromAssembly.InDirectory(new AssemblyFilter(HttpRuntime.BinDirectory, "Suteki.*.dll")));
 }
 }
 protected void InitialiseControllerFactory()
 {
 var controllerFactory = container.Resolve<IControllerFactory>();
 ControllerBuilder.Current.SetControllerFactory(controllerFactory);
 }
 static IWindsorContainer container;
 public IWindsorContainer Container
 {
 get { return container; }
 }
}

One of the installers it runs is my RavenInstaller:

public class RavenInstaller : IWindsorInstaller
{
 public void Install(IWindsorContainer container, IConfigurationStore store)
 {
 container.Register(
 Component.For<IDocumentStore>().Instance(CreateDocumentStore()).LifeStyle.Singleton,
 Component.For<IDocumentSession>().UsingFactoryMethod(GetDocumentSesssion).LifeStyle.PerWebRequest
 );
 }
 static IDocumentStore CreateDocumentStore()
 {
 var store = new DocumentStore
 {
 ConnectionStringName = "tardisConnection"
 };
 store.Initialize();
 return store;
 }
 static IDocumentSession GetDocumentSesssion(IKernel kernel)
 {
 var store = kernel.Resolve<IDocumentStore>();
 return store.OpenSession();
 }
}

Because Windsor already provides a global application store for both singleton and per-request components, we can give it the responsibility of handling the instantiation and lifecycle of the DocumentStore and DocumentSesssion. The DocumentStore is created and initialised once the first time it is requested. The DocumentSession has a PerWebRequest lifestyle and is created using a factory method the first time it is requested.

Don’t forget to register the PerRequestLifestyleModule in Web.config:

<system.webServer>
 <validation validateIntegratedModeConfiguration="false"/>
 
 <modules runAllManagedModulesForAllRequests="true">
 <add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
 </modules>
</system.webServer>

Windsor will properly dispose both the DocumentStore when the application closes and the DocumentSession at the end of each request.

Posted by Mike Hadlow at 9:25 am 6 comments

Friday, September 17, 2010

Implementing a ‘Money’ type in an ASP.NET MVC and NHibernate application.

Years ago I read Kent Beck’s seminal Test Driven Development. The first third of the book is a complete worked example of building a Currency type using TDD and I remember thinking at the time that this was an incredibly powerful technique. Rather than using basic data types -  int, string, DateTime - we could define what we actually meant. Rather than having a property Age of type int, we would define an Age type. Rather than two string properties we could define a Name type. One of the factors in moving Suteki Shop from Linq-to-SQL to NHibernate was NHibernate’s support for more complex finer-grained mapping.

When I first hacked together Suteki Shop, I used the decimal type everywhere I needed to represent money. This has mostly worked well for the simple scenario where a shop only has a single currency and that currency is sterling (£). But anyone wanting to sell in US dollars had to find every one of the many instances of “£” and replace them with “$”, and if you wanted to do something fancy with multiple currencies, you would have been out of luck. What I needed was a Money type. It’s not a trivial refactoring. Here are the steps I needed to take:

  1. Create a Money type that behaved as far as possible like a .NET numeric type.
  2. Create an ASP.NET MVC IModelBinder that knew how to bind the Money type.
  3. Create an NHibernate IUserType that knew how to persist the Money type.
  4. Change every point in the code that was using decimal to represent money to use the Money type instead.

Create a Money type

I wanted to create a Money type that would behave nicely when I did arithmetic with it. So I wanted to be able to write expression like this:

var x = new Money(43.5M);
var result = x * 3 + x/2 - 4;
result.Amount.ShouldEqual(148.25M);

To achieve this you have to write a lot of operator overloads for all combinations of operator, Money <-> Money, Money <-> decimal and decimal <-> Money. I won’t bore you, if you want to see the gory details, you can view the code here.

I also wanted my Money type to include the currency symbol when ToString was invoked, so I would get “£12.54” rather than “12.54”, but I found that this caused lots of problems with HTML forms and the default behaviour of the MVC HtmlHelper extensions. In the end I left ToString returning the number without the currency symbol and implemented a new method ToStringWithSymbol with it included. It feels like a horrible hack though.

I also overrode the equality operator, GetHashCode and Equals so make Money behave as expected in equality operations.

public class Money
{
 public decimal Amount { get; private set; }
 public Money(decimal amount)
 {
 Amount = amount;
 }
 public static string Symbol
 {
 get { return "£"; }
 }
 public static Money Zero
 {
 get { return new Money(0M);}
 }
 public override string ToString()
 {
 return Amount.ToString("0.00");
 }
 public string ToStringWithSymbol()
 {
 return Amount.ToString(Symbol + "0.00");
 }
 public override int GetHashCode()
 {
 return Amount.GetHashCode();
 }
 public override bool Equals(object obj)
 {
 var otherMoney = obj as Money;
 if (otherMoney == null) return false;
 return Amount.Equals(otherMoney.Amount);
 }
 public bool Equals(Money otherMoney)
 {
 if (otherMoney == null) return false;
 return Amount.Equals(otherMoney.Amount);
 }
 public static bool operator ==(Money a, Money b)
 {
 // If both are null, or both are same instance, return true.
 if (ReferenceEquals(a, b))
 {
 return true;
 }
 // If one is null, but not both, return false.
 if (((object)a == null) || ((object)b == null))
 {
 return false;
 }
 return a.Amount == b.Amount;
 }
 public static bool operator !=(Money a, Money b)
 {
 return !(a == b);
 }
 
 .....
}

Create an ASP.NET MVC IModelBinder

I wanted any of my forms that posted money values to work seamlessly when binding to entities with Money properties. For this to work I had to implement an IModelBinder for Money.

Creating the ModelBinder was pretty straightforward since I was just binding to a simple type.

public class MoneyBinder : IModelBinder
{
 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
 {
 if (!typeof (Money).IsAssignableFrom(bindingContext.ModelType))
 {
 throw new SutekiCommonException(
 "MoneyBinder has attempted to bind to type '{0}', but may only bind to Money",
 bindingContext.ModelType);
 }
 var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
 try
 {
 var decimalValue = (decimal)valueProviderResult.ConvertTo(typeof(decimal));
 return new Money(decimalValue);
 }
 catch (Exception exception)
 {
 bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Not a valid price.");
 return null;
 }
 }
}

But it could have been much easier. The IModelBinder interface doesn’t make it easy to work out how you are supposed to do simple binding. A SimpleModelBinder<T> with an abstract method like: T BindSimpleModel(string value) would have been nice.

The MoneyBinder is registered as a binder for the Money type in Global.asax.cs:

ModelBinders.Binders.Add(typeof(Money), new MoneyBinder());

Create an NHibernate IUserType

I wanted any entity that had a property of type Money to get automatically persisted by NHibernate as a numeric value. To do this I needed an IUserType. This tells NHibernate how to persist your custom type.

To create the NHibernate IUserType I borrowed Darrell Mozingo’s excellent BaseImmutableUserType<T>. With this it’s fairly straightforward:

public class MoneyUserType : BaseImmutableUserType<Money>
{
 public override object NullSafeGet(IDataReader rs, string[] names, object owner)
 {
 var amount = ((decimal?)NHibernateUtil.Decimal.NullSafeGet(rs, names[0]));
 return amount.HasValue ? new Money(amount.Value) : Money.Zero;
 }
 public override void NullSafeSet(IDbCommand cmd, object value, int index)
 {
 var moneyObject = value as Money;
 object valueToSet;
 if (moneyObject != null)
 {
 valueToSet = moneyObject.Amount;
 }
 else
 {
 valueToSet = DBNull.Value;
 }
 NHibernateUtil.Decimal.NullSafeSet(cmd, valueToSet, index);
 }
 public override SqlType[] SqlTypes
 {
 get
 {
 return new[]
 {
 SqlTypeFactory.Decimal
 };
 }
 }
}

To register the MoneyUserType I just added a convention to my Fluent NHibernate setup:

public class FluentNHibernateConfigurationBuilder : IConfigurationBuilder
{
 ....
 public static void ConfigureMappings(MappingConfiguration mappingConfiguration)
 {
 mappingConfiguration.FluentMappings
 .AddFromAssembly(typeof (ProductMap).Assembly)
 .Conventions.Add(
 ForeignKey.EndsWith("Id"),
 PrimaryKey.Name.Is(x => x.EntityType.Name + "Id"),
 DefaultCascade.None(),
 new MoneyConvention());
 }
}
public class MoneyConvention : UserTypeConvention<MoneyUserType>{}

Change every point in the code that was using decimal to represent money to use the Money type instead

I had naively hoped that a good enough Money class would allow me to just change the type of money properties from decimal to Money and have done with it. But even with the operator overloading I found that there were many places where I needed to add ‘.Amount’ to make the code behave. I could have implemented an implicit cast, but I don’t like not knowing where types are being converted. I’ve found in the past that it can lead to subtle, hard to find, bugs.

What I have now is a single point where I can change the way money behaves in Suteki Shop. Providing multi-currency or changing the behaviour of money in any way should be far easier. And if you simply want to take Suteki Shop and sell in Dollars, Yen or Euros, it’s now just a single line change to the code. And yes, configurable currencies will come very soon.

Some thoughts on the C# type system

This was far more work than it needs to be and is a clear example of the deficiencies of the C# type system. With Haskell’s type classes or Scalar’s implicits it would have been possible to create a new numeric type without having the change ripple through the entire application. The Scalar implicit is a very interesting idea and would surely be a very cool addition to the C# type system. Anders?

Posted by Mike Hadlow at 12:53 pm 7 comments

Tuesday, April 06, 2010

A Custom ASP.NET MVC Model Binder for Repositories

How do you take the values posted by an HTML form and turn them into a populated domain entity? One popular technique is to bind the POST values to a view-model and then map the view-model values to an entity. Since your action method’s argument is the view-model, it allows you to decide in the controller code if the view-model is a new entity or an existing one that should be retrieved from the database. If the view-model represents a new entity you can directly create the entity from the view-model values and then call your repository in order to save it.  In the update case, you can directly call your repository to get a specific entity and then update the entity from the values in the view-model.

However, this method is somewhat tedious for simple cases. Is a view-model always necessary? Wouldn’t it be simpler to have a model binder that simply created the entity for you directly? Here’s my attempt at such a binder:

updated to fix bug when a child entity is changed

public class EntityModelBinder : DefaultModelBinder, IAcceptsAttribute
{
 readonly IRepositoryResolver repositoryResolver;
 EntityBindAttribute declaringAttribute;
 public EntityModelBinder(IRepositoryResolver repositoryResolver)
 {
 this.repositoryResolver = repositoryResolver;
 }
 protected override object CreateModel(
 ControllerContext controllerContext, 
 ModelBindingContext bindingContext, 
 Type modelType)
 {
 if (modelType.IsEntity() && FetchFromRepository)
 {
 var id = GetIdFromValueProvider(bindingContext, modelType);
 if (id.HasValue && id.Value != 0)
 {
 var repository = repositoryResolver.GetRepository(modelType);
 object entity;
 try
 {
 entity = repository.GetById(id.Value);
 }
 finally
 {
 repositoryResolver.Release(repository);
 }
 return entity;
 }
 }
 // Fall back to default model creation if the target is not an existing entity
 return base.CreateModel(controllerContext, bindingContext, modelType);
 }
 protected override object GetPropertyValue(
 ControllerContext controllerContext, 
 ModelBindingContext bindingContext, 
 System.ComponentModel.PropertyDescriptor propertyDescriptor, 
 IModelBinder propertyBinder)
 {
 // any child entity property which has been changed, needs to be retrieved by calling CreateModel, 
 // we can force BindComplexModel (in DefaultModelBinder) do this by
 // setting the bindingContext.ModelMetadata.Model to null
 var entity = bindingContext.ModelMetadata.Model as IEntity;
 if (entity != null)
 {
 var id = GetIdFromValueProvider(bindingContext, bindingContext.ModelType);
 if (id.HasValue && id.Value != entity.Id)
 {
 bindingContext.ModelMetadata.Model = null;
 }
 }
 return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
 }
 private static int? GetIdFromValueProvider(ModelBindingContext bindingContext, Type modelType)
 {
 var fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, modelType.GetPrimaryKey().Name);
 if (!bindingContext.ValueProvider.ContainsPrefix(fullPropertyKey))
 {
 return null;
 }
 var result = bindingContext.ValueProvider.GetValue(fullPropertyKey);
 if (result == null) return null;
 var idAsObject = result.ConvertTo(typeof (Int32));
 if (idAsObject == null) return null;
 return (int) idAsObject;
 }
 public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
 {
 bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
 var model = base.BindModel(controllerContext, bindingContext);
 ValidateEntity(bindingContext, controllerContext, model);
 return model;
 }
 protected virtual void ValidateEntity(
 ModelBindingContext bindingContext, 
 ControllerContext controllerContext, 
 object entity)
 {
 // override to provide additional validation.
 }
 private bool FetchFromRepository
 {
 get
 {
 // by default we always fetch any model that implements IEntity
 return declaringAttribute == null ? true : declaringAttribute.Fetch;
 }
 }
 public virtual void Accept(Attribute attribute)
 {
 declaringAttribute = (EntityBindAttribute)attribute; 
 }
 // For unit tests
 public void SetModelBinderDictionary(ModelBinderDictionary modelBinderDictionary)
 {
 Binders = modelBinderDictionary;
 }
}

I’ve simply inherited ASP.NET MVC’s DefaultModelBinder and overriden the CreateModel method. This allows me to check if the type being bound is one of my entities and then grabs its repository and gets it from the database if it is.

Now, I’m most definitely not doing correct Domain Driven Development here despite my use of terms like ‘entity’ and ‘repository’. It’s generally frowned on to have table-row like settable properties and generic repositories. If you want to do DDD, you are much better off only binding view-models to your views.

Posted by Mike Hadlow at 9:13 am 5 comments
Labels:

Friday, February 12, 2010

In praise of ELMAH

Do you have an ASP.NET application? Do you want to record any errors it might throw? Do you want an easy way to view the errors and possibly get them emailed, or even tweeted to you? Look no further ….

ELMAH stands for ‘Error Logging Modules and Handlers’. It’s an incredibly easy to use but full featured set of error logging bolt-on bits for ASP.NET. The committers list on the project page reads like a who’s who of .NET, so you can’t argue that it’s not well supported. The documentation is pretty good so I’m not going to try and give a blow-by-blow account of how to set it up, I just want to show how I’ve used it in Suteki Shop and how easy and simple it is to get up and running.

The first thing to note about ELMAH is that it requires zero change to your application’s code base. The only thing you might want to do is remove any existing error loggin. In fact the best application to use ELMAH with, is one where errors are simply allowed to bubble up to the ‘yellow screen of death’. Don’t forget: The first rule of exception handling: do not do exception handling.

In Suteki Shop I’d previously used the MVC Framework’s default Rescue behaviour, so the only real code change I made was to remove the [Rescue("Default")] attribute from my ControllerBase class. I also had to add the ELMAH assembly to my dependencies folder. But most of the work consists of changes to Web.config.

First I added a new config section, then the ELMAH UI endpoint httpHandler that shows the error log. This is so I can type http://sutekishop.co.uk/elmah/elmah.axd and view any errors that Suteki Shop may throw (no it won’t work for you):

<add verb="POST,GET,HEAD" path="/elmah/elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />

Update: I was contacted by Phil Haselden shortly after I wrote this post, pointing me to this stack overflow answer:
http://stackoverflow.com/questions/1245364/securing-elmah-in-asp-net-website
You need to secure the path to ‘elmah.axd’ by making it a child of a ‘virtual’ directory, which in my case I’ve named ‘elmah’, and then only allowing admin users access to that directory. If you don’t do this and simply secure ‘elmah.axd’, it’s possible for a user to view your ELMAH page with a URL like http://sutekishop.co.uk/xxx/elmah.axd. I’ve updated this post now, so if you follow these instructions you should be fine.

Next I added the ELMAH modules I required. I wanted to receive error notifications as tweets so I needed the core error log and the ErrorTweetModule:

<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="ErrorTweet" type="Elmah.ErrorTweetModule, Elmah"/>

The actual ELMAH configuration itself is done in the elmah config secion:

<elmah>
 <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/App_Data" />
 <security allowRemoteAccess="1" />
 <errorTweet userName="sutekishop" password="sutekishops_password" 
 statusFormat="d @mikehadlow {Message}" />
</elmah>

You have to specify where you want the log to be stored - you can choose to put the log in a database, but I’m happy to have it written to an xml file in App_Data. I’ve also configured that I want to allow remote access so that I can view the log remotely.

To configure the twitter client I simply created a twitter account named ‘sutekishop’  and specified its user name, password and the format of the error message I want to send. Here I’m sending myself DMs of any error.

Lastly I configured the standard ASP.NET security so that only the admin account on suteki shop is able to view the elmah.axd page:

<location path="elmah">
 <system.web>
 <authorization>
 <allow users="the_admin_user"/>
 <deny users="*" />
 </authorization>
 </system.web>
</location>

This means that if you try browsing to http://sutekishop.co.uk/elmah.axd you’ll be thrown back to the login page. If you could login as the administrator you would see this page:

[画像:elmah]

Hmm, looks like a robot’s been doing some fishing and a user who’s session has expired is trying to go back and view their order. I probably shouldn’t throw for that, but return a nice information page instead.

The great thing is that I also get all these errors as tweets as well:

[画像:elmahTweet]

ELMAH is just so easy to configure and so easy to use that it seems the obvious first choice for error logging in any ASP.NET application.

Posted by Mike Hadlow at 10:36 pm 2 comments

Friday, November 28, 2008

Multi-tenancy part 2: Components and Context

In my previous post, Multi-tenancy part 1: Strategy, I talked about some of the high level decisions we have to make when building a single software product for multiple users with diverse requirements. Today I'm going to look at implementing basic multi-tenancy with Suteki Shop. I'm going to assume that my customers have identical functional requirements, but will obviously need to have separate styles and databases. Some other simple configurable items will also be different, such as the name of their company and their contact email address.

But first I want to suggest something that's become quite clear as I've started to think more about implementing multi-tenancy. I call it:

Hadlow's first law of multi-tenancy: A multi-tenanted application should not look like a multi-tenanted application.

What do I mean by this? The idea is that you should not have to change your existing single-tenant application in any way in order to have it serve multiple clients. If you build your application using SOLID principles, if you architect it as a collection of components using Dependency Injection and an IoC container, then you should be able to compose the components at runtime based on some kind of user context without changing the components themselves.

I am going to get Suteki Shop to serve two different tenants without changing a single existing line of of (component) code.

We are going to be serving two clients. The first one is our existing client, the mod-tastic Jump the Gun. I've invented the new client zanywear.com. I just made the name up, it's actually a registered domain name but it's not being used. We're going to serve our clients from the same application instance, so we create a new web site and point it to an instance of Suteki Shop. Now we configure two host headers (AKA Site Bindings) for the web application:

test.jumpthegun.co.uk
zanywear.com

multitenanted-iis

For testing purposes (and because I don't own zanywear.com :) I've added the two domains to my C:\WINDOWS\system32\drivers\etc\hosts file so that it looks like this:

# Copyright (c) 1993-1999 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
127.0.0.1 localhost
127.0.0.1 test.jumpthegun.co.uk
127.0.0.1 zanywear.com

Now when I browse to test.jumpthegun.co.uk or zanywear.com I see the Jump the Gun website.

The task now is to choose a different database, style-sheet and some basic configuration settings when the HTTP request's host name is zanywear.com. Conventiently Suteki Shop has two services that define these items. The first is IConnectionStringProvider which provides (you guessed it) the database connection string:

namespace Suteki.Common.Repositories
{
 public interface IConnectionStringProvider
 {
 string ConnectionString { get; }
 }
}

And the other is the somewhat badly named IBaseControllerService that supplies some repositories and config values to be consumed by the master view:

using Suteki.Common.Repositories;
namespace Suteki.Shop.Services
{
 public interface IBaseControllerService
 {
 IRepository<Category> CategoryRepository { get; }
 IRepository<Content> ContentRepository { get; }
 string GoogleTrackingCode { get; set; }
 string ShopName { get; set; }
 string EmailAddress { get; set; }
 string SiteUrl { get; }
 string MetaDescription { get; set; }
 string Copyright { get; set; }
 string PhoneNumber { get; set; }
 string SiteCss { get; set; }
 }
}

Note that this allows us to to set the name of the style-sheet and some basic information about the shop.

In order to choose which component is used to satisfy a service at runtime we use an IHandlerSelector. This interface was recently introduced to the Windsor IoC container by Oren Eini (AKA Ayende Rahien) specifically to satisfy the requirements of multi-tenanted applications. You need to compile the trunk if you want to use it. It's not in the release candidate. It looks like this:

using System;
namespace Castle.MicroKernel
{
 /// <summary>
 /// Implementors of this interface allow to extend the way the container perform
 /// component resolution based on some application specific business logic.
 /// </summary>
 /// <remarks>
 /// This is the sibling interface to <seealso cref="ISubDependencyResolver"/>.
 /// This is dealing strictly with root components, while the <seealso cref="ISubDependencyResolver"/> is dealing with
 /// dependent components.
 /// </remarks>
 public interface IHandlerSelector
 {
 /// <summary>
 /// Whatever the selector has an opinion about resolving a component with the 
 /// specified service and key.
 /// </summary>
 /// <param name="key">The service key - can be null</param>
 /// <param name="service">The service interface that we want to resolve</param>
 bool HasOpinionAbout(string key, Type service);
 /// <summary>
 /// Select the appropriate handler from the list of defined handlers.
 /// The returned handler should be a member from the <paramref name="handlers"/> array.
 /// </summary>
 /// <param name="key">The service key - can be null</param>
 /// <param name="service">The service interface that we want to resolve</param>
 /// <param name="handlers">The defined handlers</param>
 /// <returns>The selected handler, or null</returns>
 IHandler SelectHandler(string key, Type service, IHandler[] handlers);
 }
}

The comments are self explanatory. I've implemented the interface as a HostBasedComponentSelector that can choose components based on the HTTP request's SERVER_NAME value:

using System;
using System.Linq;
using System.Web;
using Castle.MicroKernel;
using Suteki.Common.Extensions;
namespace Suteki.Common.Windsor
{
 public class HostBasedComponentSelector : IHandlerSelector
 {
 private readonly Type[] selectableTypes;
 public HostBasedComponentSelector(params Type[] selectableTypes)
 {
 this.selectableTypes = selectableTypes;
 }
 public bool HasOpinionAbout(string key, Type service)
 {
 foreach (var type in selectableTypes)
 {
 if(service == type) return true;
 }
 return false;
 }
 public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
 {
 var id = string.Format("{0}:{1}", service.Name, GetHostname());
 var selectedHandler = handlers.Where(h => h.ComponentModel.Name == id).FirstOrDefault() ??
 GetDefaultHandler(service, handlers);
 return selectedHandler;
 }
 private IHandler GetDefaultHandler(Type service, IHandler[] handlers)
 {
 if (handlers.Length == 0)
 {
 throw new ApplicationException("No components registered for service {0}".With(service.Name));
 }
 return handlers[0];
 }
 protected string GetHostname()
 {
 return HttpContext.Current.Request.ServerVariables["SERVER_NAME"];
 }
 }
}

It works like this: It expects an array of types to be supplied as constructor arguments. These are the service types that we want to choose based on the host name. The HasOpinionAbout method simply checks the supplied serivce type against the array of types and returns true if there are any matches. If we have an opinion about a service type the container will ask the IHandlerSelector to supply a handler by calling the SelectHandler method. We create an id by concatenating the service name with the host name and then return the component that's configured with that id. So the configuration for Jump the Gun's IConnectionStringProvider will look like this:

<component
 id="IConnectionStringProvider:test.jumpthegun.co.uk"
 service="Suteki.Common.Repositories.IConnectionStringProvider, Suteki.Common"
 type="Suteki.Common.Repositories.ConnectionStringProvider, Suteki.Common"
 lifestyle="transient">
 <parameters>
 <ConnectionString>Data Source=.\SQLEXPRESS;Initial Catalog=JumpTheGun;Integrated Security=True</ConnectionString>
 </parameters>
</component>

Note the id is <name of service>:<host name>.

The configuration for Zanywear looks like this:

<component
 id="IConnectionStringProvider:zanywear.com"
 service="Suteki.Common.Repositories.IConnectionStringProvider, Suteki.Common"
 type="Suteki.Common.Repositories.ConnectionStringProvider, Suteki.Common"
 lifestyle="transient">
 <parameters>
 <ConnectionString>Data Source=.\SQLEXPRESS;Initial Catalog=Zanywear;Integrated Security=True</ConnectionString>
 </parameters>
</component>

Note that you can have multiple configurations for the same service/component in Windsor so long as ids are different.

When the host name is test.jumpthegun.co.uk the HostBasedComponentSelector will create a new instance of ConnectionStringProvider with a connection string that points to the JumpTheGun database. When the host name is zanywear.com it will create a new instance of ConnectionStringProvider with a connection string that points to the Zanywear database. We configure our IBaseControllerService in a similar way.

The only thing left to do is register our IHandlerSelector with the container. When I said I didn't have to change a single line of code I was telling a fib, we do have to change the windsor initialization to include this:

protected virtual void InitializeWindsor()
{
 if (container == null)
 {
 // create a new Windsor Container
 container = new WindsorContainer(new XmlInterpreter("Configuration\\Windsor.config"));
 // register handler selectors
 RegisterHandlerSelectors(container);
 // register WCF integration
 RegisterWCFIntegration(container);
 // automatically register controllers
 container.Register(AllTypes
 .Of<Controller>()
 .FromAssembly(Assembly.GetExecutingAssembly())
 .Configure(c => c.LifeStyle.Transient.Named(c.Implementation.Name.ToLower())));
 // set the controller factory to the Windsor controller factory (in MVC Contrib)
 System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
 }
}
/// <summary>
/// Get any configured IHandlerSelectors and register them.
/// </summary>
/// <param name="windsorContainer"></param>
protected virtual void RegisterHandlerSelectors(IWindsorContainer windsorContainer)
{
 var handlerSelectors = windsorContainer.ResolveAll<IHandlerSelector>();
 foreach (var handlerSelector in handlerSelectors)
 {
 windsorContainer.Kernel.AddHandlerSelector(handlerSelector);
 }
}

The handler selector setup occurs in the RegisterHandlerSelectors method. We simply ask the container to resolve any configured IHandlerSelectors and add them in. The configuration for our HostBasedComponentSelector looks like this:

<component
 id="urlbased.handlerselector"
 service="Castle.MicroKernel.IHandlerSelector, Castle.MicroKernel"
 type="Suteki.Common.Windsor.HostBasedComponentSelector, Suteki.Common"
 lifestyle="transient">
 <paramters>
 <selectableTypes>
 <array>
 <item>Suteki.Shop.Services.IBaseControllerService, Suteki.Shop</item>
 <item>Suteki.Common.Repositories.IConnectionStringProvider, Suteki.Common</item>
 </array>
 </selectableTypes>
 </paramters>
</component>

Note that we are configuring the list of services that we want to be selected by the HostBasedHandlerSelector by using the array parameter configuration syntax.

And that's it. We now have a single instance of Suteki Shop serving two different clients: Jump the Gun and Zanywear.

[画像:multitenanted-websites]

Today I've demonstrated the simplest case of multi-tenanting. It hardly qualifies as such because our two tenants both have identical requirements. The core message here is that we didn't need to change a single line of code in any of our existing components. You can still install Suteki Shop and run it as a single-tenant application by default.

In the next installment I want to show how we can provide different implementations of components using this approach. Later I'll deal with the more complex problem of variable domain models. Watch this space!

Posted by Mike Hadlow at 2:22 pm 9 comments
Subscribe to: Posts (Atom)
 

AltStyle によって変換されたページ (->オリジナル) /