Showing posts with label MonoRail. Show all posts
Showing posts with label MonoRail. Show all posts

There is a new framework in the NHibernate Contrib project named NHibernate.Validator. The project began as port of the java Hibernate.Validator project and is started by Dario Quintana. This framework allows you to validate objects in a similar way to other validation frameworks except that it has out of the box integration with the NHibernate's entity lifecycle. This means that you can configure it to do validation on entity insert/updates. The integration with NHibernate is not required however.

You can specify the validation rules either as property/field attributes directly in the code or in a separate xml file with a schema similar to that used in NHibernate mapping files.

Example:

public class User
{
  public virtual int Id
  {
      get { return id; }
  }

  [NotEmpty, NotNull]
  public virtual string UserName
  {
      get { return userName; }
      set { userName = value; }
  }

  [Email]
  public virtual string Email
  {
      get { return email; }
      set { email = value; }
  }

  [Past]
  public DateTime CreatedDate
  {
    get { return createdDate; }
    set { createdDate = value; }
  }

  [Min(18, Message="You are to young!")]
  public int Age
  {
    get { return age; }
    set { age = value; }
  }

  [CreditCardNumber]
  public string CreditCardNumber
  {
    get { return creditCardNumber; }
    set { creditCardNumber = value; }
  }
  
  ///... 
}

If you don't like to clutter your code with attributes you can use the xml config option, example:

<nhv-mapping xmlns="urn:nhibernate-validator-1.0">
  <class name="NHibernate.Validator.Demo.Winforms.Model.Customer, NHibernate.Validator.Demo.Winforms">    
    <property name="FirstName">
      <not-empty/>
      <not-null/>
    </property>    
   
    <property name="Email">
      <email/>
    </property>

    <property name="Zip">
      <pattern  regex="^[A-Z0-9-]+$" message="Examples of valid matches: 234G-34DA | 3432-DF23"/>
      <pattern  regex="^....-....$" message="Must match ....-...."/>
    </property>    
    
  </class>  
</nhv-mapping>

Personally I can barley stand the NHibernate xml mapping files (don't like to poke around in xml files that much) so I think I prefer the attribute version. But it is nice to have this option. It makes it possible to validate objects in third party assemblies that you do not have the code for. There are many more validators than the ones used above and it is very easy to create custom validators.

You can configure the validation engine in code or in app/web.config, this is how you can do it in code:

NHVConfiguration nhvc = new NHVConfiguration();
nhvc.Properties[Environment.ApplyToDDL] = "false";
nhvc.Properties[Environment.AutoregisterListeners] = "true";
nhvc.Properties[Environment.ValidatorMode] = "UseAttribute";
nhvc.Mappings.Add(new MappingConfiguration("NHibernate.ValidatorDemo.Model", null));

ValidatorEngine validator = new ValidatorEngine();
validator.Configure(nhvc);

Since NHibernate.Validator uses the event listener system I think you have to use NHibernate 2.0 if you want the NHibernate integration. To validate an object you simply use the Validate function, here is a simple example (using MonoRail):

public void Create([DataBind("user")] User user)
{
  InvalidValue[] errors = validator.Validate(user);

  if (errors.Length > 0)
  {
    Flash["errors"] = errors;
    RedirectToAction("index");
  }
  else
  {
    repository.Create(user);
  }
}

And here is the view:

<div>
    <h2>Create user</h2>    
    
    ${Html.FormToAttributed("Home", "Create", {@id: "create-form"})}
    
    <p>    
        <label for="UserName">UserName:</label>
        ${Form.TextField("user.UserName")}
        
        <label for="UserName">Email:</label>
        ${Form.TextField("user.Email")}
        
        ${Html.SubmitButton("Submit")}
    </p>
            
    ${Html.EndForm()}
    
    
    <?brail if IsDefined("errors"): ?>
        <ul>
        <?brail for error in errors: ?>
            <li>${error.PropertyName}: ${error.Message}</li>    
        <?brail end ?>
        </ul> 
    <?brail end ?>
</div>

This is of course a very simple example. You probably want to have the error message after each field and some client based validation. I think I will do another post about trying to integrate NHiberante Validator with JQuery validation.

If you want to try NHibernate validator and do not want to build it from the trunk there is a 1.0 alpha release available on sourceforge. The release contains a WinForms examples that shows how to integrate it with the WinForms error provider system.

I am currently trying to prototype how a portal application could be build using ASP.NET MVC (I would prefer to use MonoRail but can't).

There is a big problem in the way the default implementation of the IControllerFactory is handling controller types. It is using a simple dictionary to map controller names to controller types. The key in the dictionary is only the controller name (like "Home" for a HomeController type), this means that you cannot have controllers with the same type name (no matter what namespace or assembly the controller is in). In big applications you probably want to group your controllers into areas in which case a controller of the same name could be likely.

So currently it is not possible to group controllers into what MonoRail calls Areas.  To do this you would have to implement IControllerFactory from scratch since the root of the problem is the ControllerTypeCache which is being used by the DefaultControllerFactory, and the ControllerTypeCache is of course internal and sealed. There are probably other changes that would need to be made to introduce the concept of areas.

There is a extensibility point named IViewLocator used by the WebFormViewEngine which can be exchanged to change the way the view engine locates views. A simple example:

public class PortalViewLocator : ViewLocator
{
      public PortalViewLocator ()
      {
          ViewLocationFormats = new[] {
              "~/CustomViewDir/{1}/{0}.aspx",
              "~/CustomViewDir/{1}/{0}.ascx",
              "~/CustomViewDir/Shared/{0}.aspx",
              "~/CustomViewDir/Shared/{0}.ascx"
          };
          MasterLocationFormats = new[] {
              "~/CustomViewDir/{1}/{0}.master",
              "~/CustomViewDir/Shared/{0}.master"
          };
      }
}

To solve the problem with controller areas MonoRail has something it calls a Controller Tree which serves the same role as the ASP.NET MVC ControllerTypeCache. In MonoRail the area of a controller is specified using the optional ControllerDetails attribute:

using Castle.MonoRail.Framework;

[ControllerDetails(Area="admin")]
public class UsersController : Controller
{
    public void Index()
    {
    }
}
With the default routing the above index function would correspond the URL "/admin/users/index". If ASP.NET MVC adopted a similar approach it could check for an attribute in it's clever Html helper functions which takes in lambda functions (i.e. expression trees):
Html.ActionLink<UserController>(c => c.Register(), "Register now!")

I understand that they want the keep the ASP.NET MVC framework as lightweight and simple as possible and only add features when they are really needed (which is a good philosophy) and have extensibility points for specific scenarios. However I think that a concept like controller areas should built into the framework.

I also hope they start looking at scenarios where you might want to create a modular web applications composed of multiple sub projects. I guess it would be possible to do that now but it would be very hard and would require you to replace many of the default implementations in the MVC framework.

The new statistics class in Hibernate 2.0 is a great tool to use for monitoring potential performance problems with your app. The class exposes a large number of counters and measurements. Here are some highlights of what is available:

  • EntityDeleteCount
  • EntityInsertCount
  • EntityLoadCount
  • EntityUpdateCount
  • QueryExecutionCount
  • QueryExecutionMaxTime
  • QueryExecutionMaxTimeQueryString
  • QueryCacheHitCount
  • ConnectCount
  • SessionCloseCount
  • SessionOpenCount
  • CollectionLoadCount
  • CollectionUpdateCount
  • CollectionRemoveCount
  • Queries
  • PrepareStatementCount

The statistics engine can be turned on/off dynamically with the IsStatisticsEnabled property and you can get statistics on specific entities as well.

Just to try this out I created a MonoRail filter that checks for "nhibstats" in the query parameters of the request. If the parameter is found it will turn on the nhibernate statistics, this is done before the controller action is executed. After the action as completed it will add the stats data to the PropertyBag so the view can access it.

public class NHibernateStatsFilter : IFilter
{
    public bool Perform(ExecuteWhen exec, IEngineContext context, IController controller,
                        IControllerContext controllerContext)
    {
        if (context.Request["nhibstats"] == null)
            return; 
        
        if (exec == ExecuteWhen.BeforeAction)
        {
            sessionFactory.Statistics.Clear();
            sessionFactory.Statistics.IsStatisticsEnabled = true;
        }
        else if (exec == ExecuteWhen.AfterAction)
        {
            controllerContext.PropertyBag["nhibernate_stats"] = sessionFactory.Statistics;
            sessionFactory.Statistics.IsStatisticsEnabled = false;
        }
    }
}

I use the same filter for both the before and after action "events", this means that you have to specify the filter attribute like this:
[Filter(ExecuteWhen.BeforeAction | ExecuteWhen.AfterAction, typeof(NHibernateStatsFilter))]
public class BaseController : Controller
{
        
}
Now you can add to your top layout view something like this:
<?brail if IsDefined("nhibernate_stats"): ?>
    <dl>
        <dt>EntityInsertCount</dt>
        <dd>${nhibernate_stats.EntityInsertCount}</dd>        
        <dt>EntityUpdateCount</dt>
        <dd>${nhibernate_stats.EntityUpdateCount}</dd>        
        .
        ..
        ...
    </dl>
<?brail end ?>

I think this could be really useful. Now you don't have to open SQL Profiler whenever you want to know what NHibernate is up to!