Saturday 21 June 2008

DynamicData: Database Based Permissions - Part 3

**** UPDATED ****

  1. Part 1 - Create the database tables.
  2. Part 2 - Add a User Interface to modify the permissions.
  3. Part 3 - User Marcin's InMemoryMetadataProvider to add the database based permissions to the Metadata at runtime.
  4. Part 4 - Add components from A DynamicData Attribute Based Permission Solution using User Roles to consume the database based metadata.
  5. Part 5 - Oops! Table Names with Spaces in them and Pluralization.

Add the Database Based Permissions to the Metadata.

In this post we will add the final bit in this series which is using Marcin's InMemoryMetadataProvider to apply the permissions data from the database to the MetaModel used in the application. I've got to admit I was expecting this to be the most difficult bit of the series but thanks to Marcin and his InMemoryMetadataProvider sample Dynamic Data samples: Custom metadata providers on his blog is was easy hardly any reflection used at all.

model.RegisterContext(typeof(NWDataContext), new ContextConfiguration()
{
    ScaffoldAllTables = true,
    MetadataProviderFactory = 
        (
            type => new InMemoryMetadataTypeDescriptionProvider
                (
                    type, new AssociatedMetadataTypeTypeDescriptionProvider(type)
                )
        )
});

Listing 1 - adding the InMemoryMetadataProvider

As you can see all I have had to do here is add same code as Marcin had in his blog to add the InMemoryMetadataProvider nice. And flowing on from that we add our two pieces of code one for the table attributes and one for the column attributes.

var tableAttributes = ADC.AttributesTablePermissions;
if (tableAttributes.Count() > 0)
{
    foreach (var ta in tableAttributes)
    {
        var table = model.Tables.SingleOrDefault(t => t.Name == ta.TableName);
        String[] roles = ta.Roles.Split(new Char[] { ',' }); // Added
        if (table != null)
            InMemoryMetadataManager.AddTableAttributes
                (
                    table.EntityType,
                    new TablePermissionsAttribute(ta.Permission, roles) // Changed ro roles from ta.Roles
                );
    }
}

Listing 2 - applying the table attributes

In this we run through all the column attributes in the returned form the query finding the table using Linq (My personal favourite addition to c# 3.0) which makes getting the table easy and future maintenance of the code easy as its pretty clear what you are doing.

// Import Database Column Attributes
var columnAttributes = ADC.AttributesColumnPermissions;
if (columnAttributes.Count() > 0)
{
    foreach (var col in columnAttributes)
    {
        var table = model.Tables.SingleOrDefault(t => t.Name == col.TableName);
        var column = table.EntityType.GetProperties().SingleOrDefault(c => c.Name == col.ColumnName);
        String[] roles = col.Roles.Split(new Char[] { ',' }); // Added
        if (table != null)
            InMemoryMetadataManager.AddColumnAttributes
                (
                    column,
                    new ColumnPermissionsAttribute(col.Permission, roles) // changed to roles from col.Roles
                );
    }
}

Listing 3 - applying the column attributes

it's in this where we are getting the column that we need a tiny bit of reflection which to be honest you have to debug to see that's what you are doing as the column tat is returned from the query in the foreach loop is of the Type System.Reflection.PropertyInfo.

And that is it for the functionality the Database Based Permissions series. I think that the next step is to provide a tool for adding the columns that the project uses to a database easily, and also i would like to add at least one sproc (stored procedure) to the database that can be called to update the AttributesTables and AttributesColumns with the latest tables and columns from the database and maybe clean up orphaned attribute but I'll have a think about that a bit first.

No comments: