Blogs

Gary's Blog

XAF–Domain Components How-to Implement an Address in a Business Entity

     

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

Addresses are funny things aren’t they? If you own the the Address class, and you are writing it for your own application then they are easy things to handle. Each address object is a composition of a number of string types containing the information you require for your application. Simple and straight forward. Of course, as soon as you try to write a reusable interface library, things start to get a little more complicated, so let’s take a look at how we did it.

First, let’s start with  an IBaseAddress component:

public interface IBaseAddress
{
    string DisplayName { get; set; }
}

Having this base class for addresses we can now go on to define an IAddressable interface, which entities that require to have one, or more, addresses can use – things like Contacts, Orders, Shipping Notes etc. We can define our IAddressable interface like so:

[DomainComponent]
public interface IAddressable
{
    [DevExpress.ExpressApp.DC.Aggregated]
    [VisibleInListView(false)]
    IBaseAddress PrimaryAddress { get; set; }

    [CreateInstance]
    IBaseAddress CreateAddress();
}

And we can use it, wherever we need to, like this:

public interface ICRMContact : IContact, IAddressable, ...
public interface ICRMAccount : IAccount, IAddressable, ...
public interface ICRMLead : ILead, IAddressable, ...

In this interface, there are two significant parts: the PrimaryAddress member, which is aggregated, and it brings in the minimum information of an addresses – the primary address.
The second significant part is the CreateAddress method that is marked with the CreateInstance attribute. This method is used as a factory method to create an entity that is registered for the IBaseAddress domain component. This method is autogenerated and will work when one entity in the application implements the IBaseAddress interface. In other cases, you need to implement it manually and you’ll have to specify which entity should be created, in this method, when it is called:

[DomainLogic(typeof(IAddressable))] 
public class AddressableLogic { 
    public static void AfterConstruction(IAddressable addressable) { 
        if(addressable.PrimaryAddress == null) { 
            addressable.PrimaryAddress = addressable.CreateAddress(); 
        } 
}

Now our implementation is far from perfect. Using a string makes it hard to process the address by address part, to filter by a city for example. It is also impossible to guarantee any strict form of address format, which your application may need to do. Providing default lists, say of cities or of US states, is also difficult to do.

So, let’s improve things by defining a generic address component in our library, this component can be substituted with your own implementation should you need something more specific.

Our IGenericAddress component will look like this:

[DomainComponent]
public interface IGenericAddress : IBaseAddress
{
    string Street1 { get; set; }
    string Street2 { get; set; }
    string City { get; set; }
    string State { get; set; }
    string Zip { get; set; }
    string Country { get; set; }
}

Here you will notice that the single string DisplayName has been broken down into several string members, and is constructed from these parts when any of them is modified:

[DomainLogic(typeof(IGenericAddress))] 
public class GenericAddressLogic { 
private static void UpdateDisplayName(IGenericAddress address) { 
address.DisplayName = string.Format("{0} {1} {2} {3} {4} {5}", address.Street2, address.Street1, address.City, address.State, 
address.Zip, address.Country); 
} 
public static void AfterChange_Street1(IGenericAddress address) { 
UpdateDisplayName(address); 
} 
public static void AfterChange_City(IGenericAddress address) { 
UpdateDisplayName(address); 
} 
...

With this approach, it is possible for you to introduce any other Address implementation into any library component that contains the IBaseAddress member.

On the Order UI in our XCRM demo application, the address UI looks like this:

XCRM Order Address

Well that’s all for this post, until next time, happy XAF’ing! Smile

Published Mar 28 2011, 04:57 PM by Gary Short (DevExpress)
Filed under: , ,
Technorati tags: XAF, DC, Domain Components
Bookmark and Share

Comments

 

Arjan van Dijk said:

The IDisplayname could (should) be used more genericly. It can be defined as a common pattern for more entities, not only for address. I can imagine that you want to use it for f.e. customers, orders, invoices etc. Everywhere you want to use a "displayname". More patterns can be defined like

- IActive: to activate/deactive an entity

- ICode: provide a code and a description field

- IDefault: to set 1 default entity in a list of entities

etc....

This is how you can make truly building blocks for common patterns very easily thanks to multiple inheritance.

I'm using these patterns extensively in my main model DC layer which I use as a basis for all my future applications.

Keep up the good work!

March 31, 2011 11:16 AM
 

Yorch said:

I am evaluating and doing some test on XAF to see if we would use it to develop a final application , im triying to use this  CRM as an example and created a new simple project and creatiung an interface IAPPAccount:ICustomer,INotes,IAddressable

at run time it says:

{"Classes DevExpress.Persistent.BaseImpl.Address and Address are mapped to the same table."}

Jorge.

April 14, 2011 4:16 PM
 

Dan (DevExpress) said:

@Arjan van Dijk: Thank you for the feedback. We will test small interfaces, it seems to be a good idea.

@Yorch: This error is raised by XPO because there are two classes that are mapped to the same table: the 'DevExpress.Persistent.BaseImpl.Address' class from the DevExpress.Persistent.BaseImpl assembly (most likely, you have included this class in the designer or used it in your classes) and the 'Address' class, which is dynamically generated for the IAddress interface (you have registered it under the "Address" name, haven't you?)

April 15, 2011 9:06 AM
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
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, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.