in
Forums
Blogs
DevExpress.com
Client Center
Support Center
DevExpress Channel

eXpress App Framework Team

  • Sneak peek: navigation tree in XAF v2009 vol 2 (eXpressApp Framework)

    Another new feature in v2009 vol 2 of XAF. This is very early stuff, I hear the beta won't be along before the end of June.

    XAF now supports a tree view for navigation purposes, both in Windows Forms and ASP.NET. Of course this is very useful for applications that have a lot of different views. Here are some examples of navigation trees on both platforms - multiple levels, node images, ...

    In conjunction with the navigation bar, there are many different styles that can be configured for the visualization of navigation structures. The Navigation Demo that comes with XAF allows programmers to change options and see their immediate effects in the UI.

  • Sneak peek: collection validation in XAF v2009 vol 2 (eXpressApp Framework)

    Just a very quick look at a new feature in v2009 vol 2 of XAF. This is very early stuff, I hear the beta won't be along before the end of June.

    We have added the ability to apply rules to collection properties. For instance, in this screenshot you can see a rule failing that is defined to check against the sum of values over the collection. The values in the field "Sum between 0 and 50" are supposed to add up to 50 at most, but since the sum is actually 60, the rule triggers a validation error.

    Here's another example. In the "lowest" of the three stacked windows, you can see a collection of objects with a field called "Item Unique Value". The rule that is applied to the collection checks whether the field values are unique across the entire collection. When the detail view is brought up and the second item is edited to have the same value in that field as the first object, the rule triggers a validation error.

  • My TechEd US BOF Session is in, thanks to you!

    A little while ago, I was asking for help with the voting on my TechEd US BOF session, and today I've received the confirmation that it's made it in! Great news, and even better because the title of the session, which had been the reason for certain, shall we say, ridicule, has magically changed now to this:

    Parallelization: Benefits of Functional Programming Ideas

    It will take place on Tuesday, May 12th, from 1pm to 2:15pm - rooms aren't assigned yet. I hope to see some of you there!

    All of those who have helped with their votes (and those who haven't but who manage to claim with a straight face that they have) - if you meet me somewhere, I owe you a beer! Okay, it should be a place where it is technically possible to acquire a beer. And of course you can drink water if that's what you prefer. You know. Thank you very much for your help!

    And finally: if you have still not voted for your favorite DevExpress products, please click this link and help us there as well! Thank you! Who knows, maybe it's even worth a second beer :-)

  • Vote for my TechEd US BOF session, please!

    I have submitted a BOF (that's birds of a feather, btw) session for TechEd US. Unfortunately there's an ugly voting process in the way - so here's a chance for you to help me get in by voting for us! Yes, that's everybody - whether you're going to be there or not ;-)

    Here's the page where the voting takes place. Very easy and quick. And the title of the BOF session is this:

    Benefits of Functional Programming ideas in the context of parallelization efforts

    I know that Mark and Paul have also submitted sessions, but last time I looked, they were not in the list yet. Maybe they're going to be added by the time you get there - in any case, the titles of these two sessions are these:

    High-speed Development in C# and VB with CodeRush Xpress

    Data Access Layer Tools and Practices: What works and what doesn't?

     

    Thanks for your help, much appreciated!

  • TechEd Europe coming up

    Gary and I are going to Barcelona tomorrow for TechEd Europe. We're going to man our booth there, hoping to meet up with lots of existing customers as well as those who still want to become customers, showing of everything that's new with DevExpress.

    If you are in Barcelona, please make sure to come by the booth and say hi. Or come to my vendor session and win something!

  • DC: Combining associations

    Remember that the base idea of creating the Domain Component Technology is to be able to compose an application from reusable blocks – components. These blocks can be bought from third-party sources. This allows you to reuse well-polished blocks. But this also means that you cannot change the sources of these blocks. The other day, while working on XCRM, I tried to combine two component sets, but encountered a problem. In this post, I want to discuss this problem in detail.

    Imagine that you have two domain component sets – Tasks and Ownership Security. Both of them contain DC interfaces, domain logic, Controllers, Actions, UI settings and end-user documentation. You want to reuse everything. Now, I will focus on DC interfaces only. Here are the interfaces that are related to Tasks:

    [DomainComponent]
    public interface ITask {
        string Subject { get; set; }
        ITaskOwner AssignedTo { get; set; }
    }
    [DomainComponent]
    public interface ITaskOwner {
        IList<ITask> Tasks { get; }
    }

    The developer, who designed this component, made it independent from the User concept. A Task can be assigned to any object that implements the ITaskOwner.

    Here are the interfaces that define Owership Security:

    [DomainComponent]
    public interface ISecuredItem {
        ISecurityOwner Owner { get; set; }
    }
    [DomainComponent]
    public interface ISecurityOwner {
        bool IsManager { get; set; }
    }

    Here, a user can only see his/her own items, if he/she has the IsManager property set to false. The developer, who designed this component, made it independent from other components again.

    Now, you want to assemble your own system out of these components. You want to have Tasks secured using Ownership Security. So, you declare combined domain components and register them as entities in the following manner:

    [DomainComponent]
    public interface IUser : ITaskOwner, ISecurityOwner {
    }
    
    [DomainComponent]
    public interface ISecuredTask : ITask, ISecuredItem {
    }
    [Test]
    public void TestOwner() {
        RegisterDC<IUser>();
        RegisterDC<ISecuredTask>();
        IUser user = CreateObject<IUser>();
        ISecuredTask task = CreateObject<ISecuredTask>();
        task.Owner = user;
    }

    What will be generated from these components? The answer is quite obvious. The IUser and ISecuredTask interfaces will be implemented and mapped to the following tables:

    dc06-1

    This structure allows you to assign different Users to a Task object's AssignedTo and Owner properties. But, what if you want these properties always set to the same User? For this purpose, I could provide additional logic that would synchronize both these properties. However, I would still have two associations in the database, which I don’t need. I want to have a table structure like this:

    dc06-2

    I need a way to tell the XpoBuilder that I want the ITask.AssignedTo and ISecuredItem.Owner to be mapped to the same column/property. Remember, I cannot change the source code of the Tasks and Ownership Security components. I can only change my IUser and ISecuredTask. So, to implement the ISecuredTask in accordance to my requirement, I can write something like this:

    public class SecuredTask : XPObject, ISecuredTask  {
        public User Owner  
        ITaskOwner ITask.AssignedTo {
            get { return (ITaskOwner)Owner; }
            set { Owner = (User)value; }
        }
        ISecurityOwner ISecuredItem.Owner {
            get { return (ISecurityOwner)Owner; }
            set { Owner = (User)value; }
        }
    }

    Here, I build a new association and explicitly implement both the ITask.AssignedTo and ISecuredItem.Owner properties.

    It'd be much easier, if I could tell the XpoBuilder something like this:

    [DomainComponent]
    public interface ISecuredTask : ITask, ISecuredItem {
        [Implements("ITask.AssignedTo", "ISecuredItem.Owner")]
        new IUser Owner { get; set; }
    }

    This would indicate that I have a new association between a SecuredTask and User, and I want the ITask.AssignedTo and ISecuredItem.Owner properties to be “mapped'” to it. But something tells me that this would break associations. For instance, when I try to get the User.Tasks, XPO won’t be able to find a back reference (it expects the AssignedTo property, but it is non-persistent). So again, I had to beg the XPO team for some alias magic.

    Please, XPO team, make my (our?) dream come true!

    Posted Oct 10 2008, 04:23 PM by Roman Eremin with no comments
    Filed under: , , ,
  • XCRM: Activities

    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.

    This post is entirely devoted to Activities. It's more interesting to design Activities, because they are more complex than other components that I described earlier. There are three points I consider when designing Activities:

    1. There can be many Activity types: a phone call, email, meeting/appointment, task, campaign activity or case/service activity.
    2. An Activity has an owner - a person who is assigned to perform the Activity. The person should be able to see all his/her activities in one list.
    3. An Activity can be related to a Contact, Account, Lead, Opportunity, MarketingCampaign or any other “main” entity record in the system.

    Let's go through all these points:

    1. We can divide all Activities into two basic categories:

    • Event

    An Activity that has clear start and end points. Generally, Events can be scheduled on a calendar. So, the system should be able to show all Events in one Calendar view.

    • Task

    A less time-bound Activity than the Event. It may have a DueDate only.

    Simple CRM systems work with Tasks and Events simultaneously. More complex systems distinguish these Activity types, so they can track specialized information, like a phone number for a call.

    In our application, let’s define a Task and Event. In addition, let's create specialized Activities: a Phone Call, as a variation of the Task Activity, and an Appointment, as a variation of the Event Activity. Here are the tests that express these requirements:

    [Test]
    public void ActivityTypesTest() {
        ICall call = ObjectSpace.CreateObject<ICall>();
        IAppointment appointment = ObjectSpace.CreateObject<IAppointment>();
        ITask task = ObjectSpace.CreateObject<ITask>();
        IEvent eventObj = ObjectSpace.CreateObject<IEvent>();
    
        IList<IActivity> allActivities = ObjectSpace.GetObjects<IActivity>(null);
        Assert.AreEqual(4, allActivities.Count);
        IList<IEvent> allEvents = ObjectSpace.GetObjects<IEvent>(null);
        Assert.AreEqual(2, allEvents.Count);
    }
    [DomainComponent]
    public interface IActivity {
        string Subject { get; set; }
        
        IActivityTarget RelatedTo { get; set; }
    }
    
    [DomainComponent]
    public interface IEvent : IActivity {
        DateTime StartTime { get; set; }
        DateTime EndTime { get; set; }
        bool AllDayEvent { get; set; }
    }
    public enum TaskPriority { Low, Medium, High }
    
    [DomainComponent]
    public interface ITask : IActivity {
        TaskPriority Priority { get; set; }
        DateTime DueDate { get; set; }
    }
    [DomainComponent]
    public interface ICall : ITask {
    }
    [DomainComponent]
    public interface IAppointment : IEvent {
    }

    I don’t call the RegisterDC method in this test, because I've moved all Activity registrations to the SetUp method (it is called before a test is started by NUnit):

    [SetUp]
    public override void SetUp() {
        base.SetUp();
        RegisterDC<ILead>();
        RegisterDC<ICampaign>();
        RegisterDC<IContact>();
        RegisterDC<IAccount>();
        RegisterDC<IOpportunity>();
        RegisterDC<IEvent>();
        RegisterDC<IAppointment>();
        RegisterDC<ITask>();
        RegisterDC<ICall>();
        Generate();
    }

    Note that I don't register the IActivity. This is a base abstract interface that cannot be instantiated in our system.

    2. An Activity has an owner – usually the system's user. The owner may have a list of Activities:

    [DomainComponent]
    public interface ITestUser : IActivityOwner {
    }
    [TestFixture]
    public class ActivityTests : BaseTest {
        …
        [Test]
        public void ActivityOwnershipTest() {
            ITask blogAboutDC = ObjectSpace.CreateObject<ITask>();
            ITestUser romanEremin = ObjectSpace.CreateObject<ITestUser>();
            blogAboutDC.Owner = romanEremin;
            Assert.AreEqual(blogAboutDC, romanEremin.Activities[0]);
        }
    }
    [DomainComponent]
    public interface IActivityOwner {
        IList<IActivity> Activities { get; }
    }
    [DomainComponent]
    public interface IActivity {
        
        IActivityOwner Owner { get; set; }
    }

    3. An Activity can be related to a Contact, Account and any other “main” record in the system. Some systems allow you to link Activities to several records of different type, but I guess this is just a workaround to make programming easier. Our DC framework should be flexible, so I will design it in “the right way”:

    [Test]
    public void ActivityRelationTest() {
        IAccount account = ObjectSpace.CreateObject<IAccount>();
        IContact contact = ObjectSpace.CreateObject<IContact>();
        ILead lead = ObjectSpace.CreateObject<ILead>();
        IOpportunity opportunity = ObjectSpace.CreateObject<IOpportunity>();
        ITask task = ObjectSpace.CreateObject<ITask>();
    
        task.RelatedTo = account as IActivityTarget;
        Assert.IsNotNull(account.Activities);
        Assert.AreEqual(task, account.Activities[0]);
        task.RelatedTo = contact as IActivityTarget;
        Assert.AreEqual(task, contact.Activities[0]);
        task.RelatedTo = lead as IActivityTarget;
        Assert.AreEqual(task, lead.Activities[0]);
        task.RelatedTo = opportunity as IActivityTarget;
        Assert.AreEqual(task, opportunity.Activities[0]);
    }

    To make this test pass, I should define the IActivityTarget interface. Only the components that implement this interface will be able to be added to the “related to” list:

    [DomainComponent]
    public interface IActivityTarget {
        IList<IActivity> Activities { get; }
    }

    [DomainComponent]
    public interface IActivity {
        
        IActivityTarget RelatedTo { get; set; }
    }

    Note that I have not declared a requirement that I want to be able to see all Activity Targets in one list. In that instance, we would have to create a common table for them somehow, and this might be a major restriction on possible use cases of Activities.

    I don’t require all IActivityTarget implementers to be in one list, but I still need to be able to edit the RelatedTo field in a UI. This editor should let end-users locate an object of one of predefined types. CRM systems tend to achieve this by adding a combo box with object types. In this instance, end-users first select the type, and then the object of this type:

    dc5-01

    As far as I know,  we don’t have an editor of this kind yet. So, I've written down a task to implement this editor and added a test that describes this scenario. To tell you the truth, the XpoBuilder has trouble with this test. Let me explain in more detail.

    How to persist a reference to the interface that is implemented in several classes.

    Here, we have a case of leaky abstraction, impedance mismatch and that sort of thing. The fact is that a relational database is not mapped to the OO world easily. In our case – how would you manually implement the scenario where an Activity can be related to one of several records, say, Account or Contact? I see two ways (note that we exclude the requirement that the Contact and Account must have the same base).

    The first approach - Multiple associations.

    Make two associations:

    dc5-02

    The implementation of the IActivity.RelatedTo property will look like this:

    public IActivityTarget RelatedTo {
        get {
            if (Contact != null) return Contact;
            if (Account != null) return Account;
            return null;
        }
        set {
            Contact = null;
            if (value is Contact) {
                Contact = value as Contact;
                Account = null;
                return;
            }
            if (value is Account) {
                Contact = null;
                Account = value as Account;
                return;
            }
        }
    }

    This code is ugly, but other than that it looks fine – the Contact and Account have a list of Activities. An Activity allows you to access a Contact or Account as IActivityTarget. But let’s make a simple change. Let's add the Active property to the IActivityTarget interface:

    public interface IActivityTarget {
        IList<IActivity> Activities { get; }
        bool Active { get; set; }
    }

    XPO will not be able to apply a criteria “[RelatedTo.Active] = true” to Activities on the server, because the RelatedTo is not an FK field and cannot be used in a simple SQL query.

    The second approach - Intermediate table.

    Create an intermediate table for the IActivityTarget interface:

     dc5-03

    In this instance, the implementation of the IActivityTarget interface in the Account and Contact would redirect all calls to the aggregated ActivityTarget class:

    public IList<IActivity> Activities {
        get { return ActivityTarget.Activities; }
    }

    Adding extra properties to the IActivityTarget interface won’t be a problem:

    public interface IActivityTarget {
        IList<IActivity> Activities { get; }
        bool Active { get; set; }
    }

    ... because these properties will be contained in the ActivityTarget class, and the Contact and Account will redirect calls to it:

    public bool Active {
        get { return ActivityTarget.Active; }
        set { ActivityTarget.Active = value; }
    }

    But this approach yields another problem – since we hide an indirection, any Activity-related criteria for, say, a Contact (“Activities.Count > 0”) will not be evaluated on the server, because there is no association between the Contact and Activity. As an alternative, we could translate “Activites.Count” to “ActivityTarget.Activities.Count”. This translation might not be very obvious and would lead to problems.

    As you can see, that is something for XPO developers to think about. I’m going to talk to them and consider the approach we should use...

    Good news! XPO can use persistent aliases to map the Account’s Activities to its ActivityTarget.Actvities. So, we will use the Intermediate Table approach with aliases, and we might not have problems related to criteria. My Activity components can work as is and I can proceed further while waiting for the XpoBuilder to generate what I want.

    Posted Oct 09 2008, 12:46 PM by Roman Eremin with 6 comment(s)
    Filed under: , ,
  • XCRM: Marketing Campaigns and Customer support

    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.

    Marketing Campaigns

    A Marketing Campaign has no relationships. However, a Lead and Opportunity may reference a Campaign:

    [Test]
    public void CampaignRelationships() {
        RegisterDC<ILead>();
        RegisterDC<ICampaign>();
        RegisterDC<IContact>();
        RegisterDC<IAccount>();
        RegisterDC<IOpportunity>();
        Generate();
        ICampaign campaign = ObjectSpace.CreateObject<ICampaign>();
        campaign.Name = "Playboy back cover";
        ILead lead = ObjectSpace.CreateObject<ILead>();
        IOpportunity opportunity = ObjectSpace.CreateObject<IOpportunity>();
        lead.Campaign = campaign;
        opportunity.Campaign = campaign;
    }
    [DomainComponent]
    public interface ICampaign {
        string Name { get; set; }
    }
    [DomainComponent]
    public interface ILead {
        
        ICampaign Campaign { get; set; }
    }
    [DomainComponent]
    public interface IOpportunity {
        
        ICampaign Campaign { get; set; }
    }

    Instead of adding the Campaign reference property to both the ILead and IOpportunity components, I could add the ICampaignResult interface and inherit the ILead and IOpportunity from it. But I think, this is overkill. However, if one more class references the Campaign, and business logic is supplied for this reference, I'll choose this approach.

    Cases (customer support incidents)

    A Case is a record about a user's inquiry or problem. It is linked to an Account and/or a Contact. If the Contact is set, the Account is initialized from the Contact’s Account, if any:

    [Test]
    public void CaseRelationships() {
        RegisterDC<IContact>();
        RegisterDC<IAccount>();
        RegisterDC<ICase>();
        RegisterDC<IOpportunity>();
        Generate();
        IContact roman = ObjectSpace.CreateObject<IContact>();
        roman.FirstName = "Roman";
        roman.LastName = "Eremin";
        IAccount dx = ObjectSpace.CreateObject<IAccount>();
        dx.Name = "DevExpress";
    
        ICase case1 = ObjectSpace.CreateObject<ICase>();
        case1.Subject = "1";
        case1.Contact = roman;
        Assert.IsNull(case1.Account);
    
        ICase case2 = ObjectSpace.CreateObject<ICase>();
        case2.Subject = "2";
        case2.Account = dx;
        Assert.IsNull(case2.Contact);
    
        roman.Account = dx;
        ICase case3 = ObjectSpace.CreateObject<ICase>();
        case3.Subject = "3";
        case3.Contact = roman;
        Assert.AreEqual(dx, case3.Account);
    }

    [DomainComponent]
    public interface ICase {
        string Subject { get; set; }
        IAccount Account { get; set; }
        IContact Contact { get; set; }
    }

    To make this test pass we need an extra code, in addition to the interface. We need domain logic for the ICase to initialize the ICase.Account property by the ICase.Contact's Account:

    [DomainLogic(typeof(ICase))]
    public class CaseLogic {
        public static void AfterChange_Contact(ICase self) {
            if (self.Contact != null && self.Account == null) {
                self.Account = self.Contact.Account;
            }
        }
    }

    Here is how we are planning to add domain logic to the classes that are generated by the XpoBuilder. We are going to use naming conventions. If the XpoBuilder finds a method named “AfterChange_XXX”, it tries to call this method in the generated setter of a XXX property. We'll probably add the attribute-based binding in the future.

    A Case should have a human-readable ID that can be used in communications to quickly reference the Case. This ID should be generated only once, when a new object is beeing created. In addtion, the ID property should be read-only, but persistent.

    Here is one more DC design challenge: how domain logic can specify the initial value of a read-only property? Should I add an AfterConstruction method to the business logic to set a value? But in this instance, I'll have to provide some system interfaces to call the ID’s setter. So, I think it is better to implement the Init_XXX domain logic methods that will allow you to set a property's initial value for a newly created object.

    Since the ID property doesn't have a setter, it won't be persisted by default. To make this property persistent, I've decided to decorate it with the PersistentDc attribute. It could make sence to call this attribute Persistent, but in XAF this name conflicts with XPO’s Persistent attribute. So, it will be PersistentDc for now, and we will see what to do with it later.

    [DomainComponent]
    public interface ICase {
        [PersistentDc]
        string ID { get; }
        
    }
    [DomainLogic(typeof(ICase))]
    public class CaseLogic {
        public static string Init_ID(ICase self) {
            //ToDo: Change to "select count(*)+1 from case" analog
            return Guid.NewGuid().ToString();
        }
        
    }

    This is a very simplistic implementation. I will return with a more realistic code, as soon we have a proper infrastructure.

    Posted Oct 07 2008, 04:01 PM by Roman Eremin with 3 comment(s)
    Filed under: , ,
  • SDN Conference next week

    Just a quick note: I'll be speaking at the SDN Conference in Noordwijkerhout in the Netherlands next week. We're not exhibiting there, but of course I'll be looking out for DevExpress customers nevertheless - so if you're there, make sure to say hi! Of course I'll also appreciate you coming to my sessions on F# :-)

  • XCRM: Leads and opportunities

    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.

    A Lead is not directly linked to a Contact or Account. It can be converted into an Account, Contact or Opportunity later. I’ll just capture the fact that the Lead class exists and may have some basic info:

    [Test]
    public void LeadsRelationships() {
        RegisterDC<ILead>();
        Generate();
        ILead romanFromDx = ObjectSpace.CreateObject<ILead>();
        romanFromDx.FirstName = "Roman";
        romanFromDx.LastName = "Eremin";
        romanFromDx.CompanyName = "DevExpress";
    }
    [DomainComponent]
    public interface ILead {
        string FirstName { get; set; }
        string LastName { get; set; }
        string CompanyName { get; set; }
    }

    At this point, I’m not sure that the ILead should be inherited from the IPerson. So, I've just added the FirstName and LastName properties, and made a note to resolve this problem later.

    An Opportunity is a more probable possibility for business. It is related to an Account (who you want to sell to) and Product (what you want to sell). Simple CRM systems do not track products and prices, so I will leave them out in our CRM app. Here is the test for Opportunities:

    [Test]
    public void OpportunityAccountRelationships() {
        RegisterDC<IContact>();
        RegisterDC<IAccount>();
        RegisterDC<IOpportunity>();
        Generate();
        IAccount dx = ObjectSpace.CreateObject<IAccount>();
        dx.Name = "DevExpress";
        IOpportunity sellComponents = ObjectSpace.CreateObject<IOpportunity>();
        sellComponents.Name = "Sell some third-party components to DX";
        sellComponents.Account = dx;
        Assert.IsTrue(Enumerator.Exists<IOpportunity>(dx.Opportunities, sellComponents));
    }

    To make this test pass I need the following:

    [DomainComponent]
    public interface IOpportunity {
        string Name { get; set; }
        IAccount Account { get; set; }
    }
    [DomainComponent]
    public interface IAccount {
        
        IList<IOpportunity> Opportunities { get; }
    }

    Posted Oct 03 2008, 09:00 AM by Roman Eremin with 2 comment(s)
    Filed under: , ,
  • XCRM: Writing real-world CRM application

    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.

    I'm starting to write a generic CRM application, to try the new domain component (DC) technology and decide what classes should be included in our Domain Component technology. This application should be suitable for small business and extendable to support specific markets. While writing, I'm sure to find bottlenecks in XAF that we will have to fix.

    In general, a CRM application should be able to manage the following things:

    • Contacts
    • Accounts
    • Leads
    • Opportunities
    • Products
    • Appointments/Calendar/Tasks
    • Service Requests
    • Marketing Campaigns

    In addition, even a basic CRM system should have a security system that is aware of organization structure.

    To emphasize the most interesting design parts, I won't detail every domain class. I will create most classes in brief form, and then extend them as required. In the first iteration, I will focus on class relationships.

    Contact. Can belong to an Account. So, it has the Account property.

    Account. Can belong to another Account. So, it has the ParentAccount and Subaccounts properties. In addition, it should have a Contacts list, and a PrimaryContact, which may not belong to the Contacts list.

    I’m using TDD, so I will capture this knowledge in unit tests.

    Unit tests for Domain Components

    To write tests for domain components, I think I will need a DC infrastructure to be able to generate components from interfaces and business logic. I won't use the entire XAF – I will need only small parts of its services. Here is an example of a test:

    [DomainComponent]
    public interface IBasicTestThing {
        string Name { get; set; }
    }
    [TestFixture]
    public class BasicTests : BaseTest {
        [Test]
        public void TestBasicThing() {
            RegisterDC<IBasicTestThing>();
            Generate();
            IBasicTestThing thing1 = ObjectSpace.CreateObject<IBasicTestThing>();
            thing1.Name = "abc";
            ObjectSpace.CommitChanges();
            IBasicTestThing thing2 = ObjectSpace.FindObject<IBasicTestThing>(null);
            Assert.AreEqual("abc", thing2.Name);
        }
    }
    

    In all my tests, I'll register and generate domain components, and use an ObjectSpace object. So, all my tests will be inherited from the BaseTest class that will provide the required services. Look how it's implemented now:

    public class BaseTest {
        private IObjectSpace objectSpace;
        private IObjectSpaceProvider osProvider;
    
        [SetUp]
        public virtual void SetUp() {
            XafTypesInfo.Reset();
            osProvider = new ObjectSpaceProvider(new MemoryDataStoreProvider());
            objectSpace = osProvider.CreateObjectSpace();
        }
        public void RegisterDC<T>() {
            if (typeof(T).IsInterface) {
                XafTypesInfo.Instance.AddEntityToGenerate(typeof(T).Name, typeof(T));
            } else {
                XafTypesInfo.Instance.RegisterEntity(typeof(T));
            }
        }
        public IObjectSpace ObjectSpace {
            get { return objectSpace; }
        }
        public void Generate() {
            XafTypesInfo.Instance.GenerateEntities();
        }
    }
    

    The SetUp method cleans up the current type information, and creates an Object Space using a newly created Object Space Provider (using in-memory data store). The RegisterDC and Generate methods work with the XafTypesInfo system, like it's performed in XAF.

    Capturing domain knowledge in unit tests

    I’m ready to write a test for the Contact - Account relationship:

    [Test]
    public void ContactAccountRelationships() {
        RegisterDC<IContact>();
        RegisterDC<IAccount>();
        Generate();
        IContact roman = ObjectSpace.CreateObject<IContact>();
        roman.FirstName = "Roman";
        roman.LastName = "Eremin";
        IAccount dx = ObjectSpace.CreateObject<IAccount>();
        dx.Name = "DevExpress";
    
        dx.PrimaryContact = roman;
        Assert.IsNull(roman.Account);
    
        dx.Contacts.Add(roman);
        Assert.AreEqual(dx, roman.Account);
    
        IAccount xafTeam = ObjectSpace.CreateObject<IAccount>();
        xafTeam.ParentAccount = dx;
        Assert.IsTrue(Enumerator.Exists<IAccount>(dx.Subaccounts, xafTeam));
    }
    

    Enumerator is a helper class from the DevExpress.ExpressApp.Utils.dll assembly. It provides utility methods for the IEnumerable interface (similar extension methods already exist in .net 3.0).

    Here is the code that makes this test pass (remember – I’m focused on relationships, so don’t tell me that the IContact should contain the IPerson interface) :

    
    
    [DomainComponent]
    public interface IAccount {
        string Name { get; set; }
    
        IAccount ParentAccount { get; set; }
        IList<IAccount> Subaccounts { get; }
    
        IContact PrimaryContact { get; set; }
        IList<IContact> Contacts { get; }
    }
    
    [DomainComponent]
    public interface IContact {
        IAccount Account { get; set; }
    }

    No logic is required, because the XpoBuilder automatically generates the correct associations and XPO manages them. So, when I add roman to the dx.Contacts list, the roman.Account property is initialized automatically.

    Posted Oct 01 2008, 01:00 PM by Roman Eremin with 7 comment(s)
    Filed under: , , ,
  • DC: Implemented interfaces in BOModel

    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.

    While playing with CRM domain models, I've got a component like this:

    	[DomainComponent]
    	public interface IContact : IPersonalContactInfo, IPerson {
    		IAccount Account { get; set; } 
    	}

    This led me to one more issue that prevents XAF from working with the new models. I found out that the BOModel node doesn’t provide information about implemented interfaces. I would like XAF to treat implemented interfaces as it currently treats base classes – include base members into generated Views and reuse View Info provided for base classes.

    So, to start, I need information about implemented interfaces in the BOModel. To make a long story short – here it is:

    DC4-01

    The next step is to make members of the implemented interfaces visible in Views.

    Posted Oct 01 2008, 10:00 AM by Roman Eremin with 2 comment(s)
    Filed under: ,
  • DC: Thinking of base library

    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.

    Posted Sep 30 2008, 12:31 PM by Roman Eremin with 4 comment(s)
    Filed under: , ,
  • DC: Interfaces and BOModel

    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

    Posted Sep 30 2008, 12:29 PM by Roman Eremin with 4 comment(s)
    Filed under: , ,
  • Associations in DC

    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.

    Posted Sep 29 2008, 09:22 AM by Roman Eremin with 8 comment(s)
    Filed under: ,
More Posts Next page »
Copyright © 1998-2010 Developer Express Inc.
ALL RIGHTS RESERVED