Hi Folks,
I've implemented an Object-oriented trigger infrastructure that can be plugged into Xpo to offer functionality analogous (and even superior to) database triggers. It is based on handling Session events and uses a singleton class, the TriggerService, to which all sessions should be registered so that trigger functionality is enabled for that session. I've then created a UnitoOfWork descendant, called TriggerAwareUnitOfWork which basically register itself within the TriggerService (and unregister itself when disposed).
Since I'm working in a Xaf application, I need to integrate this OO Trigger functionality into Xaf. The straightforward way of doing that is simply to "force" all Session (actually UnitOfWork) objects created by all ObjectSpace objects to be of the TriggerAwareUnitOfWork type. I know how to create an ObjectSpace descendant that will always create the session of the TriggerAwareUnitOfWork type. I also know that I need to create an ObjectSpaceProvider that will be responsible for creating my custom ObjectSpace (TriggerAwareObjectSpace sounds like a good name). What I don't know is how (where) to handle the XafApplication.CreateCustomObjectSpaceProvider event!!!
Could you provide sample code to show me where/when/how to write this event handler for the XafApplication… Should I put it in the Update.cs, in the Module.cs, will I need to create a controller and wire the event handler there?
This is a very, very urgent matter for us, that's why I'm checking the priority on this issue.
Thanks in advance.
We have closed this ticket because another page addresses its subject:
How to customize the Object Space behavior in XPO-based XAF applications
Hi Felipe,
Unless I am mistaken, a similar scenario is demonstrated in the How to use a custom ObjectSpaceProvider in XAF example. Please review it, and let us know if this helps.
Thanks,
Michael.
Hi,
That certainly would work if it was not for the fact that I want to do that in the platform agnostic module!
I'm hitting a wall here because I've seen that there is an ObjectSpaceProvider and an ObjectSpaceProviderThreadSafe, and I don't know what exactly could I do to setup one correctly.
In fact, what I need to do is to ensure that every single UnitOfWork created in the whole Xaf application (Windows and ASP.NET) is the custom TriggerAwareUnitOfWork.
If there are other ways to achieve that without having to create an ObjectSpace and ObjectSpaceProvider descendant, that would be great too. That's why I've explained the background of the problem I'm trying to solve too! Many times you guys come up with a better overall solution to the problem domain in question after all…
Hi Felipe,
Since WinForms and ASP.NET XAF applications use different object space providers, we recommend that you supply a corresponding custom provider descendant in the platform-specific application project. I am afraid it is impossible to accomplish your task without creating a custom ObjectSpaceProvider descendant, because the IObjectSpaceProvider does not have the necessary public events.
However, you can handle the XafApplication.CreateCustomObjectSpaceProvider event in the overridden ModuleBase.Setup(XafApplication application) method. To determine whether to create SimpleDataLayer or ThreadSafeDataLayer, you can detect the application type as described in issue How can I detect which platform the application is running on (Windows or Web)?.
Thanks,
Michael.
Hi Michael,
It may really be impossible to introduce a custom ObjectSpace / ObjectSpaceProvider at the platform agnostic module, so I've moved my implementation attempts to the platform specific module. I was trying to make a proof of concept for the Windows target, following the example you've pointed out here: http://devexpress.com/Support/Center/p/E411.aspx
I have one pending issue with the following implementation: (my comments will follow the code bellow)
/*** CODE STARTS HERE ****/
using System;
using DevExpress.ExpressApp.Win;
using DevExpress.ExpressApp;
using DevExpress.Xpo;
using DevExpress.ExpressApp.DC;
namespace PTS.Admin.Win
{
public partial class AdminWindowsFormsApplication : WinApplication
{
public AdminWindowsFormsApplication()
{
InitializeComponent();
}
private void AdminWindowsFormsApplication_DatabaseVersionMismatch(object sender, DevExpress.ExpressApp.DatabaseVersionMismatchEventArgs e)
{
#if EASYTEST
e.Updater.Update();
e.Handled = true;
#else
if (System.Diagnostics.Debugger.IsAttached)
{
e.Updater.Update();
e.Handled = true;
}
else
{
throw new InvalidOperationException(
"The application cannot connect to the specified database, because the latter doesn't exist or its version is older than that of the application.\r\n" +
"This error occurred because the automatic database update was disabled when the application was started without debugging.\r\n" +
"To avoid this error, you should either start the application under Visual Studio in debug mode, or modify the " +
"source code of the 'DatabaseVersionMismatch' event handler to enable automatic database update, " +
"or manually create a database using the 'DBUpdater' tool.\r\n" +
"Anyway, refer to the 'Update Application and Database Versions' help topic at http://www.devexpress.com/Help/?document=ExpressApp/CustomDocument2795.htm " +
"for more detailed information. If this doesn't help, please contact our Support Team at http://www.devexpress.com/Support/Center/");
}
#endif
}
protected override IObjectSpaceProvider CreateDefaultObjectSpaceProvider(IXpoDataStoreProvider dataStoreProvider)
{
return new TriggerAwareObjectSpaceProvider(dataStoreProvider);
}
}
public class TriggerAwareObjectSpaceProvider : ObjectSpaceProvider
{
public TriggerAwareObjectSpaceProvider(IXpoDataStoreProvider dataStoreProvider) : base(dataStoreProvider) { }
protected override IObjectSpace CreateObjectSpaceCore(UnitOfWork unitOfWork, ITypesInfo typesInfo)
{
ObjectSpace objectSpace = new TriggerAwareObjectSpace(new TriggerAwareUnitOfWork(unitOfWork.ObjectLayer), typesInfo);
/*
* objectSpace.AsyncServerModeSourceResolveSession = AsyncServerModeSourceResolveSession;
* objectSpace.AsyncServerModeSourceDismissSession = AsyncServerModeSourceDismissSession;
*/
return objectSpace;
}
}
public class TriggerAwareUnitOfWork : UnitOfWork
{
public TriggerAwareUnitOfWork() { }
public TriggerAwareUnitOfWork(DevExpress.Xpo.Metadata.XPDictionary dictionary) : base(dictionary) { }
public TriggerAwareUnitOfWork(IDataLayer layer, params IDisposable[] disposeOnDisconnect) : base(layer, disposeOnDisconnect) { }
public TriggerAwareUnitOfWork(IObjectLayer layer, params IDisposable[] disposeOnDisconnect) : base(layer, disposeOnDisconnect) { }
}
public class TriggerAwareObjectSpace : ObjectSpace
{
public TriggerAwareObjectSpace(DevExpress.Xpo.UnitOfWork unitOfWork, DevExpress.ExpressApp.DC.ITypesInfo typesInfo) : base(unitOfWork, typesInfo) { }
public TriggerAwareObjectSpace(DevExpress.Xpo.UnitOfWork unitOfWork) : base(unitOfWork) { }
protected override UnitOfWork CreateUnitOfWork(IDataLayer dataLayer)
{
return new TriggerAwareUnitOfWork(dataLayer);
}
}
}
/*** CODE ENDS HERE ****/
The problem I'm facing is with the implementation of the TriggerAwareObjectSpaceProvider. It lies in the implementation of the following method:
protected override IObjectSpace CreateObjectSpaceCore(UnitOfWork unitOfWork, ITypesInfo typesInfo)
{
ObjectSpace objectSpace = new TriggerAwareObjectSpace(new TriggerAwareUnitOfWork(unitOfWork.ObjectLayer), typesInfo);
/*
* objectSpace.AsyncServerModeSourceResolveSession = AsyncServerModeSourceResolveSession;
* objectSpace.AsyncServerModeSourceDismissSession = AsyncServerModeSourceDismissSession;
*/
return objectSpace;
}
As you can see, the AsyncServerModeSourceResolveSession and AsyncServerModeSourceDismissSession assignments are commented out. This is because they are marked as "protected internal"!!! So, while they were meant to be used for inheritors, DevExpress have chosen to mark them for internal, effectively making it possible for us to extend the class is we need them. I don't know exactly why I would need them, but since the original CreateObjectSpaceCore makes this same assignment, I thought it was sensible that my own implementation did the same. The problem is that I just can't do that due to the "internal" access level.
How can I guarantee that my own TriggerAwareObjectSpaceProvider creates my own TriggerAwareObjectSpace that will behave in every single aspect exactly like the default ObjectSpaceProvider and ObjectSpace, except for the fact that they will create a Session of type TriggerAwareUnitOfWork instead of UnitOfWork?
Is there a way around the above "internal" access level? Is this really a problem? Can I live without that? :-)
Hi Felipe,
These fields are only used in asynchronous server mode, which is not yet officially supported in XAF. So, there is no problem to omit these field assignments, unless you are planning to use this undocumented mode. We will change access modifiers or, probably, rewrite our core when the asynchronous mode is supported.
Thanks,
Michael.
Hi Michael,
Then I guess this is it! Thanks for the amazing support, once again.