2009-01-22

NHibernate Provider for Dynamic Data - Part 1

Dynamic Data supports LINQ To SQL and Entity Framework. There is also a provider for LLBLGenLLBLGen.

As a fan of it, I plan to plug NHibernate and Dynamic Data.

Is it possible ?
A read a lot of articles on how to plug an ORM tool and Dynamic Data. There is two points that your ORM tool must provide :
- A DataContext (Or ObjectContext) to provide a working session
- Linq support
- A Datasource control which has SelectParameters support (FransBouma)

Since last week, NHibernate.Linq Alpha is released. So we've got Linq Support and a new object NHibernateContext which could be our DataContext Object.
So I need two components :
- The Model Provider which will provide NHibernate methods to Dynamic Data
- The Datasource control

I've created a basic NHibernate solution to test my provider :



Here is a short description :
- Entities : The entity class, with NHibernate mapping files. I've generated it with Mygeneration soft. Basing on the NHibernate.Linq examples, I've build my context class (with only one entity in it for the moment) :
public class WineNotesContext : NHibernateContext
{
public WineNotesContext(ISession oSession) : base(oSession)
{
}


public IOrderedQueryable<DtCountry> Countries
{
get { return Session.Linq<DtCountry>(); }
}

}

- NHibDynamicData.Provider : This is the project where I wrote my NHibernate Model Provider.
- NHibDynamicData.Controls : This is the project where I wrote my NHibernate DataSource.
- TestLinqDD : A standard Linq To SQL Dynamic Data Website.

The Model Provider
Based on the SimpleModelProvider from dynamic data release. You just have to include it on your global.asax on your dynamic data web site :
model.RegisterContext(
new NHibModelProvider(typeof(WineNotesContext)), new ContextConfiguration() { ScaffoldAllTables = false });


The DataSource Control
Based on the LinqDataSource control. You have to replace standard LinqDataSource on your dynamic data pages by this control.

Customizing your entities
You just have to use attributes. Here is the example of country entity :

[ScaffoldTable(true)]
[
DisplayName("Country")]
[
DisplayColumn("CtrName")]
public class DtCountry
{

The entity will be used by the model (scaffold attribute) and will be shown by its display name. The same attributes are existing for each columns.

There are problems !!!
...but I will fix it on next part of this article :
- Deleting problems
- Showing and updating foreign keys
- Primary key detection problem, if it is not detected, MetaTable will be still on read-only mode !

You can checkout this idea here : http://speezo.unfuddle.com/svn/speezo_nhibdynamicdata/
kick it on DotNetKicks.com

2009-01-13

A DbMetadataProvider for ASP.NET Dynamic Data

I read the article from Marcin on Asp.net on Dynamic Data Futures and custom metadata Providers. I wanted to implement his feature with a database persistance.
For this example, I will store this properties :
  • Name of a table

  • Name of a column

  • Show or not a column


Database structure

The structure is composed of two tables :
  • ST_TYPEOBJET : which store the table attributes : TOBJ_NAME (Name of the table)

  • ST_PROPRIETEOBJET : store the column attributes : PROB_NAME (Name of the column) and PROB_SHOW (Show it Y/N)

ST_TYPEOBJET have a column named T_OBJTYPENAME which store the corresponding Mapping type name (Full Name with assembly)
ST_PROPRIETEOBJET havec a column named PROB_PROPERTYNAME which store the corresponding mapping property.

Implementing a DbMetadataProvider

This DbMetadataProvider is pretty simple. It parse the data in all tables (with LinqToEntities provider), then for each table it register the name with the InMemoryMetaDataManager.
Then, for each property, it register the name and the scaffolding (Show it or not).

///
/// Database metadata Provider
///
public class DbMetadataProvider
{
///
/// Registering metadatas
///
public static void RegisterMetadata()
{
Entities oContext = new DALBase.Entities();
foreach (ST_TYPEOBJET oTypeObjet in oContext.ST_TYPEOBJET.Include("ST_PROPRIETEOBJET"))
{
//Getting the type of the table in the mapping assembly
Type oType = System.Type.GetType(oTypeObjet.TOBJ_TYPENAME);

//Saving the name of the table as attribute
InMemoryMetadataManager.AddTableAttributes(oType,
new DisplayNameAttribute(oTypeObjet.TOBJ_NAME));

foreach(ST_PROPRIETEOBJET oPropriete in oTypeObjet.ST_PROPRIETEOBJET)
{
//Getting the property
PropertyInfo oProperty = oType.GetProperty(oPropriete.PROB_PROPERTYNAME);

//Saving scaffolding
if (oPropriete.PROB_SHOW)
{
//Saving name
InMemoryMetadataManager.AddColumnAttributes(oProperty, new DisplayNameAttribute(oPropriete.PROB_NAME));
}
else
{
InMemoryMetadataManager.AddColumnAttributes(oProperty, new ScaffoldColumnAttribute(false));
}
}
}
}

Call to the provider in global.asax

You just have to specify wich metadataprovider you will use with your model in the global.asax of your web site.
Here, you specify the InMemoryMetadataProvider as the MetadataProviderFactory :
model.RegisterContext(typeof(Entities), new ContextConfiguration()
{
ScaffoldAllTables = true,
MetadataProviderFactory = (type => new InMemoryMetadataTypeDescriptionProvider(type, new AssociatedMetadataTypeTypeDescriptionProvider(type)))
});

Then, you load here all your metadatas :
DbMetadataProvider.RegisterMetadata();

That's it.

kick it on DotNetKicks.com