KB Article T929471
Visible to All Users

How to migrate Domain Components (DC) interfaces to pure XPO classes

Technology evolves and we do our best to support everyone to the best of our abilities. In XAF's ASP.NET Blazor Server UI, we support pure XPO, EF Core and non-persistent/POCO classes only.

Persistent interfaces, formerly known as DC, are deprecated and not recommended for new projects. We have asked users to migrate away from DC since 2016. If you choose not to migrate, your DC-based projects will continue to work for WinForms and ASP.NET WebForms – you don’t have to switch to Blazor. In other words, if you are happy with DC and do not need new features, you can continue using DC as long as you wish. Otherwise, we suggest that you consider migrating to pure XPO classes.

If you choose to use Blazor, you can convert your existing DC interfaces to XPO classes in many scenarios. Yes, it’s not a simple process but many organizations have migrated successfully. We are more than happy to help you in this migration process.

NOTE: Backup your XAF solution sources and database before going further, because .NET code and database modifications are to be done.

DC Source Code Migration

Consider one of the following code migration options below.

Option 1. Reuse the XPO Classes Generated for DC Interfaces

Since XAF internally generates standard XPO classes for DC interfaces, you can copy-paste their source code to *.cs files and use it for further modification.

1. Consider the following source DC interfaces and their registration:

C#
[DomainComponent] public interface IMaster { string Name { get; set; } IList<IDetail> Details { get; } } [DomainComponent] public interface IDetail { string Name { get; set; } } XafTypesInfo.Instance.RegisterEntity("Master", typeof(IMaster)); XafTypesInfo.Instance.RegisterEntity("Detail", typeof(IDetail));

2. Run your DC-based app in Release mode and without the debugger attached (Control+F5 in Visual Studio). Notice the DcAssembly.dll file created near the executable file.

3. Open DcAssembly.dll using the free ILSpy (or Reflector and similar .NET decompiler tools) and see the generated C# code for runtime classes corresponding to your registered domain components. You may see the following result in ILSpy for the Master class:
Clipboard-File-1.png

NOTE: Instead of decompiling this assembly, you can put a breakpoint into the DevExpress.ExpressApp.Utils.CodeGeneration.CSCodeCompiler.Compile method, run the application and check the method's sourceCode parameter using our PDBs: How can I debug DevExpress .NET source code using PDB files.

Option 2. Generate XPO Classes with ORM Data Model Wizard

You can use the database that was created by your Domain Components to generate a new XPO data model as described in the following help topic: How to: Generate XPO Business Classes for Existing Data Tables.

Option 2. Design XPO Classes from Scratch

It is possible that designing XPO classes from scratch can be faster than decompiling or generating their code, as shown above. This is likely true in cases when you do not need to migrate all the data members as per #1. The use of tools like CodeRush and its shortcuts for XPO feature can be helpful in this case.

Common Considerations

Once you get your XPO class code using one of the options above, you will have to copy this code into a new code file and modify it. Note that code for scalar properties can be reused "as is". You will mainly need to rework the code for reference and collection properties (for instance, see the selected fragment) and associated classes. For more information, refer to the following article: Object Relationships. Additionally, you will need to rework the class declaration and service DC classes and properties like PersistentInterfaceData, Instance. For more information, see the attached Class1.cs file with the modified code of the Master and Detail classes from DcAssembly.dll and also inspect the DcAssemblyScenario5SharedParts.dll file.

DC Attribute Migration

To create XPO classes easier, the following table lists attributes from the DevExpress.ExpressApp.DC namespace and their replacements from the DevExpress.Xpo namespace.

DC Attribute XPO Attribute
NonPersistentDcAttribute NonPersistentAttribute
PersistentDcAttribute PersistentAttribute
AggregatedAttribute AggregatedAttribute
CalculatedAttribute PersistentAliasAttribute
FieldSizeAttribute SizeAttribute
BackReferencePropertyAttribute AssociationAttribute
DomainComponentAttribute No Replacement
DomainLogicAttribute No Replacement
CreateInstanceAttribute No Replacement

For the full list of XAF and XPO attributes, refer to Data Annotations in XAF Data Model and XPO Built-In Attributes.

Domain Logic Code Migration

To migrate your business logic in domain logic containers, consider one of the following options:
1. Reuse your existing domain logic classes inside XPO classes
1.1. Remove DomainLogicAttribute from your logic classes and replace the IObjectSpace with Session and IYourBusinessObject with YourBusinessObject in logic method parameters.
1.2. Plug in your logic classes into your XPO class as shown in the generated C# code for runtime DC classes (open DcAssembly.dll using .NET decompiler tools as discussed above). For this, you can declare a private field of your domain logic type (for instance, private IProduct_Logic logic_IProduct_Logic;) and then initialize it from your XPO class constructor (see the CreateLogics method in the screenshot below).
1.3. Replace the IObjectSpace parameters in logic method calls with this.Session inside your XPO class. Take special note that if you call your logic from property setters or the overridden OnChanged methods of XPO classes, check the IsLoading and IsSaving property values before your logic calls. For examples, see XPO Best Practices | How to: Calculate a Property Value Based on Values from a Detail Collection.
Consider the example below:
Clipboard-File-11.png

2. Implement ORM-agnostic logic containers
This option was a predecessor of DC domain logic classes back in 2005 and it is still valid today. You can see examples within the XAF sources in the following files:

  • …\DevExpress.Persistent\DevExpress.Persistent.BaseImpl\Task.cs;
  • …\DevExpress.Persistent\DevExpress.Persistent.Base\General\TaskImpl.cs.

This options allows you to create easily testable logic classes not concerned with ORM persistence. This logic can also be easily reused within XAF business classes and controllers as well as non-XAF apps.

3. Implement business logic inside XAF Controllers
You can handle View and IObjectSpace events to call required business logic. Examples: IObjectSpace.ObjectChanged | Add an Action with Option Selection. To organize this logic for different classes and apps, you may want to implement logic container classes as in option #2 above.

DC Database Migration

The database table structure for DC interfaces is different than the table structure used for pure XPO classes because service shared part and association tables are created to mimic multiple inheritance. To see all the differences between databases for plain XPO classes and DC interfaces, please open SQL Server Management Studio (SSMS) and compare table and column structures for DC-based and XPO-based apps. For instance, check how one-to-many and inheritance relationships for the Contact, Person, Party and PhoneNumber XPO classes from our MainDemo app are reflected in the underlying database. You can compare this XPO data model and its table structure with a similar scenario in your DC-based app. Our MainDemo app also demonstrates other popular scenarios with many-to-many, inheritance, and one-to-to relationships.

If you want to reuse data models and databases from your existing DC-based apps in new XPO-based apps, you can consider one of the following strategies:

  • Strategy 1. If you have one app that uses DC with one database and want to create a Blazor app that will use new XPO classes, you can also create a separate database for your Blazor app, because DC-based and XPO-based databases are incompatible.
  • Strategy 2. If you have one app that uses DC and want to create a Blazor app that will use new XPO classes, but want both apps to use the same database, you can manually migrate from DC interfaces to new XPO classes in both apps and also migrate your existing database structure and data (for instance, using SQL scripts) to comply with the new XPO data model.

Consider the following data migration scenarios depending on your original DC data model:

Scenario 1: Plain data model structures with no inheritance and relationships

C#
[DefaultClassOptions] [DomainComponent] public interface IDetail { string DetailProperty { get; set; } } XafTypesInfo.Instance.RegisterEntity("iDetail", typeof(IDetail));

Clipboard-File-4.png
To migrate this simplest data model and database, do the following:

  1. Create a new XPO Detail class manually or as described in sections "Reuse the source code generated for DC".
  2. Either rename the table to Detail or decorate your XPO class with the [Persistent("iDetail")] attribute (if you are happy with the iDetail table name).
  3. Locate the corresponding record in the XPObjectType table with TypeName = "DevExpress.ExpressApp.DC.GeneratedClasses.iDetail" and change TypeName to "YourNamespace.Detail" manually or automatically using the ModuleUpdater descendant (inside the YourSolutionName.Module/Updater.xx file). For this, call the following line in the UpdateDatabaseAfterUpdateSchema method: ((XPObjectSpace) ObjectSpace).Session.CreateObjectTypeRecords(); - this will update records in the XPObjectType table based on your new XPO classes.

Scenario 2. Plain data model structures with no inheritance and a M-N relationship (master back reference property is explicit)

C#
[DefaultClassOptions] [DomainComponent] public interface IMaster { string MasterProperty { get; set; } IList<IDetail> Details { get; } } [DefaultClassOptions] [DomainComponent] public interface IDetail { IMaster MasterReference { get; set; } string DetailProperty { get; set; } } XafTypesInfo.Instance.RegisterEntity("iMaster", typeof(IMaster)); XafTypesInfo.Instance.RegisterEntity("iDetail", typeof(IDetail));

Clipboard-File-5.png

To migrate this simplest data model and database, use the steps from Scenario #1 for IDetail and IMaster.

Scenario 3. Plain data model structures with no inheritance and with a M-N relationship (master back reference property is implicit)

C#
[DefaultClassOptions] [DomainComponent] public interface IMaster { string MasterProperty { get; set; } IList<IDetail> Details { get; } } [DefaultClassOptions] [DomainComponent] public interface IDetail { string DetailProperty { get; set; } } XafTypesInfo.Instance.RegisterEntity("iMaster", typeof(IMaster)); XafTypesInfo.Instance.RegisterEntity("iDetail", typeof(IDetail));

Clipboard-File-7.png

To migrate this data model and database, do the following:

  1. In the Detail XPO class and table, create an back reference property/FK column in the Master class explicitly (MasterReference as in Scenario #2). This FK column should refer to the Oid PK column of the Master table.
  2. Populate this new MasterReference property/FK column with values from the IDetail_Implicit_IMaster_Details_List_Link column in the IMaster_Details table.
  3. Remove the IMaster_Details table and execute additional steps from Scenario #2.

Scenario 4. Complex data model structures with a single inheritance and no shared parts (a simple base class entity)

C#
[DefaultClassOptions] [DomainComponent] public interface IDetail: ISharedPart1 { string DetailProperty { get; set; } } [DomainComponent] public interface ISharedPart1 { string SharedProperty1 { get; set; } } XafTypesInfo.Instance.RegisterEntity("iDetail", typeof(IDetail)); XafTypesInfo.Instance.RegisterEntity("iSharedPart1", typeof(ISharedPart1));

Clipboard-File-8.png

To migrate this simplest data model and database, do the following:

  1. Inherit the Detail XPO class from the SharedPart1 XPO class. Take special note that for inheritance (public class Detail : SharedPart1 { ... }) the additional ObjectType column exists in the base class table and its values refer to the Oid column in the XPObjectType table (the former record with TypeName = "DevExpress.ExpressApp.DC.GeneratedClasses.iDetail" or TypeName = "YourNamespace.Detail" after the upgrade).
  2. Use the steps from Scenario #1 for IDetail and ISharedPart1.
  3. If you used automatic XPObjectType table update from Scenario #1 ( ((XPObjectSpace) ObjectSpace).Session.CreateObjectTypeRecords();), manually update the ObjectType column in corresponding base tables based on the automatically created type records in the XPObjectType table (they will have different Oid).

Scenario 5: Complex data model structures with multiple inheritance and shared parts

C#
[DefaultClassOptions] [DomainComponent] public interface IDetail: ISharedPart1, ISharedPart2 { string DetailProperty { get; set; } } [DomainComponent] public interface ISharedPart1 { string SharedProperty1 { get; set; } } [DomainComponent] public interface ISharedPart2 { string SharedProperty2 { get; set; } } XafTypesInfo.Instance.RegisterEntity("iDetail", typeof(IDetail)); XafTypesInfo.Instance.RegisterSharedPart(typeof(ISharedPart1)); XafTypesInfo.Instance.RegisterSharedPart(typeof(ISharedPart2));

Clipboard-File-9.png
Clipboard-File-9.png

The underlying DC data model and database structure for shared parts is very complex. This structure consists of a base XPO class and descendant for each shared part (ISharedPart1\_Data\_iDetail : ISharedPart1\_Data), which store their data (SharedPropertyX) in the ISharedPartX_Data tables. Inspect the DcAssemblyScenario5SharedParts.dll file and the corresponding table structure for more information. Unfortunately, this structure cannot be migrated to plain XPO classes and database cannot be reused easily.

To migrate this data model and database, do the following:

  1. Rethink the structure of data models that used shared parts and implement XPO classes from scratch. Implementation options include:
    A) Declare data properties from shared parts (SharedPropertyX) in the Detail XPO class directly with or without using base class inheritance. This will help you achieve the best SQL query performance, because only plain data will be queried without joining other data tables.
    B) Declare base classes for shared parts (SharedPartX) and use them in the Detail class through an aggregated persistent reference property. Multiple reference properties may cause performance degradations in certain scenarios, so try to keep such shared part properties to the minimum or use option A instead.
C#
[DefaultClassOptions] public class Detail : BaseObject { public Detail(Session s) : base(s) { } public override void AfterConstruction() { base.AfterConstruction(); SharedPart1 = new SharedPart1(Session); SharedPart2 = new SharedPart2(Session); } private string _DetailProperty; public string DetailProperty { get { return _DetailProperty; } set { SetPropertyValue(nameof(DetailProperty), ref _DetailProperty, value); } } private SharedPart1 _SharedPart1; [DevExpress.Xpo.Aggregated, ExplicitLoading(0), ExpandObjectMembers(ExpandObjectMembers.Always)] public SharedPart1 SharedPart1 { get { return _SharedPart1; } set { SetPropertyValue(nameof(SharedPart1), ref _SharedPart1, value); } } private SharedPart2 _SharedPart2; [DevExpress.Xpo.Aggregated, ExplicitLoading(0), ExpandObjectMembers(ExpandObjectMembers.Always)] public SharedPart2 SharedPart2 { get { return _SharedPart2; } set { SetPropertyValue(nameof(SharedPart2), ref _SharedPart2, value); } } } public class SharedPart1 : BaseObject { public SharedPart1(Session s) : base(s) { } private string _SharedProperty1; public string SharedProperty1 { get { return _SharedProperty1; } set { SetPropertyValue(nameof(SharedProperty1), ref _SharedProperty1, value); } } } public class SharedPart2 : BaseObject { public SharedPart2(Session s) : base(s) { } private string _SharedProperty2; public string SharedProperty2 { get { return _SharedProperty2; } set { SetPropertyValue(nameof(SharedProperty2), ref _SharedProperty2, value); } } }
  1. Manually migrate required column values from the ISharedPartX_Data tables to the new SharedPartX or Detail tables, if applicable.
  2. Remove the ISharedPartX_Data tables and their corresponding records in the XPObjectType table.

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.