DC: Combining associations

XAF Team Blog
10 October 2008

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

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!

Tags
no comments
No Comments

Please login or register to post comments.