Problem
It may be required to distinguish or separate service tables used by XAF/XPO applications from other legacy database tables, for instance, by using a table name prefix.
Current (recommended) solutions
If you want to change a table name just for a few custom persistent classes, use PersistentAttribute. To change table names or provide prefixes or different schemas for many tables, consider the following approaches:
1. You can customize the XPO connection provider settings as per How do I map persistent classes to another schema, e.g. other than the default "dbo" in MS SQL Server? This solution appears to be the best and simplest way to avoid any possible naming conflicts with other companies or applications and we recommend you seriously consider it over the next two options.
2. You can customize type metadata of required persistent classes by adding/removing PersistentAttribute with the desired table name dynamically. Refer to the Customize Business Object's Metadata topic and example below for more information:
C#public override void CustomizeTypesInfo(ITypesInfo typesInfo) {
base.CustomizeTypesInfo(typesInfo);
XPDictionary dictionary = XpoTypesInfoHelper.GetXpoTypeInfoSource().XPDictionary;
//You can use this code to load missing classes into XPDictionary. However, it is better to do that in the code of your module as follows: this.AdditionalExportedTypes.Add(typeof(DevExpress.ExpressApp.Updating.ModuleInfo));
dictionary.GetClassInfo(typeof(DevExpress.ExpressApp.Xpo.Updating.ModuleInfo));
dictionary.GetClassInfo(typeof(DevExpress.Xpo.XPObjectType));
dictionary.GetClassInfo(typeof(DevExpress.Xpo.XPWeakReference));
foreach (XPClassInfo ci in dictionary.Classes) {
if (ci.IsPersistent && (ci.AssemblyName.StartsWith("DevExpress.") || ci is DevExpress.Xpo.Metadata.Helpers.IntermediateClassInfo)) {
ci.RemoveAttribute(typeof(PersistentAttribute));
ci.AddAttribute(new PersistentAttribute(string.Format("System_{0}", ci.TableName)));
typesInfo.RefreshInfo(ci.ClassType);
}
}
}
For versions prior to 19.2.4, you need to use the code below:
C#private static Object lockObject = new Object();
private static bool prefixesUpdated;
public override void CustomizeTypesInfo(ITypesInfo typesInfo) {
base.CustomizeTypesInfo(typesInfo);
if (!prefixesUpdated){
lock(lockObject){
if (!prefixesUpdated){
XPDictionary dictionary = XpoTypesInfoHelper.GetXpoTypeInfoSource().XPDictionary;
//You can use this code to load missing classes into XPDictionary. However, it is better to do that in the code of your module as follows:
this.AdditionalExportedTypes.Add(typeof(DevExpress.ExpressApp.Updating.ModuleInfo));
dictionary.GetClassInfo(typeof(DevExpress.ExpressApp.Xpo.Updating.ModuleInfo));
dictionary.GetClassInfo(typeof(DevExpress.Xpo.XPObjectType));
dictionary.GetClassInfo(typeof(DevExpress.Xpo.XPWeakReference));
foreach (XPClassInfo ci in dictionary.Classes) {
if (ci.IsPersistent && (ci.AssemblyName.StartsWith("DevExpress.") || ci is DevExpress.Xpo.Metadata.Helpers.IntermediateClassInfo)) {
ci.RemoveAttribute(typeof(PersistentAttribute));
ci.AddAttribute(new PersistentAttribute(string.Format("System_{0}", ci.TableName)));
typesInfo.RefreshInfo(ci.ClassType);
}
}
prefixesUpdated = true;
}
}
}
}
For more examples, research our internal TableNameCustomizer class code (…\DevExpress.ExpressApp.Xpo\Utils\TableNameCustomizer.cs).
3. You can move the ModuleInfo, XPObjectType and other unwanted system tables into a separate database as described at How to prevent altering the legacy database schema when creating an XAF application.
Please let us know which option works best in your particular case.
Previous (deprecated) solutions
XAF provided the following APIs for this task when XPO is used for data access:
XafApplication > TablePrefixes
XafApplication > CustomizeTableName
XPObjectSpaceProvider > CustomizeTableName
These APIs have a number of constraints that we cannot alter due to backward compatibility (risks of breaking changes to existing customers):
- No effect on the service XPObjectType and ModuleInfo classes;
- No effect on persistent classes that already have PersistentAttribute applied explicitly (for instance, certain security system classes like SecuritySystemRole, PermissionPolicyRole, etc.).
We no longer recommend using the TablePrefixes feature in XAF projects due to the aforementioned specifics and also due to the availability of better solutions for the same ultimate goal. With XAF v18.2, these APIs are also marked as internal and we no longer provide formal support for them. If you are happy with these APIs, you can leave your code as is. Your code will operate as before, but these APIs will just disappear from Intellisense and the Application Designer.
Anything with a workaround to me looks like a bug.
I'd expect this would have been fixed in the new release? Or is it still an issue?
This suggestion has not been implemented.
XAF is designed to provide a number of common development tasks in the out-of-the-box manner and, even though this number is great enough, there is always a room that is not covered for some reason. Thus, common ASP.NET, WindowsForms, XPO, or control specific techniques and approaches should be used to implement the necessary behavior.
For instance, in the meantime, you can use a solution suggested above.
IMHO, this is not a suggestion, but a bug, and thus should be fixed ASAP.
@Robert: Thank you for the feedback. There are more important issues in our TO-DO list than this particular one that has an easy workaround. Does not the suggested workaround work for you or what is the reason for the "ASAP" you mentioned?
> Does not the suggested workaround work for you
You said: "Further research has shown that the TablePrefixes feature does not work for the XPObjectType table."
Thus the workaround is not a really a workaround.
> what is the reason for the "ASAP" you mentioned
Any bug should always be fixed ASAP.
Thank you for the feedback, Robert.
XAF v19.2.4 ensures that several XafApplication instances do not call the CustomizeTypesInfo method simultaneously. You no longer need the lock statement when you modify Types Info.