-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: Runtime modification of mappings for user-added columns?
PostPosted: Wed Mar 21, 2007 5:44 pm 
Newbie

Joined: Wed Mar 21, 2007 4:31 pm
Posts: 6
Our application allows users to extend the existing objects by adding additional columns. They are prefixed to avoid collisions with the application columns. We bind them all to strings in our current implementation (legacy Visual C++ application), but need a way to handle this with NHibernate.

So, the mapping that ships with the application is a subset of what would actually be in the schema that is opened.

Can someone point me to some examples where the mapping configuration is dynamically modified at runtime? I mainly want to see how one might add components to existing tables. There is a <dynamic-component> tag that can be used, but one still must include the properties in the mapping, so runtime manipulation would still be required.

Note that it would be reasonable to have to reinstantiate the SessionFactory (and invalidate open session) any time the schema was changed (or even require the user to restart the application).

Also, this means that a new SessionFactory would be required each time a new database is opened.

Anyway, I'm just looking for some examples (and hopefully success stories!).

Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 21, 2007 11:56 pm 
Beginner
Beginner

Joined: Sat Dec 10, 2005 6:22 pm
Posts: 28
Location: Chicago, IL
You can easily use external configuration files and edit those as they are simple xml files. The hard part will be generating the changes to the objects. You will have to recompile the entities with the new properties. I am sure it can be done somehow but seems like a lot of work.

Why not do what Sharepoint does and create a UserData table with a bunch of columns. Each entity in your application can have an association to the UserData object. You then just need to manage mappings for property names to UserData column names.

Code:
public class UserData
{
  string _col01;
  string _col02;
  Dictionary<String, int> _propertyMapping;

public void CreateProperty(string propertyName)
{
    // Get first unused column
    _propertyMapping[propertyName] = unusedColumnNumber;
}

public string this[string propertyName]
{
    get {

     switch (_propertyMapping[propertyName])
     {
        case 1: return _col01;
        case 2: return _col02;
     }
    }
    set {
      switch (_propertyMapping[propertyName])
      {
         case 1: _col01 = value;
         case 2: _col02 = value;
      }
    }
}
}


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 22, 2007 8:37 am 
Newbie

Joined: Wed Mar 21, 2007 4:31 pm
Posts: 6
abombss wrote:
...
Why not do what Sharepoint does and create a UserData table with a bunch of columns. Each entity in your application can have an association to the UserData object. You then just need to manage mappings for property names to UserData column names.
...


Mainly because it's a legacy application that is being migrated to .NET. It's been around over 10 years, and there are potentially 500-1000 customers who would want to upgrade. Since our stated goal is not to do any breaking changes in the database schema for the first release, we have to find a way to map the existing schem into the new framework.

I'm also evaluating the Wilson ORMapper as an alternative. It's viable, but would require changes to support many of the features that NHibernate supports out of the box. I had to add an API to it to add additional fields after the XML mapping was loaded.

If I can resolve just this one problem with NHibernate, I think it's a Go -- it's a very nice framework. However, I don't know how comfortable I am changing it's internals to support what I need. I'd be more than willing to regenerate an XML mapping document for an individual class and hand it off to NHibernate for recompilation if that's what it takes.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 22, 2007 10:30 am 
Beginner
Beginner

Joined: Sat Dec 10, 2005 6:22 pm
Posts: 28
Location: Chicago, IL
Can you post a brief an example of how current project does this. How are you accessing the new data column through the existing code? Is it something like Object.GetCustomField("MyField") ?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 22, 2007 2:04 pm 
Newbie

Joined: Wed Mar 21, 2007 4:31 pm
Posts: 6
abombss wrote:
Can you post a brief an example of how current project does this. How are you accessing the new data column through the existing code? Is it something like Object.GetCustomField("MyField") ?


That is as good an analagous description as any.

The current project uses recordsets (MFC, CRecordset-derived) for the CRUD code, and the tables that support custom fields have a dynamic component (basically a linked list of column/value pairs) where the columns are provided after the database connection is established and then the recordset will bind and load the correponding values.

So, it's basically just rs.GetFieldValue("MyField")

I have done some more research since my original post, and the <dynamic-component> tag does in fact work well in my testing (but I have provided the XML statically in the file at this point before I wade into doing changes at runtime).

E.g. using your field name:

XML:

<dynamic-component
name="CustomFields"
access="property">
<property
name="MyField"
column="MyField"
type="System.String">
</property>
</dynamic-component>

C# code in class:

private IDictionary customFieldDictionary;

public virtual IDictionary CustomFields
{
get { return customFieldDictionary; }
set { customFieldDictionary = value;
}
}


---------------

Then you would just use the CustomFields IDictionary interface to read/write the fields. Unfortunately, they aren't directly bindable without doing extra work, I'm not at all surprised by that.

It took some experimenting to see what was actually happening under the covers. NHibernate instantiates a Hashtable to hold the values, but only if it encounters a non-null value. It actually calls the "set" property to populate your IDictionary member.

So, I need to figure out how to populate that property list after I load a configuration but before I instantiate the session factory, and I can make this work!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 27, 2007 8:54 am 
Newbie

Joined: Wed Mar 21, 2007 4:31 pm
Posts: 6
OK, it turns out this can be done if you spend a day or two of research to understand how the NHibernate.Cfg.Configuration class maps the <dynamic-component> tag when the XML is loaded.

I first tried a table that had 3 "extra" fields, 2 of which I statically included in the XML file. After deciphering what was done by watching the mapping process handle this component, I then figured out how to programmatically adjust the mapping to include the 3rd field.

While NHibernate will also accept an "empty" <dynamic-component> tag in the property list (which I thought I might need to include for those tables that support having extra user-defined fields, it turns out that it wasn't so difficult to add, either.

Of course, all this must be done before you instantiate the SessionFactory, but at least it works, and also no modification of the internal NHibernate code was required (no chance of getting away with this using the Wilson ORMapper).

The reverse engineering was much complicated by the use of the local variable "model" to refer to things of all different types (SimpleValue, Property, Column, and Component), so it took some careful walking through the debugger and inspection of the variables to get it all straight.

I suppose there is somewhere I could submit the code to do this, but I'm a newbie.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 16, 2007 11:20 am 
Newbie

Joined: Wed May 16, 2007 7:17 am
Posts: 7
Hi,

Could I get a sample of you code to do this please, or a nudge in the right direction? I am currently trying to solve exactly the same problem.

I would like to add a new custom field to a db table, and then reload the configuration, adding a new <property /> tag to the <dynamic-componenent /> section.

My email is "spam-1 AT karumbo DOT com".

Many thanks
Chris


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 16, 2007 3:59 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
We handled this problem by regenerating the entities and the mappings. We generate both the entity classes and the mappings from existing database schema, rather than generating database schema (and entity classes) from mappings. We did this not only because of legacy reasons -- i.e. we already had an existing database before adopting NHibnerate -- but our managers argued that our customers who extend our product are familiar with making new database tables, columns, foreign keys, etc. but are not familiar with NHibernate mappings.

What we do is track what parts of the database schema are "standard product" and what parts are "user-defined". When regenerating the entities, we generate subclasses that inherit from the "standard product" entities, so their code explicitly only contains new properties for new columns and foreign keys. The mapping, though, we completely regenerate. In our application startup, we have logic that knows which entities were extended by the user, and for those, it loads the user-regenterated mapping for that particular entity instead of the standard-product mapping. Our entity factory (used for creating new, transient instances) also knows which class to create -- standard product vs. user extended.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.