Associations in DC

XAF Team Blog
29 September 2008

EDIT:
Domain Components (DC) is in maintenance mode and we do not recommend its use in new software projects.

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.

Tags
9 comment(s)
Anonymous
Alexander Koger

Hi,

I'm still missing the link from IItemWithNotes to INote.

The internal member Notes should be declared as something like this: IList<INote> Notes.

Shouldn't it?

And would it be possible to use these DomainLogic-Attributes to build an associtation between INote and IItemWithNotes.

Then you could replace the owner by this: IList Owner.

Regards,

Alex

29 September, 2008
Anonymous
Alain

Hi Roman

Another solution is to have a table of relationships.

Class_A_Oid --- Instance_Left_Side_Oid --- Class_B_Oid ---Instance_Right_Side_Oid --- KindOfRelationship

Regards

29 September, 2008
Mark Krasnohorsky
Mark Krasnohorsky

Hi Roman,

Please make sure that there is a way to override the default association behaviour.

For example, it may be desirable to declare an IList property and populate it via the "DomainObject (marked) interface extender so, an automatic association may cause some grief. (You have a class like, product, where there is an iList called PriceList which is actually a dynamically generated set of data that is not persisted to the DB).

Thanks,

Mark

29 September, 2008
Roman Eremin (DevExpress)
Roman Eremin (DevExpress)

Alexander:

You are right, this is what was in my code. Probably <INote> pare was lost in my early code copy-paste attempt (it looks like HTML tag for the browser). I've fixed the code.

Alain:

Yes, but this will lead to indirection problems - similar to those XPO have in automatic many-to-many associations. There will be an intermediate class that should be hidden somehow.

29 September, 2008
Mark Krasnohorsky
Mark Krasnohorsky

Hi Roman,

I think that it may be possible to use the association attribute at the class level:

[Association("DeveloperNotes", typeof(IItemWithNotes))

public interface IDeveloper : IPerson, IItemWithNotes{

}

or

[Association("DeveloperNotes", "Note")

public interface IDeveloper : IPerson, IItemWithNotes{

}

I think it will have to be the second one due to the fact that you can register an interface with multiple names using the "AddEntityToGenerate" method; but this means that you lose some type safety as you are using the name of the entity as it is registered via XPO.

However, this would allow for selective association.

MK

PS: I guess if the "AddEntityToGenerate" method allows for multiple registrations of the same interface, there has to be some way to specify which specific domain object class definition an interface is using.

For Example:

public interface IDeveloper : IPerson, IItemWithNotes{

}

And I have:

XafTypesInfo.Instance.AddEntityToGenerate("Developer", typeof(IDeveloper));  

XafTypesInfo.Instance.AddEntityToGenerate("Note1", typeof(INote));

XafTypesInfo.Instance.AddEntityToGenerate("Note2", typeof(INote));

How does the XPO know which INote to use for IDeveloper?

I guess the solution may be to not allow for multiple registrations of the same interface; however, I feel that something will be lost, just can't figure out what yet.

29 September, 2008
Roman Eremin (DevExpress)
Roman Eremin (DevExpress)

Hi Mark,

Yes, we will make sure that this is possible.

29 September, 2008
Roman Eremin (DevExpress)
Roman Eremin (DevExpress)

Mark:

Regarding two interfaces registered as entity - currently XpoBuilder will just throw an excption. We will deal with this problem later. We will provide a way to give system some hints how to resolve interface-entity mapping in complex cases.

29 September, 2008
Anonymous
Alain

yes, but might be you can hide the indirection in code.

handling 1..N relationship from the base clase, to allow something like this

Relationship["Students"].Add( new Student());

In this case, we dont need to create property to each relationship in our code. Just declare the relationship in some place (might be an static method from each class)

Regards

29 September, 2008
Anonymous
Tien Do

I just tested this XPO limitation in XAF 10.2 RC and very glad that it has been worked-around :) (and database is more complex than BO approach).

26 April, 2010

Please login or register to post comments.