Friday, December 14, 2012 #

Sharing DataAnnotations between the UI and the business logic

Along with with .Net version 4.0 came DataAnnotations which is a library for validating properties in classes. The library consists of validation-attributes, core classes and interfaces for validating properties. Using DataAnnotations to validate the UI is modern, elegant and is understood straight out of the box by Microsoft frameworks such as ASP.Net MVC and WPF, as long as the platform is .Net 4.0 or above. Now, wouldn’t it be nice if we could extend the usage of DataAnnotations and annotate the domain models within the business logic, sharing the same annotations? This would provide a common set of annotated validation rules and simplifying validation. And, when used in conjunction with Entity Framework Code First, we are also effectively validating the data access.

DataAnnotations in the UI

A common scenario is to validate forms in web pages with dataannotated properties and give feedback to the client whenever a validation fails. This approach is very elegant because very little code must be written in order to achieve validation. Also, when using a viewmodel for the view, the annotation is done on the viewmodel, not the view, which loosens the coupling between the view and the viewmodel.

The recipe is very simple:

  • Annotate the property:

    image

  • Bind the view to the property:

    image
    (Using ASP.Net MVC 4 application)

  • The result:

    image

 

How to share dataannotations?

Now, using dataannotations on the client side is fairly straight forward, there are plenty of documentation describing how to do this. There are some differences in the binding mechanism and view validation depending on the UI framework, but the annotation of the properties are the same. Of course, in an application there is also business logic with domain models and most often a repository for storing data. The repositories and domain models need also validate input coming from outside the bounded context (from DDD). Often, but not always the validation rules are the same in UI as in the business logic. One could of course have separate validation definitions, one in the UI and one in the business logic, this is would enforce loose coupling and layered architecture, but would mean duplicating the validation rules. Another approach would be to skip the layering all together and use domain models as viewmodels, but this would lead to high coupling and breaking separated patterns such as MVVM, MVC, MVP and other variants.

The solution is to declare the annotations in a separate solution-project (type class library), we could then use the same DataAnnotations in multiple context boundaries and still enforce the architecture.

image

Now lets say we have a Customer domain model and a viewmodel RegisterCustomerVm for registering customers. Also, they both have a property Name which also have the same validation rule. Further, lets say the rule is that name is required and that the length of the name must not exceed 100 characters.

Since we are sharing DataAnnotations, we must define custom ValidationAttributes in the shared visual studio project. We cannot annotate the built-in attributes directly on the properties of view models or the domain models, the rules must be defined one place, and one place only.

So, declaring the above validation rule can be done like this:

public class CustomerNameAttribute : ValidationAttribute
{
        private string _msg;
        private const int MaximumCharacters = 100;
 
        public override bool IsValid(object value)
        {
            var strValue = value as string;
            if (string.IsNullOrEmpty(strValue))
            {
                _msg = "Name is required.";
                return false;
            }
 
            if (strValue.Length > MaximumCharacters)
            {
                _msg = string.Format("Must contain less than {0} characters.", MaximumCharacters);
                return false;
            }
 
            return true;
        }
 
        public override string FormatErrorMessage(string name)
        {
            return _msg;
        }
}
 
When declaring the validation rule we must inherit the attribute from VaidationAttribute, override IsValid() and FormatErrorMessage(). We have to override the FormatMessage() in order to create custom error messages. IsValid(), will be called by the framework when validation occurs. Unfortunately, there are no generic implementation of ValidationAttribute, so we have to cast the object parameter in IsValid().
 
 
To apply the rule on the view model is the simple:
 
public class RegisterCustomerVm
{
    [CustomerName]
    public string Name { get; set; }
}
 
Failing to oblique the validation rule leads to:
 
image
 
Or:
 
image

 

DataAnnotating the domain models

Validating the user interface is simple, but annotating the domain models is actually even more simple and elegant. In an object-oriented manner we can now validate the domain models in the construction of the class. Of course, there are different approaches and techniques one could use, but in this example demonstrates the basic technique.

With this technique, we shall use constructors to encapsulate our domain models. For the Customer domain model, it has only one parameter, called Name. The class looks like this:

public class Customer
{
    public int Id { get; private set; }
 
    [CustomerName]
    public string Name { get; private set; }
 
    public Customer(string name)
    {
        Name = name;
    }
}

Note that the properties have private setters, so that encapsulation will be enforced. Also, there is a property Id, if you are using EF Code First, the id of the entity will be generated for us, so we don’t have to create an id in the constructor.

As the code stands now, no validation will occur, so we have to validate the validation-attribute when constructing. This can be done by using the ValidateObject() method in the core library of DataAnnotation, located in the DataAnnotations.Validations assembly:
 
public Customer(string name)
{
    Name = name;
 
    Validator.ValidateObject(this, new ValidationContext(this), true);
}
 
The ValidateObject method will look for attributes of type ValidationAttribute in the specified object. In this case, it will find that the Name property of Customer has a matching attribute. Next, the ValidateObject will call IsValid() in the matching ValidationAttributes. Finally, if IsValid() fails, it will throw a ValidationException.
 
 
In order to get a small footprint in the domain models, we can define a simple extension method on object:
 
public static class ObjectExtension
{
    public static void Validate(this object obj)
    {
        Validator.ValidateObject(obj, new ValidationContext(obj), true);
    }
}
 
The Customer domain model is now complete with validation:
 
public class Customer
{
    public int Id { get; private set; }
 
    [CustomerName]
    public string Name { get; private set; }
 
    public Customer(string name)
    {
        Name = name;
        this.Validate();
    }
}

Now this is really a small footprint of the validation, it’s object-oriented and elegant! Open-mouthed smile

 

Unit-Testing that the validation rules are enforced on the domain models

If you want to test the validation rules with unit-test (of course you want), it is very simple. We just have to validate that the ValidationException is thrown whenever we break the validation rule.

[TestMethod]
public void WhenCustomerIsCreatedValidateThatNameIsNotNull()
{
    AssertHelper.Throws<ValidationException>(() =>
    {
        var c = new Customer(null);
    });
}
 
[TestMethod]
public void WhenCustomerIsCreatedValidateThatNameIsNotEmpty()
{
    AssertHelper.Throws<ValidationException>(() =>
    {
        var c = new Customer("");
    });
}
 
[TestMethod]
public void WhenCustomerIsCreatedValidateThatNameDoesNotExceedOneHundredCharacters()
{
    AssertHelper.Throws<ValidationException>(() =>
    {
        var c = new Customer(new string('x', 101));
    });
}

I have created an AssertHelper class with a Throws() method that throws AssertFailedException if the specified generic exception was not thrown.

 

Happy validating! :D

Posted On Friday, December 14, 2012 3:07 PM | Comments (1)

Sunday, October 14, 2012 #

Hosting and consuming WCF services without configuration files

In this post, I'll demonstrate how to configure both the host and the client in code without the need for configuring services i the <system.serviceModel> section of the config-file. In fact, you don't need a  <system.serviceModel> section at all. What you'll do need (and want) sometimes, is the Uri of the service in the configuration file. Configuring the Uri of the the service is actually only needed for the client or when self-hosting, not when hosting in IIS.

So, exactly What do we need to configure?

  • The binding type and the binding constraints
  • The metadata behavior
  • Debug behavior

You can of course configure even more, and even more if you want to, WCF is after all the king of configuration…

As an example I'll be hosting and consuming a service that removes most of the default constraints for WCF-services, using a BasicHttpBinding. Of course, in regards to security, it is probably better to have some constraints on the server, but this is only a demonstration.

The ServerConfig class in the code beneath is a static helper class that will be used in the examples. In this post, I’ll be using this helper-class for all configuration, for both the server and the client. In WCF, the  client and the server have both their own WCF-configuration. With this piece of code, they will be sharing the same configuration.


   1: public static class ServiceConfig
   2: {
   3:     public static Binding DefaultBinding
   4:     {
   5:         get
   6:         {
   7:                 var binding = new BasicHttpBinding();
   8:                 Configure(binding);
   9:                 return binding;
  10:             } 
  11:         } 
  12:  
  13:         public static void Configure(HttpBindingBase binding)
  14:         {
  15:             if (binding == null)
  16:             {
  17:                 throw new ArgumentException("Argument 'binding' cannot be null. Cannot configure binding.");
  18:             }
  19:  
  20:             binding.SendTimeout = new TimeSpan(0, 0, 30, 0); // 30 minute timeout
  21:             binding.MaxBufferSize = Int32.MaxValue;
  22:             binding.MaxBufferPoolSize = 2147483647;
  23:             binding.MaxReceivedMessageSize = Int32.MaxValue;
  24:             binding.ReaderQuotas.MaxArrayLength = Int32.MaxValue;
  25:             binding.ReaderQuotas.MaxBytesPerRead = Int32.MaxValue;
  26:             binding.ReaderQuotas.MaxDepth = Int32.MaxValue;
  27:             binding.ReaderQuotas.MaxNameTableCharCount = Int32.MaxValue;
  28:             binding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
  29:         }
  30:  
  31:         public static ServiceMetadataBehavior ServiceMetadataBehavior
  32:         {
  33:             get
  34:             {
  35:                 return new ServiceMetadataBehavior
  36:                 {
  37:                     HttpGetEnabled = true, 
  38:                     MetadataExporter = {PolicyVersion = PolicyVersion.Policy15}
  39:                 };
  40:             }
  41:         }
  42:  
  43:         public static ServiceDebugBehavior ServiceDebugBehavior
  44:         {
  45:             get
  46:             {
  47:                 var smb = new ServiceDebugBehavior();
  48:                 Configure(smb);
  49:                 return smb;
  50:             }
  51:         }
  52:  
  53:  
  54:         public static void Configure(ServiceDebugBehavior behavior)
  55:         {
  56:             if (behavior == null)
  57:             {
  58:                 throw new ArgumentException("Argument 'behavior' cannot be null. Cannot configure debug behavior.");
  59:             }
  60:             
  61:             behavior.IncludeExceptionDetailInFaults = true;
  62:         }
  63: }

Configuring the server

There are basically two ways to host a WCF service, in IIS and self-hosting. When hosting a WCF service in a production environment using SOA architecture, you'll be most likely hosting it in IIS. When testing the service in integration tests, it's very handy to be able to self-host services in the unit-tests. In fact, you can share the the WCF configuration for self-hosted services and services hosted in IIS. And that is exactly what you want to do, testing the same configurations for test and production environments.

 

Configuring when Self-hosting

When self-hosting, in order to start the service, you'll have to instantiate the ServiceHost class, configure the  service and open it.

   1: // Create the service-host.
   2: var host = new ServiceHost(typeof(MyService), endpoint);
   3:  
   4: // Configure the binding    
   5: host.AddServiceEndpoint(typeof(IMyService), ServiceConfig.DefaultBinding, endpoint);
   6:  
   7: // Configure metadata behavior
   8: host.Description.Behaviors.Add(ServiceConfig.ServiceMetadataBehavior);
   9:  
  10: // Configure debgug behavior
  11: ServiceConfig.Configure((ServiceDebugBehavior)host.Description.Behaviors[typeof(ServiceDebugBehavior)]);
  12:     
  13: // Start listening to the service
  14: host.Open();
  15:  

Configuring when hosting in IIS

When you create a WCF service application with the wizard in Visual Studio, you'll end up with bits and pieces of code in order to get the service running:

  • Svc-file with codebehind.
  • A interface to the service
  • Web.config

In order to get rid of the configuration in the <system.serviceModel> section, which the wizard has generated for us, we must tell the service that we have a factory that will create the service for us. We do this by changing the markup for the svc-file:

   1: <%@ ServiceHost Language="C#" Debug="true" Service="Namespace.MyService" Factory="Namespace.ServiceHostFactory" %>

The markup tells IIS that we have a factory called ServiceHostFactory for this service.

The service factory has a method we can override which will be called when someone asks IIS for the service. There are two overloads we can override:

   1: System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
   2: System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
   3:  

In this example, we'll be using the last one, so our implementation looks like this:

   1: public class ServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
   2:     {
   3:  
   4:         protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
   5:         {
   6:             var host = base.CreateServiceHost(serviceType, baseAddresses);
   7:             host.Description.Behaviors.Add(ServiceConfig.ServiceMetadataBehavior);
   8:             ServiceConfig.Configure((ServiceDebugBehavior)host.Description.Behaviors[typeof(ServiceDebugBehavior)]);
   9:             return host;
  10:         }
  11:     }
  12:  


As you can see, we are using the same configuration helper we used when self-hosting. Now, when you have a factory, the <system.serviceModel> section of the configuration can be removed, because the section will be ignored when the service has a custom factory. If you want to configure something else in the config-file, one could configure in some other section.

 

Configuring the client

Microsoft has helpfully created a ChannelFactory class in order to create a proxy client. When using this approach, you don't have generate those awfull proxy classes for the client. If you share the contracts with the server in it's own assembly like in the layer diagram under, you can share the same piece of code. The contracts in WCF are the interface to the service and if any and the datacontracts (custom types) the service depends on.

Capture


Using the ChannelFactory with our configuration helper-class is very simple:

   1: var identity = EndpointIdentity.CreateDnsIdentity("localhost");
   2: var endpointAddress = new EndpointAddress(endPoint, identity);
   3: var factory = new ChannelFactory<IMyService>(DeployServiceConfig.DefaultBinding, endpointAddress);
   4: using (var myService = new factory.CreateChannel())
   5: {
   6:     myService.Hello();
   7: }
   8: factory.Close();

 

Happy configuration!

Posted On Sunday, October 14, 2012 4:58 PM | Comments (2)