eXpress App Framework Team

This Blog

September 2008 - Posts

  • DC: Thinking of base library

    This post may be outdated. For the latest Domain Components concepts and examples refer to the current online documentation.

    This post is one in a series about our work on Domain Components (DC) framework and related component libraries. I’m just describing what we are working on, what problems we are facing and what results we've got so far.

    The new Domain Components framework will let developers create reusable domain components. Obviously, we should supply our variant of the domain component library. Our library should be full enough to let XAF developers start fast, and to provide a common well-established base for domain component developers. At the same time, it should be flexible enough to let developers replace or extend the parts that they don’t like.

    I think, in the end, we will have our domain component library derived in two parts – very specific base components, which will be reused across all other components, and common domain components, which can be replaced or extended as required. Let me illustrate this approach.

    Nearly every application needs information about a person. It can be just user information, or more detailed information on a customer, or very detailed information on a legal person. Obviously, there should be a base component Person, which will be referenced and reused by third-party domain components, and a real Person component, which contains more detailed information.

    Components that reference the base Person don't need much information on it. So, it may contain a FullName and ShortName, and, in rare cases, Birthday and Gender:

        public interface IPersonRef {
            string FullName { get; }
            string ShortName { get; }
            DateTime? Birthday { get; }
    	Gender Gender { get; set; }
        }

    A real Person component may have more properties: FirstName, MiddleName and LastName, or GivenName, Surname and FatherName (depends on the culture). We should provide the Person that is sufficient in common cases – like this:

    	[DomainComponent]
    	public interface IPerson : IPersonRef {
    		string FirstName { get; set; }
    		string MiddleName { get; set; }
    		string LastName { get; set; }
    	}

    You can use your own IPersonRef descendant in your application, instead of the IPerson. In this instance, all domain components that depend on the IPersonRef interface will work. For this purpose, we will provide a way to tell the system that "in this application the IPersonRef should be resolved into the RomanEremin.DC.General.IRussianPerson component".

    Now, we are at the genesis of creating a base domain component library, and considering an approach to implement a useful one. I think we should start writing a real-world application (this has been requested for some time). While writing, we will get core, common and application-specific components that can be included in our component library.

    What application should we write? I'm thinking of a generic CRM application. Most of us are intuitively familiar with applications of this type, so we won’t need to explain much, and most of us will be able to use it.

  • DC: Interfaces and BOModel

    This post may be outdated. For the latest Domain Components concepts and examples refer to the current online documentation.

    This post is one in a series about our work on Domain Components (DC) framework and related component libraries. I’m just describing what we are working on, what problems we are facing and what results we've got so far.

    Now, while I’m waiting for the XPO team, who promised me to support new requirements, I've found one more problem. In my BOModel node, I can see the IDeveloper and INote interfaces, but I can't see the IPerson interface. Since our ultimate goal is to provide the ability to create reusable domain components, I want to be able to set up all XAF-related aspects of the IPerson in the Application Model, and then reuse these settings in all places where the IPerson is implemented.

    But should I add all interfaces to the Application Model? What if the IDeveloper is inherited from the IDisposable, in addition to other interfaces? Should XAF add the IDisposable to the BOModel node? I guess not. There should be a way to mark the interfaces that are used for building entities later. I decided to add the DomainComponentAttribute that will serve as a mark:

    	[DomainComponent]
    	public interface IPerson {
    		string FirstName { get; set; }
    		string MiddleName { get; set; }
    		string LastName { get; set; }
    		string FullName { get; }
    		DateTime Birthday { get; set; }
    		Gender Gender { get; set; }
    	}

    Now, after I've refacored the code that analyses types inside XAF, the interfaces that use the DomainComponent attribute are added to the BOModel node:

    DC3-01

  • Associations in DC

    This post may be outdated. For the latest Domain Components concepts and examples refer to the current online documentation.

    This post is one in a series about our work on Domain Components (DC) framework and related component libraries. I’m just describing what we are working on, what problems we are facing and what results we've got so far.

    In my previous post I was looking at INote interface:

    	public interface INote {
    		string Text { get; set; }
    		IItemWithNotes Owner { get; set; }
    	}

    There is another thing I don’t like in this code: I don’t want the INote interface to know about the IItemWithNotes interface. But currently in XPO I have to define back references. Why? Let me explain the problem. Look at the class structure below:

        public class Developer : XPObject {
            private string name;
            public Developer(Session session) : base(session) { }
            public string Name {
                get { return name; }
                set { SetPropertyValue("Name", ref name, value); }
            }
            [Association]
            public XPCollection Notes { get { return GetCollection("Notes"); } }
        }
        public class Note : XPObject {
            private string text;
            public Note(Session session) : base(session) { }
            public string Text {
                get { return text; }
                set { SetPropertyValue("Text", ref text, value); }
            }
        }

    These classes won't work correctly in XPO. In a relational database, a One-to-Many relation is performed by adding a foreign key to the table. For the classes above, you would create a table structure where the Note table has the foreign key column that stores a Developer's OID:

    DC2-01

    To retrieve the Notes that are associated with a particular Developer ID, you would write an SQL query like this: "select * from Note where Developer = <developer id>". XPO generates all queries for you. To generate the query I showed to you, XPO requires that the Note class has a property mapped to a foreign key column. In other words, the Note class should look like this:

        public class Note : XPObject {
            private string text;
            private Developer developer;
    
            public Note(Session session) : base(session) { }
            public string Text {
                get { return text; }
                set { SetPropertyValue("Text", ref text, value); }
            }
            [Association]
            public Developer Developer {
                get { return developer; }
                set { SetPropertyValue("Developer", ref developer, value); }
            }
        }

    In this instance, XPO will find both ends of the Developer-Note association and create all necessary constraints and foreign keys.

    But I’m working on the Domain Component library. My goal is to provide a framework for domain components – a puzzle's independent pieces. While writing the Note component, I don’t want it to know how anybody uses it. For instance, it can be used in the following entity (I know this is not a good design, but I still think it should be possible to do so):

        public interface IFullOfNotes {
            IList<INote> PublicNotes { get; }
            IList<INote> PrivateNotes { get; }
            IList<INote> DraftNotes { get; }
        }

    To map the IFullOfNotes to the database, the Note table should have three foreign keys. If the XpoBuilder can generate back-reference (foreign key) properties automatically, our DC components will be much more flexible.

    So, I want to write the following code in my Notes library:

    	public interface IItemWithNotes {
    		IList<INote> Notes { get; }
    	}
    
    	public interface INote {
    		string Text { get; set; }
    	}

    I've added tests that express my desire. And again… I'm waiting for an answer from the XPO team. Stay tuned.

  • DC: One-to-many relationship

    This post may be outdated. For the latest Domain Components concepts and examples refer to the current online documentation.

    In the first post of the series devoted to our new domain component (DC) technology, I tried to show an interface-defined entity in a UI.

    I used some IPerson component as an example:

    	public enum Gender { Unknown, Male, Female }
    
    	public interface IPerson {
    		string FirstName { get; set; }
    		string MiddleName { get; set; }
    		string LastName { get; set; }
    		string FullName { get; }
    		DateTime Birthday { get; set; }
    		Gender Gender { get; set; }
    	}
    	[DomainLogic(typeof(IPerson))]
    	public class PersonLogic {
    		public static string Get_FullName(IPerson self) {
    			return self.FirstName + " " + self.LastName;
    		}
    	}

    IPerson is a library interface that will be used as an entity rarely. In a real-world application, another entity is expected. For instance, it may be a Developer, the entity that implements the IPerson interface, in addition to other interfaces. Assume the Developer contains Notes. So, we add the INote and IItemWithNotes library interfaces:

    	public interface IItemWithNotes {
    		IList<INote> Notes { get; }
    	}
    
    	public interface INote {
    		string Text { get; set; }
    		IItemWithNotes Owner { get; set; }
    	}

    Combining these two interfaces, we get the IDeveloper interface:

    	public interface IDeveloper : IPerson, IItemWithNotes{ 
    	}

    I change the entity registration code, which I showed in the previous post, to the following:

    		public override void Setup(XafApplication application) {
    			XafTypesInfo.Instance.AddEntityToGenerate("Developer", typeof(IDeveloper));
    			XafTypesInfo.Instance.GenerateEntities();
    			base.Setup(application);
    		}

    If I run my application now, it won't work. The problem is that I should map the INote interface to a real entity. Here, I have a design challenge: should XAF generate other entities for the Developer entity, or should it require that all Developer-related entities are registered manually? Currently, I think it is better to make a developer responsible for registering entities. First, it will give him direct control on what happens in the physical database. Second, our automatic logic can be wrong. So, I change the entity registration code again:

    		public override void Setup(XafApplication application) {
    			XafTypesInfo.Instance.AddEntityToGenerate("Developer", typeof(IDeveloper));
    			XafTypesInfo.Instance.AddEntityToGenerate("Note", typeof(INote));
    			XafTypesInfo.Instance.GenerateEntities();
    			base.Setup(application);
    		}

    After I made these changes, my application works:

    DC-06

    Note that the FullName property is calculated properly. Remember that we declared this property with a getter only. However, we implemented the PersonLogic class that contains the Get_FullName method. When the XpoBuilder generated the implementation of the Developer object, it uses the Get_FullName method to generate the FullName property.

    As you can see in the screenshot above, the "Link" and "Unlink" Actions are available (they are circled). This means that the system thinks that a Note object can be created independently. I need a way to tell the XpoBuilder that the IItemWithNotes.Notes is an aggregated collection. I want to be able to write this:

    	public interface IItemWithNotes {
    		[Aggregated]
    		IList<INote> Notes { get; }
    	}

    I still can't do that. So, I've written a test that shows what I need, and now I’m waiting for the Xpo team. I'll focus on other problems with associations. Stay tuned.

  • Writing Domain Components framework (DC)

    This post may be outdated. For the latest Domain Components concepts and examples refer to the current online documentation.

    I'm starting a series of posts on my blog to share our experience while designing the domain component (DC) technology. It will be useful for us to know whether you like what we are doing. If not, we'll be able to correct our strategies early, before we make any mistakes.

    I spent last month with the XAF Core team designing the subsystem that manages type information in XAF. Until now, the framework obtained this information directly from XPO metadata, sometimes using Reflection.

    The new DC technology operates with interfaces. So now, the framework needs type information about interfaces. To get this information, we can't use XPO metadata, because it does not contain it. Even if we could, we don't like this approach, because we've targeted it to abstract away from a particular ORM tool. So, we decided to write a subsystem that would allow its users to avoid dealing with the sources of the type information.

    When considering which approach to choose in implementing this system, Aleksey and Mike, developers from the XAF Core team, suggested an agile approach: we should refactor until the new subsystem emerges. I voted for the "Big Refactoring" approach: just write a new system, and then call it in the old methods and helpers. My approach would be faster, but it was going to lead to the crash of the entire system in a couple of weeks. This led me to do research, and partially write the type info subsystem in my private branch of XAF sources.

    The next step was to integrate the new subsystem. Luckily, it was just after we had released the XAF 2008 vol. 2, the period when most of the XAF developers were busy fixing bugs and answering questions, and Aleksey was on vacation. This allowed me to convince Mike. We decided to integrate the new system and see how it behaved. Aleksey expected something like this from me. He even called us to ask if he should take one more week, so he wouldn’t see what we were doing. The next two weeks we were getting XAF back to the working state. Since then, we've been dealing with binding, type descriptors, complex property paths, structures, XPO dictionary (it directly affects what tables are created in the database), unit tests (should we reload type info between tests or not, you know, there are 1400+ tests), performance optimization and that sort of thing.

    Anyway, it now seems that we have the type information subsystem working. This means that we can start creating a reference application using the DC base library. We will write this library as we go.

    The base of the DC approach is the runtime interface implementation. In short, we write an interface for a domain object and a "domain logic" class that contains "non-obvious" parts of the domain object's implementation. This approach provides us with the following advantages: we avoid writing obvious code, remove dependencies on a particular ORM system, inject the aspects like security or remoting flexibly, and use multiple inheritance to construct the final application from blocks.

    Real objects are generated from interfaces by the XpoBuilder class, written by the XPO team. This class is located in the XAF type information system. When you register an interface as an entity, the XpoBuilder implements an object for you. There is an important note here: you can build domain objects only once. After an object is generated, it cannot be modified.

    At this point, I have the XpoBuilder working at some level, and need to have a few things implemented in the type information system. So, let's try to build an application using the DC approach.

    Disclaimer: The code you see here cannot be run using XAF 2008 vol.2, or earlier. Moreover, I cannot be sure it will work in the final version of DC. This is just the code that works at the moment I wrote this post.

    Let’s add a Person entity to our application. In the new DC approach, the simplistic Person library element may look like this:

    	public enum Gender { Unknown, Male, Female }
    
    	public interface IPerson {
    		string FirstName { get; set; }
    		string MiddleName { get; set; }
    		string LastName { get; set; }
    		string FullName { get; }
    		DateTime Birthday { get; set; }
    		Gender Gender { get; set; }
    	}
    	[DomainLogic(typeof(IPerson))]
    	public class PersonLogic {
    		public static string Get_FullName(IPerson self) {
    			return self.FirstName + " " + self.LastName;
    		}
    	}

    Now, I need to ask XAF to generate an entity for it. For this purpose, I override the Setup method in my Module class in the following manner:

    		public override void Setup(XafApplication application) {
    			XafTypesInfo.Instance.AddEntityToGenerate("Person", typeof(IPerson));
    			XafTypesInfo.Instance.GenerateEntities();
    			base.Setup(application);
    		}

    I call the GenerateEntities method here, because at this time XAF does not performs this call inside (I've made a note to correct this).

    I run the Model Editor. As you can see, the IPerson entity is added to the BOModel node:

    IPerson node in BOModel

    Expanding the Views node I can see that the List and Detail Views are already generated. Oops, I cannot see a child node representing the IPerson in the NavigationItems node. I've made a note to fix this. For now, I'll add this child node manually, together with the corresponding node in the CreatableItems node:

    Adding navigation and creatable items nodes

    I run the application. In spite of the child node that I've added to the NavigationItems node, there is no "Person" in the navbar:

     

    DC-04

    After some investigation it turned out that I have no permissions for the newly generated entity. The fact is that the SecuritySimple strategy assumes that we are working with objects only. Look how it gives permissions:

    	public class SecuritySimple : SecurityBase  {
    		...
    		protected override PermissionSet ReloadPermissions() {
    			...
    				result.AddPermission(new ObjectAccessPermission(typeof(object), ObjectAccess.AllAccess));
    			...
    		}
    		...
    	}

    Since an interface is not a descendant of the System.Object, I have no rights to it. I've written a unit test for XAF developers. It shows that their assumption is wrong. In order to proceed, I'll write my own SecuritySystem strategy:

    	public class MySecurity : SecuritySimple {
    		protected override System.Security.PermissionSet ReloadPermissions() {
    			System.Security.PermissionSet result = base.ReloadPermissions();
    			foreach(ITypeInfo entity in XafTypesInfo.Instance.RegisteredEntities) {
    				result.AddPermission(new ObjectAccessPermission(entity.Type, ObjectAccess.AllAccess));
    			}
    			return result;
    		}
    	}

    My Security gives all permissions to all registered entities (all persistent types). This is not a system you would like to have in a real application, but it will let me move on.

    Now, I run my application, and "voila":

    DC-05

    In the next post, I'll tell you about my efforts to associate objects using the One-to-Many relationship. Now, you are free to share your ideas on this post.

LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, ASP.NET, WinForms, HTML5 or Windows 10, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2017 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners