Powered by Twitter Tools.

September 2008
M T W T F S S
« Aug    
1234567
891011121314
15161718192021
22232425262728
2930  

Chris Donnan : Programming - Brooklyn Style

software, trading, family, fun

Object Binding (.Net 1.0)

Lately - I have been up to some Presentation Layer programming. I have been making a mechanism to create bind-able things from our entity objects. This will enable us to bind to grids and other UI elements. We need to be able to create N number of views for our Entity objects - and not put code in odd places in our layered architecture. For example; if we have a FooObject entity class (and/ or several derived FooObject types), - we need to make many different views of this same entity - really entity collections - FooObjectCollection(s).
That being said - in the past I have done this a few ways -

#1) Implement the interfaces for binding (needs to be done over and over for all objects). Since our objects are sometimes quite deep aggregates of other objects, this amounts to lots of work in this case. It is not very DRY (Dont Repeat Yourself) - you need to do this again and again for each object that you want to bind.

#2) Attributes on the entity classes to define attributes at binding- the limitation here is that you get 1 view definition and it is embedded in the entity object. This also puts code for the Presentation Layer in the Domain Layer. This involves reflection and there are more ‘reflectiony variations’ on this approach - but they are basically the same.

#3) Manually create datasets, datatables, dataviews, etc. from your entities. This means manually plucking info out of your classes and sticking them into the datasets. This approach results in you loosing your entities and programming to datasets etc. It becomes a mess. You could use many dataset variants - strongly typed datasets, non-strongly typed datasets, yadda yadda….

#4) Our approach in the end was to use metadata to create a bindable something. This metadata is XML that looks something like:

The XML Meta-data for binding looks like this (this is a flat view):
2 <View>
3 <Band DisplayName=”FooObject” Type=”Some.Name.Space.FooObject, Some.Name.Space”>
4 <Column DisplayName=”FooObjectDetails Id” Path=”FooObjectDetails.Id” Type=”System.String” />
5 <Column DisplayName=”FooObjectDetails Version” Path=”FooObjectDetails.Version” Type=”System.Int32″ />
6 <Column DisplayName=”FooObjectDetails Type” Path=”FooObjectDetails.Type” Type=”System.String” />
7 <Column DisplayName=”FooObjectDetails Name” Path=”FooObjectDetails.Name” Type=”System.String” />
8 <Column DisplayName=”FooObjectDetails FlahFlah” Path=”FooObjectDetails.FlahFlah” Type=”System.String” />
9 <Column DisplayName=”FooObjectDetails IsSomething” Path=”FooObjectDetails.IsSomething” Type=”System.Boolean” />
10 <Column DisplayName=”FooObjectDetails State” Path=”FooObjectDetails.State” Type=”Some.Name.Space.State, Some.Name.Space” />
11 <Column DisplayName= “Location” Path=”FooObjectDetails.Location” Type=”Some.Name.Space.Location, Some.Name.Space” />
12 <Column DisplayName= “SizeOfMe” Path=”FooObjectDetails.SizeOfMe” Type=”Some.Name.Space.SizeOfMe, Some.Name.Space” />
13 <Column DisplayName=”FooObjectDetails FooObjectId” Path=”FooObjectDetails.FooObjectId” Type=”System.String” />
14 <Column DisplayName=”FooObjectDetails Approval” Path=”FooObjectDetails.Approval” Type=”System.String” />
15 <Column DisplayName=”FooObjectDetails SuperHat” Path=”FooObjectDetails.SuperHat” Type=”System.Int32″ />
16 <Column DisplayName=”FooObjectDetails SomeScenario” Path=”FooObjectDetails.SomeScenario” Type=”System.String” />
17 <Column DisplayName= “UptraProperty” Path=”FooObjectDetails.UptraProperty” Type=”System.String” />
18 <Column DisplayName=”FooObjectDetails ChildClothing” Path=”FooObjectDetails.ChildClothing” Type=”System.Double” />
19 <Column DisplayName=”FooObjectDetails Gap” Path=”FooObjectDetails.Gap” Type=”System.String” />
20 <Column DisplayName=”FooObjectDetails ProductGroup” Path=”FooObjectDetails.ProductGroup” Type=”System.String” />
21 <Column DisplayName=”FooObjectDetails Blargh Ref” Path=”FooObjectDetails.Blargh.Ref” Type=”System.String” />
22 <Column DisplayName=”FooObjectDetails Blargh Id” Path=”FooObjectDetails.Blargh.Id” Type=”System.String” />
23 <Column DisplayName= “RationaleOfLife” Path=”FooObjectDetails.RationaleOfLife” Type=”Some.Name.Space.RationaleOfLife, Some.Name.Space” />
24 <Column DisplayName= “FeeType” Path=”FooObjectDetails.AProperty.FeeType” Type=”Some.Name.Space.FeeType2, Some.Name.Space” />
25 <Column DisplayName= “SalesPerson” Path=”FooObjectDetails.AProperty.SalesPerson” Type=”System.String” />
26 <Column DisplayName= “KiddieKooKi Farfehn” Path=”FooObjectDetails.AProperty.KiddieKooKi.Farfehn” Type=”System.String” />
27 <Column DisplayName= “AProperty KiddieKooKi Amount” Path=”FooObjectDetails.AProperty.KiddieKooKi.Amount” Type=”System.Double” />
28 <Column DisplayName=”FooObjectDetails AProperty Farfehn” Path=”FooObjectDetails.AProperty.Farfehn” Type=”System.String” />

That Xml is then loaded as part of a IViewDescriptor in IObjectView :
3 public interface IObjectView
4 {
5 ///
6 /// Gets or sets the source object.
7 ///
8 /// The source object.
9 object SourceObject { get; set; }
10
11 ///
12 /// Gets or sets the view descriptor.
13 ///
14 /// The view descriptor.
15 IViewDescriptor ViewDescriptor { get; set; }
16
17 ///
18 /// Gets the bindable object.
19 ///
20 ///
21 object GetBindableObject();
22 }

Then at runtime you get the IViewDefintion, the source object - and call GetBindableObject() to get something to bind to.

Like so:
257 private void BindDemoForm_Load(object sender, EventArgs e)
258 {
262 // Get a view from the view repository
263 IObjectView objectView = appContext.ObjectViewRepository.GetByKey( “BindDemoForm.ModelView3″ );
264
265 //give the view some data to bind to (the fooObjects in the FooObjectRepository in this case)
266 objectView.SourceObject = appContext.FooObjectRepository.Values;
267
268 // bind to the grid
269 gridBinder.SetObjectToGrid( grid, objectView );
270
271 // get underlying objects when grid is clicked
272 gridBinder.RowDoubleClick += new StuckRowClickHandler(gridBinder_RowDoubleClick);
273 }

After that - we hook up some events so that when we select or click on a row in a grid - we get the underlying Entity object in stead of a row. This means that all of your business operations can happen on your business object - not on a dataset, etc.

This looks something like:
345 private void gridBinder_RowDoubleClick(object sender, StuckRowClickEventArgs e)
346 {
347 // get the underlying item in 1 line of code :)
348 FooObject fooObject = e.BoundObject as FooObject;
349 Debug.Assert( fooObject != null );
350 string formatString = “Your underlying object \nFooObject Id = {0}, FooObject Version = {1}, Type = [{2}]“;
351 string fooObjectDescription = string.Format(
352 formatString,
353 fooObject.FooObjectDetails.Id,
354 fooObject.FooObjectDetails.Version,
355 fooObject.GetType().ToString()
356 );
357 fooObject.FooObjectDetails.FooObjectId = new Random().Next().ToString();
358 gridBinder.UpdateRowFromObject( e.GridRow, fooObject );
359 MessageBox.Show( this, fooObjectDescription );
360 }

There are several advantages to this approach: You can flatten your view of a hierarchical object, you can change the look in the hierarchy, or of course - you can use the existing hierarchy. You can define an unlimited number of views. You can do business operations on your objects directly - not on DataSets/ DataTables/ DataRows, etc.

Behind the scenes - in essence - we are inferring a schema from the root object using reflection. From there - we create a series of datatables in a dataset - complete with relations for the hierarchy etc.

It was quite easy to derive classes from DataTable, DataRow and DataColumn. Doing this allowed me to attach the ColumnDescriptor class and the bound object to the DataRow. The ColumnDescriptors keep the translated meta-data at run-time. The underlying object that the row (and child rows) are bound to are in the BoundObject of the DataRow. The DataTable keeps a reference to the translated meta-data - the BandDescriptor and creates new derived DataRows as required.
9 public class StuckDataTable : DataTable
10 {
11 private BandDescriptor bandDescriptor;
12
13 public BandDescriptor BandDescriptor
14 {
15 get { return bandDescriptor; }
16 set { bandDescriptor = value; }
17 }
18
19 public StuckDataTable()
20 : base()
21 {
22 }
23
24 public StuckDataTable( SerializationInfo info, StreamingContext context )
25 : base( info, context )
26 {
27 }
28
29 public StuckDataTable( string tableName )
30 : base( tableName )
31 {
32 }
33
34 protected override DataRow NewRowFromBuilder( DataRowBuilder builder )
35 {
36 return new StuckDataRow( builder );
37 }
38 }

9 public class StuckDataColumn : DataColumn
10 {
11 private ColumnDescriptor columnDescriptor;
12
13 public StuckDataColumn()
14 : base()
15 {
16 }
17
18 public StuckDataColumn( string columnName, Type dataType, string expr, MappingType type )
19 : base( columnName, dataType, expr, type)
20 {
21 }
22
23 public StuckDataColumn( string columnName, Type dataType, string expr )
24 : base( columnName, dataType, expr )
25 {
26 }
27
28 public StuckDataColumn( string columnName, Type dataType )
29 : base( columnName, dataType )
30 {
31 }
32
33 public StuckDataColumn( string columnName )
34 : base( columnName )
35 {
36 }
37
38 public ColumnDescriptor ColumnDescriptor
39 {
40 get { return columnDescriptor; }
41 set { columnDescriptor = value; }
42 }
43
44 }
8 public class StuckDataRow : DataRow
9 {
10 object boundObject;
11
12 public StuckDataRow( DataRowBuilder builder )
13 : base( builder )
14 {
15 }
16
17 public object BoundObject
18 {
19 get { return boundObject; }
20 set { boundObject = value; }
21 }
22 }
To generate the Xml to do the binding required I whipped up a simple little UI. This UI allows you to load an assembly, list it’s types and generate the basic Xml Meta-data - either flattened or to match the full object hierarchy. From there - you can re-arrange the Xml, delete some etc. This gets us far along the path of binding very complex objects to grids and other things.More to come on ObjectBinding - I have flapped enough for tonight. We can chat some more about how Enum binding was handled, the getting and setting of properties using the nifty Object.SubObject.SubObject.Property syntax….. more to come.

-Chris-


Model/ Domain Objects vs DataSets
Well - here it is - Objects vs DataSets - Especially in a situation where you are not accessing a database directly, I do not get why you would use datasets at all. In a situation where your service tier is sending you back ‘objects’ - I also do not understand why you would use datasets. It seems like a mismatch to me. That being said - I believe if you are directly connecting to the DB, then it makes sense to use datasets. That is the end of the story as far as I am concerned. Furthermore - in ‘higher performance’ applications - you must use the datareader - whereby rendering the dataset unusable. In the past I led deams building the 30th and 60th busiest sites on the web. In these types of situations - datasets are a dog compared to the readers. So - we went with ‘custom objects’ and collections. All of this being said - since I tend to be an object modeler 1st, and I have worked mostly on larger apps - I have not used the dataset too much. In essence, this DOES put me on one side of the argument - so I am looking hard and trying to understand why using datasets is so great. Any feedback from anyone would help. So - below is reference to many of the folks that have raged this debate in the past.I again must say that even though I am looking to ‘get’ the dataset argument - I am still not sold :) Someone educate me - I just do not get it.

Other references to people waging this war:

Objects vs. DataSets - Is it Really About the Technical Aspects?

excerpt:
I find that that the most interesting reasons for choosing Objects vs. DataSets are less technical, but more about the project, the developer’s methodology, and the developer’s preference. The technical reasons for choosing one over the other are pretty weak and can be resolved pretty easily.

My 2 Cents :
I guess I can acept this. I do believe that in a quick and dirty situation - datasets win. Maybe some would argue that the level of developer would favor the dataset solution - the custom object solution REALLY seems simpler to me - I guess I am just biased.

Displaying aggregates: DataSet vs. Domain object performance

excerpt:
…. I work with someone who coded a bunch of stuff using a dataset. unfortunatly, due to the complexity of the project we have to scrap all that code. Custom classes will make everything we needed done so much easier and cleaner.

My 2 Cents :
I MUST say that I totally agree with this comment. In essence I believe that classes are more natural, cleaner and in the long run - you can just embed more knowledge in them. Data sets are containers - classes are the model. I also believe that the class approach - you continually refactor and the solution is flexible - not rigid… Again - I am just biased - someone compel me for datasets….

DataSets vs. Collections

excerpt:
Both DataSets and custom classes don’t limit what you can do in any way, and both can be used to accomplish the same aims. That said, DataSets are fantastic tools for prototyping applications and represent excellent solutions for building systems in a kind of emergency—a limited budget, an approaching deadline, or a short application lifetime. For relatively simple applications, custom entities add a perhaps unnecessary level of complexity. In this case, I suggest that you seriously consider using DataSets.In the economy of a large, durable, complex enterprise system that takes several months to complete, the cost of architecting and implementing a bunch of collections classes is relatively minimal and is incurred only once. The advantages in terms of performance, expressivity, readability, and ease of maintenance largely repay the investment. You are not bound to a tabular rendering of data. Business rules and custom business entities can’t always be adapted to look like a collection of tables. In general, you should avoid adapting data to the data container—quite the reverse, I’d say. Finally, using custom classes makes for easier unit testing because classes and logic are more strictly related than with DataSets.

My 2 Cents :
Dino is the man 1st off - 2nd - I REALLY agree with this statement as just about the best summary of all. This is what I truly believe. Dino also mentions generics in 2.0 and how much stronger the argment for classes/ collections becomes under the 2.0 framework…..

On the Way to Mastering ASP.NET: Introducing Custom Entity Classes

excerpt:
There are a number of situations for which DataSets are particularly well suited, such as prototyping, small systems, and support utilities. However, using them in enterprise systems, where ease of maintenance is more important than time to market, may not be the best solution.

My 2 Cents :
Man - I could excerpt this entire article. Read it - it really sums up my viewpoint as well.

DataSets Are Not Evil

excerpt:
Ok that’s it, everybody stop talking smack about the DataSet. As self-elected chairman of the inexistent “DataSet Preservation Fund”, I feel urged to respond to years of incorrect positioning, biased thinking and over-simplified black-and-white views. I won’t allow the “enterprise community” to make the DataSet the Clippy of .NET, so to speak.

My 2 Cents :
THis guy basically takes the other side of the “On the Way to Mastering ASP.NET: Introducing Custom Entity Classes” article - he does a weak job if arguing frankly…. Someone show me the dataset side of the argument please.. Still dont see it.

Returning DataSets from WebServices is the Spawn of Satan and Represents All That Is Truly Evil in the World

excerpt:
DataSets are bowls, not fruit. Do you really want to return bowls?

A DataSet is an object, right? But it’s not a Domain Object, it’s not an “Apple” or “Orange” - it’s an object of type “DataSet.” A DataSet is a bowl (one that knows about the backing Data Store). A DataSet is an object that knows how to HOLD Rows and Columns. It’s an object that knows a LOT about the Database. But I don’t want to return bowls. I want to return Domain Objects, like “Apples.”

My 2 Cents :
Yes yes yes…. Yes yes yes…

Datasets vs. Custom Entities

excerpt:

  • They are very bindable. (I will give the dataset folks this - the tools do provide this. There are ways around it however - 2.0 has the ObjectDataSource and Infragistics has the excellent - WinDataSource that is useful to make ‘objects’ bind to grids easy as pie.)
  • Easy persistence. (IF you are directly connecting to the DB….)
  • They are sortable.(yes)
  • They are filterable.(yes)
  • Strongly Typed Datasets defined by XSD’s.(yes… better but 99% the rest is the same to me.)
  • Excellent XML integration.(eh, I guess so.. still not ‘nice’ XML. It is generic XML - not XML for your nice object.)
  • Computed Columns (OK - objects do this all the same - just as well - or better depending.)
  • Relations (OK - objects do this all the same - more explicitly I would say)
  • Data Validation (OK - objects do this all the same - mostly better)
  • AutoIncrementing values (OK - objects do this all the same - this is trivial)
My 2 Cents :
OK - this guy has the BEST set of arguments for the dataset way. I do hear the arguments to some faint degree - but - not very strong still.

Serialization of Interfaces and/ or abstract classes via web services….. Objects vs data elements !
So - working on the latest and greatest solution. Great stuff - win forms app - lots (for us) of developers, I have had proper development practice/ time to plan etc so far.. I feel great about the current architecture. One thing is getting my goat and it seems to be something that i have a specific example of -but many have griped about in general. Objects vs data elements - We are basically using a rich domain model along with an MVC pattern - business tier lies behind a web service - so the web service facade is basically the business tier. In essence -we are passing around these ‘model types’ or ‘entity types’ between our client tier and our business tier -  which again I mentioned is a web service. This pattern is great and enables the application in just about every way.The 1st hurdle to overcome - I found very little written about. So — we have some assmblies that contain (amoungst other things) some ‘model types’. The client tier uses these model types - the business tier does. We XML serialize these model types to communicate between the two. This 1st issues was how to declare our model types on the client side using references NOT WEB REFERENCES. We want to add a reference to the library and then use the class - then just pass it across the wire to the business tier (web services). The web services use the SAME library, deserialize the class and KNOW it is the same type as ITS locally referenced class.

Every example out there - you declare any ‘model types’ as though they belong to the web service- and this is fine for the trivial example, BUT in an application where you are using shared libraries - you WANT the behaviour I am describing. References to assemblies - use your classes - send them to the web service and they stars align.

The answer is dumb. Make your ‘model types’ in your library assembly XML serializable. You can add some simple attributes like:


[System.Xml.Serialization.XmlTypeAttribute(Namespace=http://tempuri.org/FooConcreteA.xsd)]

[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/FooConcreteA.xsd", IsNullable=false)]

public class FooConcreteA : AbstractFooBase


You can also get deeper (you will want to) and assign more granular XML serialization attributes. You can add references to this library assembly to your client project as well as to your web service project. This is the dumb part. Now - make a method that returns a model type and build. Now add a web reference to your client project and build. You get - under the ‘web service’ reference node in your project a ‘Reference.map’ - under that is a ‘Reference.cs’ file. See how your web service proxy is created and a new serializable class with the same name as your original class is created (somewhere near the bottom)? - Comment out that class and add a using your library statement on the top of this class. Now - you are apples to apples - you are referencing the SAME class that is from your library - not this new ghost class that was created for you. So - you can simply use your locally referenced class and hand it to the web service :) (PS - remember that each time you update your web reference the Reference.cs file is destroyed)

Problem 2 - Now we have our nice - rich domain mode. We have objects that contain other objects etc. The issue now is…Objects vs data elements. We have some interfaces and/ or abstract classes. When you make a local reference to a class - you ‘have’ all of it’s methods/ properties - you just need to cast to get pointers to the interface/ class members. When you serialize an abstract class - you get the serialized base information only. For example:


WindowsApplication1.WebService1.Service1 svs = new WindowsApplication1.WebService1.Service1();

//HelloWorld() returns AbstractFooBase - like a factory

AbstractTest.FooConcreteA fooConcreteA = (FooConcreteA)svs.HelloWorld();


This type of thing just does not work. You cannot use any object inheritence (as I can see it) via web service. So - when you have a class that contains a collection of abstract types - you need to manage them EACH BY THEIR INDIVIDUAL CONCRETE TYPE - thereby killing the usefullness of objects.

Still workin’ on our incarnation of an answer ….