A straightforward approach for a create and detail screen for entity inheritance.

Introduction

Quite often in business apps, one has to deal with object oriented relationships between entities making up your domain model. For example, a person can be a student or a teacher or administration personnel. They share some common characteristics, but depending on their role, they have some specific properties.

Setting up such a data structure and corresponding detail screen  in LightSwitch is not difficult, but making things both flexible (in the sense that it should be easy to add other specialized tables) and maintainable, providing some kind of basic infrastructure for entity specialization can help.

We’ll focus on a very simple sample (read: entities without not too many fields).

Data modelling entity specialization

From a data perspective, there are multiple tables for each “specialization” category (e.g. Student, Teacher), carrying only the fields relevant for the specialized entity. Each specialised entity inherits a series of properties of a “master” entity (e.g. Person).

As you know, object orientation and relational database models doesn’t go well together. Obviously, the best way to make a perfect object oriented model of you data, is without a database, but then you might run into persistence problems :)  So, the idea is to get the best of both worlds. There is enough literature about this subject and what’s important for me, is how can Lightswitch cope as best as possible with entity inheritance.

A simple inheritance data model

As announced in the introduction, we will deal with a Person, Student and Teacher table. I’ll warn you immediately, later in this post you’ll see some code which might look quite involved, and which might look a bit over-kill for the simple setting I present you. I trust on your imagination to transpose this example to a more extended scenario where a master table can be inherited by 15 sub types and where each sub type has 56 fields.

You can see a simple master table (Person) with some fields (some of them mandatory).  A very important field is the Type field. It really drives the specialization relationships. The Type field has a choice list attached with following values:

The precise naming is extremely important. Students and Teachers refer to the “specialization” entity sets, which you’ll see here:

Note that both Student and Teacher have a mix of mandatory and optional fields. Both Teacher and Student refer via a 0..1 –>1 relationship to person.  So, database-technically,  a person can exist as a person as such. Again, database-technically, a person could be both a Teacher and a Student. Nonetheless, that’s not what we want here: we presume that upon creation of a person,  a clear choice of the person’s nature has been defined: a person as such can not exist and should be either a student, either a teacher (in other words, the specialization categories are mutual exclusive). These are prerequisites of my example.

The user interface for a student and teacher detail screen

We want only one screen which can handle both a student and a teacher entity. The reason why we want one screen is that we want to be “open for extension”.  The properties of the master entity (person) are on the first half of the screen, and the specialized properties ont he second half.

 

The specialized entities are organised with a tab control. Depending on the type property in the master entity, a particular tab is activated. The other tabs are set invisible, they have anyhow no data. Remember, the sub types are mutual exclusive.

Setting the correct tabs visible, and the incorrect invisble, is all possible with the .FindControl() method, but is quite cumbersome to do. Especially, in the scenario where additional sub types are introduced, controls being renamed, etc. So, a more generic approach is desirable.

 

 

 

A two-step approach for entity creation

A basic problem with the creation of entities based on specialization is introduced by the “type field” in the master entity (person). The problem, in the context of entity creation (on a create screen)  is that the type field defines the nature of the sub type, but obviously, the user can change her mind and change the type field to another value. What will you do when the user entered already 25 fields of the previous sub type? Delete the sub type and create another one? But maybe she will change back to the original sub type?

In short, that’s not a practical way of organizing things. Keep it simple and go for a two-step approach when it comes to creating specialized entities:

Step 1: present only the master entity fields and let the user choose a sub type (via dropdown or something else).

Step 2: from the moment the user clicked on save, the entity detail screen is opened with the correct sub type is shown in the tab control and the user can provide the specific sub type fields. The user can still edit the master entity fields except the type field.

In step 2, the user can cancel the save operation. When doing so, the master entity will still be there, so that later on, she can continue entering the sub type fields.

This 2 step create way, is an approach that works for me.  Nonetheless, the code I provide later in the post, can be easily adapted to cope with another transaction schema.

The Search Screen

Here again, keep it sample and restrict the list of fields in the result grid to the fields of the master entity. If that’s not good enough, mix everything together.

 

 The Server side

Nothing specific is necessary on the server side.

The domain model (the middle tier)

Also, nothing special to be foreseen, except that for making it possible that the entity type field in the people entity becomes read-only when the state of the entity in no longer in add mode. This can be done as follows:

 public partial class Person
    {
        partial void Type_IsReadOnly(ref bool result)
        {
            result = (this.Details.EntityState != EntityState.Added);
        }
    }

Next, we need a helper class in the middle tier. I’ll explain the purpose later, but provide the code here. Create a new class in the Common Project with following static method:

 public static class ApplicationCommonActivatorHelper
    {
        public static object CreateEntityInstance(string typeName)
        {
            Type t = Type.GetType(typeName);
            if (t != null)
                return Activator.CreateInstance(t);
            return null;
        }
    }

 The client side

The create new Person screen

It boils down to the following. Make sure to include to student nor teacher specific fields (they are provided on the detail screen) and that the choice list of the type field is up to date. The code behind class doesn’t contain anything specific.

The search screen

Again, very straightforward and no specific code behind content.

The detail screen

This one is more involved. We kept the most interesting (I hope at least) for the end. The screen design of the detail screen is very simple but the code behind contains some advanced techniques.

The tabs layout contains the two (mutual exclusive) sub types (student and teacher). The easiest way to get them there, is dragging and dropping from the PersonProperty in the viewmodel.

Ok, now we come to the most important part, the code behind of the detail screen.

My purpose is clear and simple :when a new sub type is introduced (e.g. adminPersonnel) I don’t want to touch the code behind of the detail screen, it should be simply self containing.

The main tasks of the code behind for this screen is:

  • make sure the correct tab is visible and gets the focus and make sure the non-relevant tabs are hidden.
  • make sure to instantiate the correct sub type in case it has not been instiantiated yet.
As said, the code behind is generic, it has no reference to teachers and students :)
        public string EntityTypeName { get; set; }
        public string ParentPropertyName { get; set; }
        public string ChildTabGroupName { get; set; }

partial void PersonDetail_InitializeDataWorkspace(List<IDataService> saveChangesTo)
        {
            ParentPropertyName = "PersonProperty";
            ChildTabGroupName = "PersonTabGroup";

            EntityTypeName = this.DataWorkspace.ApplicationData.Details.GetModel().EntitySets.Where(s => s.Name.ToUpper() == this.PersonProperty.Type.ToUpper()).Single().EntityType.Name;

            this.FindControl(ChildTabGroupName).ControlAvailable += (s, e) =>
            {
                var ctrl = e.Control as System.Windows.Controls.Control;
                var contentItem = ctrl.DataContext as Microsoft.LightSwitch.Presentation.IContentItem;
                foreach (var child in contentItem.ChildItems)
                {
                    IEntityReferenceProperty subEntity = child.Details as IEntityReferenceProperty;
                    if (subEntity.Name.ToUpper() == EntityTypeName.ToUpper())
                    {
                        child.IsVisible = true;
                        this.FindControl(child.ContentItemDefinition.Name).Focus();
                    }
                    else
                    {
                        child.IsVisible = false;
                    }
                }
            };

            InstantiateEntitySubTypeWhenNull(this.PersonProperty.Id, this.PersonProperty.Type, this.PersonProperty);
        }

        private void InstantiateEntitySubTypeWhenNull(int entityId, string entitySubTypeName, IEntityObject parentProperty)
        {
            IDataService dataService = this.DataWorkspace.ApplicationData;
            ICreateQueryMethod singleOrDefaultQuery = dataService.Details.Methods[entitySubTypeName + "_SingleOrDefault"] as ICreateQueryMethod;
            ICreateQueryMethodInvocation singleOrDefaultQueryInvocation = singleOrDefaultQuery.CreateInvocation(entityId);
            IEntityObject entityObject = singleOrDefaultQueryInvocation.Execute() as IEntityObject;

            if (entityObject == null)
            {
                //makes use of static class in common dll.
                parentProperty.Details.Properties[EntityTypeName].Value = ApplicationCommonActivatorHelper.CreateEntityInstance("LightSwitchApplication." + EntityTypeName);
            }
        }

 

As you see, everything is happening in the InitializeDataWorkspace method.

The first part is making sure that the correct tab is visible and gets the focus. The nice thing is that everything can be processed without any intimate knowlegde of the sub entities involved.

The second part is encapsulated in the InstantiateEntitySubTypeWhenNull private method. We first try to find out, if we still need to instantiate the sub type. Since we know, deliberately,  the sub type only by it’s name we have to use reflection to instantiate the entity type. For doing this, we need the ApplicationCommonActivatorHelper (I got this helper class via the lightswitch forum from LS__, many thanks !)  in the common project. The reason why we put this method in the common project is that the entity types are also stored over there. In silverlight it’s quite complicated to get in a generic way a reference to a referenced DLL via reflection. The problem is that you have to use the full qualified name containing also a version number which changes all the time.

The only 2 “entry points” in the code behind are the values of 2 properties:

 ParentPropertyName = "PersonProperty";
  ChildTabGroupName = "PersonTabGroup"

Extending the master entity with another sub type

The proof of the pudding is in the eating. Let’s try now to introduce a new sub type “AdminStaff” and check what’s the work to be done:

Create the AdminStaff entity and create relation to Person

Update the choice list with AdminStaffs (the entity set name of the new sub type)

 

Update the person detail screen

Just drag and drop the adminStaff navigation property from the viewmodel to the tabslayout.

That’s all, as you can see from following screen:

Conclusion

It’s perfectly possible in LightSwitch to make things a bit more generic, reusable and maintainable even without using the extension framework. So far, I have little experience with the LightSwitch extension framework, but probably the above would be a reasonable starting point for an “Inheritance based detail screen template extension“.