Search This Blog

Wednesday, April 3, 2013

Exposing Domain Components (DC) via OData for mobile apps

As you probably know, it is possible to use DC in non-XAF applications as described at eXpressApp Framework > Task-Based Help > How to: Use Domain Components in non-XAF Applications

So, I will use a similar approach in my XPO-based OData Service project (you can learn more on how to create it via the wizard here). Once I created the service, I will have to modify my XpoDataServiceV3 descendant as shown below:

[JSONPSupportBehavior]
public class DCDataService : XpoDataServiceV3 {
    static XPObjectSpaceProvider objectSpaceProvider = null;
    static readonly DataService1Context serviceContext =  new DataService1Context("XpoContext",
        "DevExpress.ExpressApp.DC.GeneratedClasses", CreateDataLayer()
    );
    protected override UnitOfWork GetSessionCore(IObjectLayer objectLayer) {
        if (objectSpaceProvider != null) {
            XPObjectSpace os = (XPObjectSpace)objectSpaceProvider.CreateObjectSpace();
            return (UnitOfWork)os.Session;
        }
        return base.GetSessionCore(objectLayer);
    }
    static IDataLayer CreateDataLayer() {
        XpoTypesInfoHelper.ForceInitialize();
        var typesInfo = XpoTypesInfoHelper.GetTypesInfo();
        var xpoTypeInfoSource = XpoTypesInfoHelper.GetXpoTypeInfoSource();

        typesInfo.RegisterEntity("Survey" typeof(ISurvey));
        typesInfo.GenerateEntities();         

        var connectionString = "Integrated Security=SSPI;Pooling=false;Data Source=(local);Initial Catalog=TestDb";
        var dataStoreProvider = new ConnectionStringDataStoreProvider(connectionString);
        objectSpaceProvider = new XPObjectSpaceProvider(dataStoreProvider, typesInfo, xpoTypeInfoSource, true);
        objectSpaceProvider.CreateObjectSpace(); // Force the objectSpaceProvider.DataLayer property initialization.
        XpoDefault.DataLayer = objectSpaceProvider.DataLayer;
        DevExpress.Xpo.XpoDefault.Session = null;
        return XpoDefault.DataLayer;
    }
}

Here I would like to focus on three things:
1. I initialized the static serviceContext variable and provide the namespace of our auto-generated DC entities as the second parameter: "DevExpress.ExpressApp.DC.GeneratedClasses". It is important to avoid dealing with long-named entities like "DevExpress_ExpressApp_DC_GeneratedClasses_Survey" in our consumer applications.

2. I initialized the data layer that is used by my XpoContext descendant via the static CreateDataLayer method. There I used some XAF magic, which is required for DC, because domain logic methods accept the parameters of the IObjectSpace type. If I did not use DC, then I would not use the XAF classes in my data service and rather go using pure XPO stuff.

3. I overrode the GetSessionCore to use the Session created by the XAF's ObjectSpace, again for the correct work of domain logic methods. Take special note that this virtual method is available in version 12.2.8+ only (thanks to our XPO guys for implementing my request so quickly;-)). If you want to test it right away, I have included the latest version of the DevExpress.Xpo.v12.2.Extensions assembly into the demo project I posted here.

Finally, let me demonstrate why I created the OData service in the first place. Of course, I needed it for my mobile DXTREME application:


View on screencast.com »


UPDATE:
Originally when experimenting with this DC-based service I tried to reference the DcAssembly.dll into my data service project. This cache assembly is automatically generated by XAF when running the application with no debugger attached. So, to get it, I simply ran my XAF WinForms application, which used the same data model, and then copied the generated assembly from the Release/Bin folder into my data service project. Since this assembly contains regular persistent classes instead of DC interfaces (remember my recent talk about different types?), I hoped that this way it might be easier for you to understand and work with. Unfortunately, I had to stop using this undocumented approach later, because it required additional configuration from the application for the correct operation of the DC-based functionality: normally, when XAF loads this assembly internally it also calls the private void ProcessGeneratedAssembly(Assembly generatedAssembly) method of the XpoTypeInfoSource class. Another reason is that with the DcAssembly.dll approach it would also be more difficult to deploy and maintain the app due to the overhead on generating and copying the assembly...

19 comments:

  1. Note that due to limitation of WCF data services inheritance scenarios are not well supported. Hopefully, this will be improved in future versions of OData.

    ReplyDelete
  2. Hello Dennis

    I am encountering a inheritance issue when using XPODataServiceV3. Here is my scenario

    public class RcSyncBase : XPObject
    {
    }
    public class Contact: RcSyncBase
    {
    }

    The above code is defined in a XafTest.module class library which using XPO to connect/generate the database from the classes mentioned above in the post

    I use your code above in the post and register entity "Contact" using the RegisterEntity().

    I am using XPODataServicev3.

    When i run the service, I see RcSyncBase exposed in the web browser.

    I am able to query http://localhost:16882/ODataService1.svc/Contact in web browser but it returns fields of RcSyncBase class instead of the fields from Contact class

    If you need more information, I can provide such as code. But most of it is the same as in your post. The only difference is i am using a reference to a class library project instead of DCAssembly.cs

    Kindly reflect if the inheritance issue has been resolved. Or will it be resolved considering the WCF Data service has been retired 3 months ago.?

    Looking forward to your response.

    Thanks


    ReplyDelete
    Replies
    1. I meant DCAssembly.dll but mistakenly typed DCAssebly.cs..

      Delete
  3. @timematcher: This behavior is not specific to XpoDataServiceVX as this is a known specificity of the OData protocol. You can google more on OData + inheritance on the Web, e.g. http://stackoverflow.com/questions/16643607/, and I am afraid there is not much to be resolved on our side. Please check out the https://www.devexpress.com/Support/Center/Question/Details/Q536572 ticket for possible solutions around this WCF Data Services limitation.

    ReplyDelete