Gathering Metadata

Gathering Metadata

Old forum URL: forums.lhotka.net/forums/t/1666.aspx


wjcomeaux posted on Thursday, November 02, 2006

I am looking for a way to gather appropriate MetaData from our database. We are using SQL Server so we have access to extended properties in both the table and columns.

What I am struggling with is how to decide when a table should be created as an EditableRoot, EditableChild, ReadOnlyRoot, etc.

Are there some guidelines for when these should be used like, if the current object is an EditableRoot then collections inside it should be EditableChildList? Or should I just fasten a property onto each table indicating which type of object to create and then also for each column to indicate what type of property to generate for that column?

This limits me to only creating properties of a single type unless I get really complex with the logic I use on my column level extended properties.

If I generate the Project table as both an EditableRoot and an EditableChild then what should I generate it's Department as? Department is a foreign key in the Project table so I can simply leave it as an int representing the ID of the Department or I can also emit a Department object as a member of the Project object. But should it be Editable or ReadOnly? Or the same as the parent object?

Thanks,

Will

Bayu replied on Thursday, November 02, 2006

This is a dangerous road ....

You BOs design should be driven by their need to serve use cases ..... so depending on how you intend to organize your UI-views in your app you should choose which object should become the editable root and which other objects are candidates for child-collections and so on.

What you propose boils down to modeling your use cases inside your DB. Or perhaps concluding your use cases from your DB model. It might work, if you have a particular app that strictly follows a particular design. In that case you could indeed enhance the metadata in your DB or follow standard reasoning patterns to extract use cases from your DB.

However, in case you want to improve general applicability (or if you are going to write metadata somewhere anyways) then I would opt for storing this kind of information separately. Following Dollard you could manually maintain an XML file for this purpose.

In this file you would then enumerate all your views and the particular BOs they require (e.g. CustomerListView, requirest just readonlylist CustomerList and your CustomerDetailsView might need an editableroot Customer and also an editablechildcollection of CustomerContacts).

Bayu

guyroch replied on Thursday, November 02, 2006

One of the most difficult things to do here is to let go of the database schema for a few minutes and focus exclusively on object responsibility -> the way is it suppose to _look_ and _work_ from the users perspective and nothing else.  You’ll be surprised at what will come out of this exercise.  For example, if you come out of this and _feels_ that the Project BO should extend _some_ behavior for both Project and Department, then you code your Project BO as such – regardless of how they will be persisted in your database.

 

I think you're trying to create one BO for 1 database table - this is wrong :(.

 

Does this make any sense to you?

 

Hope this helps…

 

 

wjcomeaux replied on Thursday, November 02, 2006

I am quite familiar with Dollard's ORM usage in code generation. I also understand the benefits of separating the business objects from the database schema. What we don't want though is the requirement of maintaing a really large ORM file that pretty much mimics the design of our database in a lot of respects.

That's why I am attempting to use the extended properties (I know these tie us to SQL Server but we're ok with that). I figure that using properties we can define what objects should be generated. We don't mind getting complex with these properties but we do want to keep them as simple as possible.

I don't want an object per table as I realize this yields poor business object design, however I am thinking of something like: At the table level I can have an extended property indicating what types of business objects to generate here (EditableRoot, EditableChild, etc).

On the column level I can just add extended properties for things like :"EmitAsEditableChildObject" or "EmitAsEditableRootObject", etc, when the column is a foreign key into another table and we want the parent object to have a child object and not just an ID.

This would save us from having to maintain a very large ORM file though we would still likely need one in some cases (an object that joins between tables).

Thoughts?

Bayu replied on Thursday, November 02, 2006

In my current project I have more objects that relate to multiple tables (via joins) than I have objects that map to a single table. So for me it would turn into a very complicated challenge to manage all the relevant ORM details using extended properties in my DB.

On the other hand, a huge ORM file can be made very maintainable by writing a simple treeview-based XML editor (or use xml spy).

Just a though: you could equally well store the ORM details in relational structure and use a CSLA-based app to manage your ORM settings. Provides a nice test case as well: you have succeeded when your code-gen can generate your ORM maintenance tool. Big Smile [:D]

Bayu

wjcomeaux replied on Thursday, November 02, 2006

Another question we just happened upon with metadata gathering.

Using PTracher as an example, A Project has a ProjectResources collection in it. So, which kind of ProjectResources collection do we generate? EditableRootList, EditableChildList? ReadOnly?

I know this depends on use case, do we ever want to let the project object add and remove resources? Probably so. Now, what if we create a ReadOnlyRoot Project. What kind of ProjectResources collection should it have? Almost certainly of ReadOnlyList but of Root or Child?

One of my colleagues doesn't like the idea of being able to do things like

myProject.myProjectResources(0).Name = "BOB";

myProject.myProjectResources(0).Update()

He would prefer something like
EditableRootResource myRootResource = myProject.myProjectResources(0);

myRootResource.Name = "BOB;

myRootResource.Update();

Using his method, then the need of an EditableChildObject becomes completely useless and it's obviously a part of the architecture for a reason.

So, what are your opinions?

Thanks,

Will

Brian Criswell replied on Thursday, November 02, 2006

Bayu:
Just a though: you could equally well store the ORM details in relational structure and use a CSLA-based app to manage your ORM settings. Provides a nice test case as well: you have succeeded when your code-gen can generate your ORM maintenance tool. Big Smile [:D]

That's what I do, except I use XML as the back end.  It is very cool when the app becomes self sufficient.

pelinville replied on Thursday, November 02, 2006

One more suggestion.
 
You can use the schema information provided by the database. I use a combination of ADO.net and the DMO objects.
 
You can get schema information for everything in the database.  Stored procedures UDF's, UDT's, view and tables. (I even get the DDL needed to create the object and store it also.)
 
Take that schema information and put it in an xml file.  Extend the xml schema to include any extra metadata you might need (like the creation DDL).
 
Now you can generate all you classes  from that DDL.  Even better it is XML format so transforming it to whatever you need is relativly easy. And because it is XML it is also Strongly Typed Datasets.  Programming against it couldn't be easier.
 
Like Brian said, cool when when it all comes together and becomes self supporting.
 
I tried to use the SQL Server schema infomatio by itself initially but even with the extended properties I kept running into situations where I needed just a little bit more data in my meta data.
 
As far as what type of objects to create?  Well I create them all as root types with friend factory methods. But then again I have to support web apps with no session state and have figured out a way to deal with the complexity of having all root objects. 
 
 

wjcomeaux replied on Friday, November 03, 2006

That's another thing I have. I am supporting both web and windows applications and we obviously don't want separate libraries for each one.

As for creating all root objects, that sounds like it would complicate the library a lot. Even in web development you still have plenty of need for child objects and NameValue lists as well as the collection objects.

At least, I would think you'd still need child objects. I could be wrong. Maybe you are simply doing a schema of anytime you have an object with a collection of children you make it a collection off root objects and simply make it a child to the main object?

Will

wjcomeaux replied on Friday, November 03, 2006

This is what we have come up with so far. Let me know if you guys see any obvious, glaring, horrid flaws.

Thanks,

Will

//Generate an EditableRoot and ReadOnlyRoot for every object that has an ID field that is the sole PrimaryKey
  if(ts.PrimaryKey.MemberColumns.Count == 1 && ts.PrimaryKey.MemberColumns.Contains("ID"))
  {
   WriteObject(xtw, ts, ds, "EditableRoot", ts.Name);
   WriteObject(xtw, ts, ds, "ReadOnlyRoot", ts.Name);
  }
   
  //These are objects that require a parent object. They can not be instantiated alone in the system.
  //If the table has a ParentObject and does NOT have a ChildObject
  if(ts.ExtendedProperties.Contains("ParentObject") && !ts.ExtendedProperties.Contains("ChildObject"))
  {
   WriteObject(xtw, ts, ds, "EditableChild", ts.Name);
   WriteObject(xtw, ts, ds, "ReadOnlyChild", ts.Name);
  }
  
  //Generate an EditableRootList and ReadOnlyRootList for every object that has an ID field that is the sole PrimaryKey,
  //and has an extended property called Child
  if(ts.ExtendedProperties.Contains("ChildObject"))
  {
   WriteObject(xtw, ts, ds, "EditableRootList", StringUtility.ToPlural(ts.Name));
   WriteObject(xtw, ts, ds, "ReadOnlyRootList", StringUtility.ToPlural(ts.Name));
  }
  
  //Generate child lists when we have two columns as a primary key and neither column is called "ID"
  //and when we need a list that is used as a collection inside of another object and not a standalone list.
  if(ts.PrimaryKey.MemberColumns.Count == 2 && !ts.PrimaryKey.MemberColumns.Contains("ID") && ts.ExtendedProperties.Contains("ChildObject") && ts.ExtendedProperties.Contains("ParentObject"))
  {
   WriteObject(xtw, ts, ds, "EditableChildList", StringUtility.ToPlural(ts.Name));
   WriteObject(xtw, ts, ds, "ReadOnlyChildList", StringUtility.ToPlural(ts.Name));
  }
  
  //Generate a NameValue list when we have an ID column and a NAME column and the ID column is the sole primary key.
  if(ts.PrimaryKey.MemberColumns.Count == 1 && ts.PrimaryKey.MemberColumns.Contains("ID") && ts.Columns.Contains("Name"))
  {
   WriteObject(xtw, ts, ds, "NameValueList", ts.Name);
  }

pelinville replied on Friday, November 03, 2006

wjcomeaux:

That's another thing I have. I am supporting both web and windows applications and we obviously don't want separate libraries for each one.

As for creating all root objects, that sounds like it would complicate the library a lot. Even in web development you still have plenty of need for child objects and NameValue lists as well as the collection objects.

At least, I would think you'd still need child objects. I could be wrong. Maybe you are simply doing a schema of anytime you have an object with a collection of children you make it a collection off root objects and simply make it a child to the main object?

Will

 
Using a couple of Interfaces and extending the base classes so that they implment those interfaces and have a variable called MyParent I can, for any object loaded, find the parent as well as the true root (which is responsible for initiating the save process.)  But if you can't use session state there isn't much else you can do.  You can still have Readonly roots and readonly children. I don't have any of them but they are still possible.
 
Rocky talks about that hear.
 
http://www.lhotka.net/Article.aspx?area=3&id=5f6efc69-c66a-4d18-b857-1b9ade6e0852
 
I guess it is hard but I have never done it any other way.  What everybody else talks about hear seems much more difficult than what we do.

guyroch replied on Friday, November 03, 2006

I'm using MyGeneration for my code gen and it comes with a very detailed MyMeta API to do exactly what you are suggesting.

I know its probably too late in the game for your to look into MyGenetation, but it might not be too late for others that will read this thread in the future :)

 

mr_lasseter replied on Friday, November 03, 2006

Do you know where one can find the CSLA templates for MyGeneration?

guyroch replied on Friday, November 03, 2006

MyGeneration is written in .Net 1.1. 

I created my own .Net project and referenced the MyMeta.dll, Zeus.dll, etc... from MyGeneration and then coded my own templates in c#.  This way I was able to take full advantage of code complete and compile error when modifying my templates. 

Here is a link... (They are 1.5 templates though, but it will give you an idea)

http://www.mygenerationsoftware.com/TemplateLibrary/Archive/?guid=8876506d-d55e-46ef-bc59-aee73e222fdf

Nothing prevents you from using existing templates with the MyGeneration UI but I've always found that _free_ templates found on the web for any code generation tool will lack some functionality your personaly need.  So I created my own templates for what I needed.

 

guyroch replied on Friday, November 03, 2006

Clarification...  The link provided above points to exiting templates on the web, they are not the one I wrote in c#.

Copyright (c) Marimer LLC