Business Object Abstration...

Business Object Abstration...

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


cmichaelgraham posted on Friday, December 23, 2011

Greetings,

I'm trying to figure out how to abstract the implementation of my business objects through a set of interfaces.

The problem I'm having is figuring out how to represent the child collections.  I was thinking ObservableCollection<IMyChild> would work in the parent, but the concrete parent will have an ObservableCollection<MyChild>, which is not an ObservableCollection<IMyChild>.

I want to be able to pass the MyParent to my app code, but have my app code access the parent and child via the interfaces...

Any advice is greatly appreciated :)

-Mike

 

JonnyBee replied on Friday, December 23, 2011

Hi,

The problem here is that an interface is not serializable. So CSLA business objects may implement your interfaces but declarations must be a concrete type.

My advice would be to rather look at using ObjectFactories in your DataAccess.

When you use ObjectFactories your BO's will only

And the ObjectFactory will have

Plus you can create "mock" factories and make your own plugable ObjectFactoryLoader by using Interfaces.

Even if you child property is declared as ObservableCollection<MyChild> the app code may cast the content to ObservableCollection<IMyChild> following the Liskov substitution principle.

 

cmichaelgraham replied on Monday, December 26, 2011

Thank you for the response. 

I agree that the use of factories is important.  My app code should receive a factory for creating new IMyChild objects.  The factory will actually return the concrete MyChild object. 

My struggle is with the declaration of the child collection in IMyParent.

CSLA creates a child collection in the concrete class that is, among other things, and ObservableCollection<MyChild>.

These are my goals:

Here is some code to illustrate my problem

 

JonnyBee replied on Monday, December 26, 2011

Hi,

I believe you should create an intermediate CSLA base class for MyChild that implements IChild interface. 

Look at this post series about polymorfism: http://forums.lhotka.net/forums/p/10976/51082.aspx#51082

The Child list should be declared as BusinessListBase of ChildBase objects (the concrete base class) and this should be the type of the managed property in Parent.

You should then be able to expose the list as BusinessLisBase of child or ObservableCollection of IChild.

cmichaelgraham replied on Tuesday, December 27, 2011

Wow !!  Thanks - that looks like it will do exactly what I asked Smile

I did notice (in the referenced post) that the interface IMyChild would have to inherit from IMobileObject (for proper Serialization?)...

Is that really a requirement, or just from the example in that post?  It does make sense that CSLA would require that the objects implement that...

I was actually hoping that these requirements could be satisfied also:

One of the goals of the abstraction / interfaces is that the app code is independent of the Business Object implementation.

I recognize that this may be impossible, and I greatly appreciate your assistance...

cmichaelgraham replied on Tuesday, December 27, 2011

Here's an attempt to capture the essence of the interface design goals:

So I just hand the app code an IMyFactory and an IMyParent and it goes about its work...

JonnyBee replied on Tuesday, December 27, 2011

Well,

How do you plan to specify the databinding interfaces?

UI often require knowledge of additional interfaces like INotifyDataError/INotifyErrorInfo/IPropertyChanged and more.

CSLA.Xaml helper classes for Error/Warn/Info and ViewModel will require access to CSLA objects.

 

cmichaelgraham replied on Tuesday, December 27, 2011

HI JonnyBee - it is a good point you make.

This is a large application with lots of plugin extensions.  Some of the extensions expose objects to the user like wells, leases, etc.  I want to fully exploit CSLA for these extensions and have no issue with taking a dependency for these.  This is where CSLA shines, with business rules, authorization rules, state tracking, UI helpers, and the like.

The application also provides other plugin extensions that are more generic tools.  An example is a decline curve chart.  These tools "attach" themselves to dynamic data sources through "templates" that define how to map the data.

To keep these tool as reusable as possible, the template objects get passed to the chart tool by the containing UI classes.  There are many varied use cases for these tools and so there are many sets of containing UI classes. 

In some scenarios, the containing UI classes will load the chart templates from a back end configuration database (this is a case that could be implemented using CSLA if the interface abstraction dilemma can be solved), while in other use cases, the templates may just be created using POCOs generated in code.  Other chart use cases will be constructed after the base product is shipped and deployed, and may provide the chart templates using some not-yet-known scheme.

It would be nice, but not crucial, if one of the chart template implementations could be CSLA-based.

cmichaelgraham replied on Tuesday, December 27, 2011

What would you think about creating an ObservableCollection<IMyChild> in the MyParent business object and writing code to keep in in sync with the MyChildCollection?

That way, the IMyParent interface can expose the ObservableCollection<IMyChild>, but consumers of the concrete classes get all of the CSLA goodness?

Would I just have to hook the collection changed event in both observable collections and propagate the changes to the other?

Do you see any hidden problems with that approach?

cmichaelgraham replied on Tuesday, December 27, 2011

I updated the GIST to illustrate the idea...  Seems to work...  Please let me know if you can think of any gotchas...

Gist Code Sample

 

JonnyBee replied on Saturday, December 31, 2011

My simplest sample would be like this:

My code:

  // Common interface for child
  public interface IChild : Csla.Core.IEditableBusinessObject
  {
    int Id { get; set; }
    string Name { get; set; }
  }

  [Serializable]
  // Common base class with common properties
  public class ChildType<T> : BusinessBase<T> where T : ChildType<T>, IChild
  {
    private static readonly PropertyInfo<int> IdProperty = RegisterProperty<int>(p => p.Id);
    public int Id
    {
      get { return GetProperty(IdProperty); }
      set { SetProperty(IdProperty, value); }
    }

    // example with managed backing field
    private static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(p => p.Name);
    public string Name
    {
      get { return GetProperty(NameProperty); }
      set { SetProperty(NameProperty, value); }
    }
  }

  [Serializable]
  public class ChildType1 : ChildType<ChildType1>, IChild
  {
    public ChildType1(int id, string name)
    {
      using (BypassPropertyChecks)
      {
        Id = id;
        Name = name;
      }
      MarkAsChild();
    }
  }

  [Serializable]
  public class ChildType2 : ChildType<ChildType2>, IChild
  {
    public ChildType2(int id, string name)
    {
      using (BypassPropertyChecks)
      {
        Id = id;
        Name = name;
      }
      MarkAsChild();
    }
  }

  [Serializable]
  public class ChildBusinessList :
    BusinessListBase<ChildBusinessList, IChild>
  {
    public static ChildBusinessList GetEditableRootList(int id)
    {
      return DataPortal.Fetch<ChildBusinessList>(id);
    }

    private void DataPortal_Fetch(int criteria)
    {
      RaiseListChangedEvents = false;

      this.Add(new ChildType1(1, "Xyx"));
      this.Add(new ChildType2(2, "Zzy"));

      RaiseListChangedEvents = true;
    }
  }

Unit Test:

    [TestMethod()]
    public void GetEditableRootListTest()
    {
      var list = ChildBusinessList.GetEditableRootList(1);

      Assert.AreEqual(typeof(ChildType1), list[0].GetType());
      Assert.AreEqual(typeof(ChildType2), list[1].GetType());
    }

Copyright (c) Marimer LLC