eXpress App Framework Team

December 2012 - Posts

  • ORM Magic–Importing from any datasource

    This post will be on how to use the approach described in Fast prototyping requires an initial data import mechanism with Excel as input source. The technic is based on System.Data.DataSet class and can be applied to any type of input source!

    Thanks to your reports and of course Slava’s help (XPO guru), XVideoRental importing mechanism has been improved a lot and now is faster, has notification events and is decoupled from XAF’s TypesInfo system. This makes it possible to simply copy the ImportData.cs file found in Common.Win project and use it in non XAF solutions!

    In previous version this importing mechanism worked only when matching in types of input and output members. This means that if my output Oid property was a System.Guid then input should be of the same type. Now we have workaround this minor issue. If conversion is possible it will be done automatically without the need of any configuration or extra coding!

    To better design, support, extend and describe the importing mechanism I used Mspec a functional testing framework and I open source the specifications in our community project eXpandFramework. These follow bellow,

    MemberMapper, When Class Has a flag that includes all members
    » should collect all members that belong to the class
    » should not collect any base class member
    » should create new members for each one of them

    MemberMapper, When class does not have a flag that includes all ownmembers
    » should not create members for those not marked with the attribute
    » should create members and name them according to the attribute data
    » should create a key property if not included in the conficuration

    MemberMapper, When class attribute has data for base members
    » should create and name them according to the attribute data
    » should map their attributes

    MemberMapper, When class is marked to include all member but member is marked seperately
    » should use the member marking

    MemberMapper, When mapping a referenced member
    » should create a member with a dynamic type ownwer

    MemberMapper, When reference type is not includedIN the configuration
    » should create non reference member

    ClassMapper, When creating dynamic classes
    » should Create Classes only for the ones that have a marking attribute
    » should name the classes taking data from marking attribute

    ClassMapper, When class has many to many collection marked for importing
    » should create intermediate classes
    » should create an auto genareted key for this class
    » should create 2 columns with names taken from the marked attribute

    DictionaryMapper, When is mapping a dictionary
    » should map all marked objects

    InitDataImporter, When importing an object
    » should create a new output object
    » should assign all mapped properties

    InitDataImporter, When input membertype is different than output
    » should convert the value when simple type
    » should convert the value when reference type


    21 passed, 0 failed, 0 skipped, took 1.61 seconds (Machine.Specifications 0.5.2-98b543c).

    The above specifications are evaluated with every eXpand build and you can see them in eXpand’s build server. Take a quick look at their code located in github to see how easy it is to write them (http://goo.gl/TNv4d).

    Now lets see how to use our magic XPO ORM to import into the real world business domain like the one of our XVideoRental demo,

    image

     

    For the sake of complexity the input source will be a list of excel files and not just a simple Excel file with many sheets.

    image

    XPO supports a DataStore provider which is based on DataSet. So, first we fill a DataSet with the excel files and for this I will use some DataSet extension methods I contributed to eXpand (see http://goo.gl/TF26g)

    static DataSet DataSet() {

        var dataSet = new DataSet();

     

        dataSet.ImportExcelXLS(new[]{

            "Customer", "Receipt", "Company", "CompanyType", "Country", "Employee", "Language", "Movie", "MovieArtist",

            "MovieArtistLine", "MovieCategory", "MovieCategoryPrice", "MovieCompany", "MovieItem",

            "MovieMovies_CountryCountries", "MoviePicture","Person","Artist","ArtistPicture","Rent"

        });

     

        return dataSet;

    }

     

    Now we are ready to create a UnitOfWork with this DataSet as DataStore like,

     

    var inputUnitOfWork = new UnitOfWork(new SimpleDataLayer(new DataSetDataStore(DataSet(), AutoCreateOption.None)));

     

    and finally call the Import method of the InitDataImporter class as shown bellow

     

    var initDataImporter = new InitDataImporter();

                var unitOfWork = OutputUnitOfWork();

                var inputUnitOfWork = new UnitOfWork(new SimpleDataLayer(new DataSetDataStore(DataSet(), AutoCreateOption.None)));

                initDataImporter.Import(() => new UnitOfWork(unitOfWork.ObjectLayer), () => new UnitOfWork(inputUnitOfWork.ObjectLayer));

    This simple, however powerful technic can be applied to any type of input source as long as you are able to fill a DataSet!

    You can download a sample project was created with v12.2.5 of our Suite from Importer.Console.

    Happy New Year to everybody!

  • XVideoRental real world application (RWA) – The overview

    XAF knowledge base is really rich and in our Support Center you can find solutions for all common business problems. Moreover our community project www.expandframework.com has tones of reusable solutions, we collected a few of them and applied them to the video rent business. The demo is available with 12.2.4 version of our Suite and can be found in the same folder with the other amazing XAF demos!

    XAF is a framework that provides the tools that a developer needs for building business solutions.

    1. A state of the art multi layered configuration system – The Application Model.
    2. A robust and flexible Security System that can be used to protect our data and control the auto generation of the UI based on Role permissions
    3. Its own reflection system (TypesInfo)– with amazingly simple ways to do what .NET does not offer eg. Runtime members, dynamic attributes etc.
    4. A well designed architecture to guide you where you should put your code. Moreover a very flexible modularization for structuring and further supporting any solution.
    5. WinForms/ASP.NET integration for a large number of our components
    6. Rich documentation and a click and run SDK!

    XVideoRental is a real world application (RWA). It is designed as a clone of our WinForms VideoRent application and demos only a small part of XAF capabilities. XAF provides the tools/architecture to do extraordinary things so we have followed a (no code)/(design at runtime) approach!. We recommend you to open the legacy Winforms demo and XVideoRental side by side and compare how much faster and richer is XAF development versus the traditional one. Moreover take a look at this help document that extensively compares traditional vs XAF development.

    1. XVideoRental uses a mechanism to serialize classes into Application Model and control them further from there! Therefore minimizes the learning curve since it is based on your existing component knowledge. (see this blog)
    2. It is a modeling rather than development technic. In addition allows further modeling at runtime since XAF already provides a runtime Model Editor.
    3. Implementations are collected from our usual places (Support Center / Code Central) / www.expandframework.com.

    The XVideoRental demo has three projects:

    Common.Win

    This is a library (not XAF module) and contains 95% of the code needed to model the VideoRental business! As a library can be reused from all domains and not only for video rental business. By extending the model as discussed in Dressing up our classes – Date with a model tonight! it is possible to create an abstract mechanism and control any class/component from there. Since all business operations can be modeled, the library contains controllers like NetIncomeController, TopObjectController that map to the real world meanings and can be applied to any domain. The XAF Application Model serves as an abstraction from technology and business specifics. This module is assembled by collecting the classes/implementations from our Support Center and from our community project www.expandframework.com.

    XVideoRental.Module.Win

    References Common.Win library and simple initializes the required classes/controllers by deriving from the abstract implementation of Common.Win library. However even if it is possible to “model” any behavior sometimes technical depth must be considered. Therefore in the project you will find a small number of video rental specific classes. In this project you can also find the model differences for this business domain.

    XVideoRental.Win

    This is the application project and contains no code. Only a few default configurations like module and security registrations.

    In the next post we are going to discuss the steps involved in building this application. I remind you again that all code functionality is borrowed from our community, this means that code / ideas really belong to you! We welcome more of your contributions and ideas to build even more wonderful applications. Please explore this demo and we are waiting for your feedback in order to blog more about features that interest you.

    A similar version can found in our community project www.expandframework.com. This version has exactly the same functionality as the original demo. However it references directly the eXpand framework so the Common.Win project does not exist nor the initialization of the controllers in the XVideoRental.Module.Win.

    We are looking forward for your feedback and contributions in order to make this demo even more powerful! In its current state, a developer can replace before lunch all the business objects with objects of a different business domain then package the application and go to customer and continue development there, at runtime! The final goal as developers is to build such configurable applications that business users can use and modify and shift this weight of our backs. XAF provides all the tools for this and this demo is a proof for this!

    Bellow is a short video that shows this demo. You can find about two more dozen of images of this demo in our FB group

    PS: A thanks to everybody that already provided feedback and reported issues with this demo. We already provided a newer version with improved performance and you can find it here. For posts related to the XVideoRental demo please subscribe to to the XVideoRental tag

    Happy XAFing!

  • OBSOLETE - How to deploy the OData security service in the Cloud for further use by a mobile DevExtreme client

    UPDATED

    This article is obsolete and we no longer provide any support on its content.

    Instead,  refer to the XAF Goes Mobile: CTP Version Ships with v15.2.9 post.

    ==================================================


    In this blog post, we’ll use the new DXTREME HTML JS framework to create a phone web app that works on iOS and Android devices. You can DOWNLOAD a working version of this app here (DXperience Universal 12.2.4 is required). Please do not miss a gentle disclaimer in the end.

    1. Mobile Client Considerations

    Let’s start with an XAF application already connected to the built-in middle-tier security service via WCF or .NET Remoting. It may look like this:

    Contacts

    If you have an existing XAF app and want to connect it to the security service, you can change the application connection string to a service URL, e.g.:
    http://samplexafsecurity.cloudapp.net/CustomWcfSecuredDataServer.svc

    Once the XAF application is connected to the security service, the built-in Security module will handle user authentication, data filtering and UI modifications as needed, according to specified security permissions. However, we need to extend the capabilities delivered by the built-in module to handle more specific security scenarios, such as:

      Is user X permitted to use this app?
      Is user X permitted to perform operation Y on data record Z?
      Is user X allowed to read member Y for data record Z?

      We can handle all of these scenarios with a custom authentication service.

      Before we start, we should think about the way we want to transfer data to our clients. Because of the wide variety of client platforms, we need protocol that allows interoperability across all the platforms and devices we want to support. OData is a great candidate. Another great choice would be DXTREME, which supports iOS, Android, Win8 and other operating systems, without forcing us to dig into the framework of each individual platform.

      Finally, we will deploy our authentication service to Windows Azure. Let’s get started!

      2. Database in the Cloud

      Note: These instructions apply to Windows Azure, but other cloud providers have similar capabilities. A quick Google search shows this is true for Amazon (btw, I recommend this blog if you are thinking about moving your XAF Web app to Amazon).

      Let’s start by launching the Azure portal. Click the SQL Databases tab and then click Create an SQL Database. Specify the necessary parameters:

      NewDB

      Note we did not specify an existing database server - we will create a New SQL Database Server.

      Next we need to fill in the SQL Server administrator credentials and specify its physical location. We will also need to create tables and provide some initial data. To make that easier, we have prepared the Azure_XAF_AuthenticationDemoDB.sql file, which is a part of the DBCreator project in this example. To use this file, we will need to locate the TestDB database created above and click the MANAGE button at the bottom: 

      ManageDB

      Click Open and provide a path to the SQL file (.\Utils\DBCreator\Azure_XAF_AuthenticationDemoDB.sql):

      OpenSQL

      Opening this file will create the required database and tables. Next we need to populate these tables with some test data using the DBCreator project mentioned earlier. It just needs an updated connection string from our Azure portal. Place the connection string in the .Utils\DBCreator\app.config file. It should look something like this:

      "User ID=test;Password=***;Pooling=false;Data Source=***.database.windows.net;Initial Catalog=TestDB"

      With the connection string specified, we can run the DBCreator console app:

      updateDB

      Running DBCreator does the following:

        Creates three users in the SecuritySystemUser table: one with unlimited rights (Admin) and two regular users (Sam and John). All users have empty passwords for the demo.
        Adds a few contacts corresponding to these users to the Contact table
        Creates security permissions so sample users can view all contacts, but only be able to edit their personal contacts.
        Creates a custom permissions set for the “Advanced User” contact. By default Sam and John can view only Photo and First Name fields; remaining fields are hidden.

        Code for creating permissions is not included in this article, however it is easily discoverable in the attached example.

        3. A WCF Middle-Tier Security Service

        Once our work with the database is complete, we can begin work on the most interesting part of this article – the creating and deploying of a service in the cloud! Let’s begin with using the Empty Web Application project template to create our service application called ClientServer_Wcf_CustomAuth.Service.

        NewWebApp

        The next step is to add a new WCF service called CustomWcfSecuredDataServer using the built-in command under the context menu invoked for the created project in the Solution Explorer. This will result in Visual Studio generating CustomWcfSecuredDataServer.svc and ICustomWcfSecuredDataServer.cs files. We will remove the latter file as it is not needed. The next step is to replace the base class for the CustomWcfSecuredDataServer class with the WcfSecuredDataServer.

        namespace ClientServer_Wcf_StandartAuth.Server {
        
            [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        
            [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, MaxItemsInObjectGraph = System.Int32.MaxValue, ConcurrencyMode = ConcurrencyMode.Multiple)]
        
            public class CustomWcfSecuredDataServer : WcfSecuredDataServer {
        
                private static SecuredDataServer securedDataServer;
        
                public static void SetSecuredDataServer(SecuredDataServer dataServer) {
        
                    securedDataServer = dataServer;
        
                }
        
                public CustomWcfSecuredDataServer() : base(securedDataServer) { }
        
            }
        
        }


         

        Next, we will add a Global.asax file using the built-in item template and modify its code as follows:


         

        namespace ClientServer_Wcf_StandartAuth.Server {
        
            public class Global : System.Web.HttpApplication {
        
                public static string ConnectionString;
        
                protected void Application_Start(object sender, EventArgs e) {
        
                    ConnectionString = ConfigurationManager.ConnectionStrings["AzureApplicationServices"].ConnectionString;
        
                    CustomAuthenticationServiceHelper customAuthenticationServiceHelper = new CustomAuthenticationServiceHelper();
        
                    ValueManager.ValueManagerType = typeof(ASPRequestValueManager<>).GetGenericTypeDefinition();
        
                    QueryRequestSecurityStrategyHandler securityProviderHandler = delegate() {
        
                        return new SecurityStrategyComplex(typeof(SecuritySystemUser), typeof(SecuritySystemRole), new AuthenticationStandard());
        
                    };
        
                    SecuredDataServer dataServer = new SecuredDataServer(
        
                            ConnectionString, customAuthenticationServiceHelper.XPDictionary, securityProviderHandler);
        
                    CustomWcfSecuredDataServer.SetSecuredDataServer(dataServer);
        
                }
        

        A final tweak will alter the web.config file to warn WCF about us transferring relatively large amounts of data to it (e.g., photos):

        <wsHttpBinding>
        
        <binding maxReceivedMessageSize="2147483646">
        
        <readerQuotas maxDepth="32" maxStringContentLength="5242880" maxArrayLength="2147483646" maxBytesPerRead="4096" maxNameTableCharCount="5242880"/>
        
        </binding>
        
        </wsHttpBinding>

         

        This concludes this step. You can always find more details in the source code of the ClientServer_Wcf_CustomAuth.Service project that hosts this service. It is not rocket science, and I believe that experienced XAFers noticed that the service code mimics what you can already find in the Application Server project template provided by XAF – here we just did things manually and used an ASP.NET application instead of the Windows Service or Console Application projects by default. Due to a heavy demand from our customers, we chose the ASP.NET application format as an example.

        4. Deploying the middle-tier security service in Azure

        By using the built-in Add Windows Azure Cloud Service Project command to create a new project titled the ClientServer_Wcf_CustomAuth.Service.Azure, we can easily deploy our newly-created security system in Azure.

        AddAzureCloudServiceProject_step1

        Please utilize the screenshots below to review the steps that follow.

        CSPPublish

        CSPPublish_step2

        CSPPublish_step3

        CSPPublish_step4

        CSPPublish_step5

        CSPPublish_step6

        Once all the above-mentioned steps have been completed, we can simply press the Publish button and wait for the service to begin so that we can ensure that things run smoothly via our Azure portal.
        Since everything operates correctly, we can now access our service at samplexafsecurity.cloudapp.net.

        5. An OData Service Consumed by Mobile and Other Non-XAF Clients

        Let’s begin by creating an empty Web project called CustomAuthenticationService. Next, we will add a class, which is derived from the base XpoDataServiceV3 class. (You may recall that it was added in XPO 12.2 to support the latest OData version.) Now we will add several service operation methods (IsGranted, IsUserAllowed, CanReadMembers) marked with the WebGet attribute to indicate that they are only used to retrieve information from the service, which will be REST by nature. Methods are required to implement a security on the client. 

        namespace CustomAuthenticationService {
        
            [JSONPSupportBehaviorAttribute]
        
            [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
        
            [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, MaxItemsInObjectGraph = System.Int32.MaxValue, ConcurrencyMode = ConcurrencyMode.Multiple)]
        
        
            public class CustomAuthenticationDataService : XpoDataServiceV3, System.Data.Services.IRequestHandler {
        
                private DataServiceHelper _dataServiceHelper;
        
                public CustomAuthenticationDataService()
        
                    : this(new HttpContextWrapper(HttpContext.Current)) {
        
                }
        
                public CustomAuthenticationDataService(HttpContextBase httpContext)
        
                    : this(httpContext, new CustomAuthenticationServiceHelper(), "CustomAuthenticationService") {
        
                }
        
                public CustomAuthenticationDataService(HttpContextBase httpContext, DataServiceHelper dataServiceHelper, string containerName) :
        
                    base(new MyContext(containerName, dataServiceHelper.NamespaceName, dataServiceHelper.CreateDataLayer())) {
        
                    if((httpContext == null) && (HttpContext.Current == null)) {
        
                        throw new ArgumentNullException("context", "The context cannot be null if not running on a Web context.");
        
                    }
        
                    _dataServiceHelper = dataServiceHelper;
        
                }
        
                public static void InitializeService(DataServiceConfiguration config) {
        
                    //config.SetEntitySetAccessRule("*", EntitySetRights.All);
        
                    config.SetEntitySetAccessRule("Contact", EntitySetRights.All);
        
                    config.SetServiceOperationAccessRule("IsGranted", ServiceOperationRights.AllRead);
        
                    config.SetServiceOperationAccessRule("IsUserAllowed", ServiceOperationRights.AllRead);
        
                    config.SetServiceOperationAccessRule("CanReadMembers", ServiceOperationRights.AllRead);
        
                    config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
        
                    config.DataServiceBehavior.AcceptProjectionRequests = true;
        
                }
        
                protected override void HandleException(HandleExceptionArgs args) {
        
                    if(args.Exception.GetType() == typeof(ObjectLayerSecurityException)) {
        
                        //Forbidden
        
                        args.Exception = new DataServiceException(403, args.Exception.Message);
        
                    }
        
                    base.HandleException(args);
        
                }
        
                [WebGet]
        
                public bool IsGranted(string objectType, string memberName, string objectHandle, string operation) {
        
                    Type type = XafTypesInfo.Instance.FindTypeInfo(objectType).Type;
        
                    return ((IRequestSecurity)SecuritySystem.Instance).IsGranted(new ClientPermissionRequest(type, memberName, objectHandle, operation));
        
                }
        
                [WebGet]
        
                public IEnumerable<string> CanReadMembers(string objectType, string membersName, string targetObjectsHandle) {
        
                    List<string> _membersName = new List<string>(membersName.Split(';'));
        
                    List<string> _targetObjectsHandle = new List<string>(targetObjectsHandle.Split(';'));
        
                    Type type = XafTypesInfo.Instance.FindTypeInfo(objectType).Type;
        
                    Dictionary<string, bool> canReadMembers = ((IRequestSecurity)SecuritySystem.Instance).CanReadMembers(type.AssemblyQualifiedName, _membersName, _targetObjectsHandle);
        
                    List<string> result = new List<string>();
        
                    foreach(KeyValuePair<string, bool> item in canReadMembers) {
        
                        result.Add(item.Key + ";" + item.Value);
        
                    }
        
                    return result;
        
                }
        
                [WebGet]
        
                public bool IsUserAllowed() {
        
                    return true;
        
                }
        
            }
        
        }


        This would be a good time to take a deeper look at this service.  As you can see, we limited access to the Contact business object when configuring the service:

        config.SetEntitySetAccessRule("Contact", EntitySetRights.All);

        This is a flexible solution for controlling what is accessible and what is hidden from service users. Error handling is provided by the HandleException method, which wraps ObjectLayerSecurityException into DataServiceException – a step required for providing a client application with meaningful error code. There are three primary exceptions: the DataServiceClientException for client operations (updates/inserts), the DataServiceQueryException for querying via LINQ and the DataServiceRequestException for requesting data via a URI. All other exceptions will be passed to the client as an “«Internal Server Error»” with the 501 code, which does not provide a lot of useful information in many cases.

        There are also a number of auxiliary classes that are required for the service to work.

        ·       The DataServiceHelper class is a helper that provides methods for accessing XAF IObjectSpaceProvider and XPO IObjectLayer entities.

        ·       The JSONPSupportInspector is required to provide jsonp support; you can learn more about it here.

        ·       The ASPRequestValueManager<ValueType> is required to store security system objects within the context of a current request.

        ·       MyContext is a descendant of the XpoContext base class.  We overrode the ShowLargePropertyAsNamedStream and HideProperty methods in this class to tell XPO to:

        -pass streams as a part of the query instead of the URL,

        -and to hide the Oid property of the Contact class.

        Once we are done with the XPO OData service, we will add the Global class using the respective item template. In this class, we will handle all the queries to our service using the means provided by the XAF security system. Let’s review the Application_AuthenticateRequest method, where we will check user credentials and request further processing.

        namespace CustomAuthenticationService {
        
            public class Global : System.Web.HttpApplication {
        
                public static string ConnectionString;
        
                protected void Application_Start(object sender, EventArgs e) {
        
                    ValueManager.ValueManagerType = typeof(ASPRequestValueManager<>).GetGenericTypeDefinition();
        
                    ConnectionString = ConfigurationManager.ConnectionStrings["AzureApplicationServices"].ConnectionString;
        
                }
        
                protected void Session_Start(object sender, EventArgs e) { }
        
                protected void Application_AuthenticateRequest(object sender, EventArgs e) {
        
                    SecurityStrategyComplex securityStrategy = new SecurityStrategyComplex(typeof(SecuritySystemUser), typeof(SecuritySystemRole), new AuthenticationStandard());
        
                    SecuritySystem.SetInstance(securityStrategy);
        
                    // Remember claims based security should be only be used over HTTPS
        
                    //if(context.Request.IsSecureConnection){
        
                    string userName = GetUserName(HttpContext.Current.Request);
        
                        if(string.IsNullOrEmpty(userName)) {
        
                            HttpContext.Current.Response.Status = "401 Unauthorized";
        
                            HttpContext.Current.Response.StatusCode = 401;
        
                            HttpContext.Current.Response.End();
        
                            return;
        
                        }
        
                        ((AuthenticationStandardLogonParameters)SecuritySystem.LogonParameters).UserName = userName;
        
                        ((AuthenticationStandardLogonParameters)SecuritySystem.LogonParameters).Password = GetPassword(HttpContext.Current.Request);
        
                    //}
        
                    CustomAuthenticationServiceHelper helper = new CustomAuthenticationServiceHelper();
        
                    try {
        
                        //Calling Cross Domain WCF Service using Jquery
        
                        // http://www.devexpress.com/Support/Center/Issues/ViewIssue.aspx?issueid=KA18633
        
                        string origin = HttpContext.Current.Request.Headers["Origin"];
        
                        if(!string.IsNullOrEmpty(origin)) {
        
                            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", origin);
        
                        }
        
                        string method = HttpContext.Current.Request.Headers["Access-Control-Request-Method"];
        
                        if(!string.IsNullOrEmpty(method))
        
                            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", method);
        
                        string headers = HttpContext.Current.Request.Headers["Access-Control-Request-Headers"];
        
                        if(!string.IsNullOrEmpty(headers))
        
                            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", headers);
        
                        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
        
                        if(HttpContext.Current.Request.HttpMethod == "OPTIONS") {
        
                            HttpContext.Current.Response.StatusCode = 204;
        
                            HttpContext.Current.Response.End();
        
                        }
        
                        SecuritySystem.Instance.Logon(helper.ObjectSpaceProvider.CreateObjectSpace());
        
                    }
        
                    catch(AuthenticationException) {
        
                        HttpContext.Current.Response.Status = "401 Unauthorized";
        
                        HttpContext.Current.Response.StatusCode = 401;
        
                        HttpContext.Current.Response.End();
        
                    }
        
                }
        

        As you can see, the main work is performed by the well-known SecurityStrategyComplex and AuthenticatinStandard components, which are used in the client XAF apps. The same built-in SecuritySystemUser and SecuritySystemRole business classes are used as well. The user credentials are retrieved from the request headers and parameters, which are originally specified by the client application initiating the request to the server. Of course, for better safety, this approach must work over HTTPS only. If the specified credentials are wrong, the AuthenticationException error with code "401 Unauthorized" is thrown. If everything is fine, the client’s request is passed further where the CustomAuthenticationDataService instance is created.

        Note that it is necessary for cross-domain operations to modify the server’s response in a speific way. Further details can be found in this KB Article.

        Finally, we will deploy the created service in Azure using the instructions we described in the previous chapter. 

        TAKE SPECIAL NOTE IF YOU UPGRADE THIS PROJECT CODE TO THE LATEST XAF VERSION:
        1. The CanReadMembers method called in the CustomAuthenticationDataService class no longer exists. Refer to the http://www.devexpress.com/issue=Q477749 ticket for a replacement.

        2. The DevExpress.ExpressApp.Security.ClientPermissionRequest class used in the CustomAuthenticationDataService class is now obsolete.  Use 'PermissionRequest' instead. Refer to the https://www.devexpress.com/issue=T322857 ticket for a replacement.

        3. The CreateSelectDataSecurity method called in the CustomAuthenticationServiceHelper class is no longer public. Instead, modify the line as follows: ((ISelectDataSecurityProvider)SecuritySystem.Instance).CreateSelectDataSecurity()


        6. The DXTREME Mobile Client

        This client application will use the CustomAuthenticationDataService we just created to get data and to get information about how to build a UI based on user rights.

        To quickly get started with a DXTREME client, let’s use the recently introduced wizard, which will build CRUD views based on the specified data service. Since our service requires a user name and password, we will have to temporarily disable this feature, so as to not confuse the wizard. This can be done by returning “Sam”, along with an empty string from the GetUserName and GetPassword methods of our Global class respectively.

        ·        Once the wizard completes its work, several views will be created. Let’s rename them as follows:

        ·        - contact-ListView
        - contact-DetailView
        - contact-EditView

        In addition, we will create a logon form, from which the IsUserAllowed service call will be initiated. A user is allowed to enter the application only if the service returns True. In case of a server error (e.g., 401(Unauthorized)) a user is rejected.

         

         

        logon-screen

         

        Once a user is authorized, the contact “ListView” is opened by default. This view has a command to create new contacts, which are available to Admin user only, by default.

        From a list form, a user can go to the contact ”DetailView”, which calls the IsGranted and CanReadMembers service methods. These calls are required for determining the visibility of the Edit and Delete buttons. If a user has no right to edit an object, a special UI Level Security button is displayed (just for the demo). The purpose of this button is to control whether or not the UI should be affected according to the results of our OData authentication service. For instance, if you set the option to Off, the IsGranted call will be ignored and the Edit/Delete buttons will always be displayed.  

         

        detail-screen_google

         

        Pressing the Edit button will take you to the contact “EditView”. Once there, you can modify data and press the Save button when you are done. The save operation will complete only if the user has the right to do so. Otherwise, an error will appear.

         

        screenshot

        Finally, you can DOWNLOAD a working version of this project here (DXperience Universal 12.2.4 is required). 

        A Quick Summary!

        1. We created a custom WCF middle-tier security service, in which we leveraged ready-to-use XAF security system means, such as SecuredDataServer, etc. We didn’t invent anything new, except for hosting the service in a regular ASP.NET application deployed in Azure, because our users inquired about how to do this in the past. Please note that our service is connected to the SQL Azure database, which is powered by our XPO datalayer.

        2. We created an XAF desktop app that is connected to the security service by simply changing the connection string to a service URL. No additional configuration was required, because it is already provided by the XAF security module out-of-the-box. With the XAF client, you can easily perform all the administrative work, such as creating users and roles, configuring security permissions, etc.

        3. We built a custom authentication OData service to provide a bridge from the built-in XAF security system to non-XAF apps. This OData service not only takes data from a middle-tier security service according to the rights of the user talking to it, but also indicates what this user is allowed to do with a certain business object, its fields, etc., allowing for the performance of UI transformations accordingly.

        4. We demonstrated the use of the OData authentication services with a DXTREME mobile client, mainly because more and more XAF customers are interested in creating mobile solutions for their clients. Take special note that could create a Console, WPF or any other application type that “understands” OData. This opens even more opportunities and flexibility for you.


        Disclaimer

        Please take special note that this is just a demonstration post that shows how to use XAF SecurityStrategyComplex class to filter out sensitive data when developing a DevExtreme mobile application.  This is not a complete guideline or recommendation on how to develop your apps by any means, but rather an illustration of our vision at the publication date on how this task can be solved. So, please treat it appropriately, because the described solution may contain bugs or there may be better approaches over time. As of the publication date, we do not ship ready and recommended solutions for developing mobile apps with XAF and the Middle Tier Security feature is at the Beta stage. We are working to finish it and will surely provide additional information regarding this scenario when we get results (we cannot provide any ETA on this for now). That said, we will not be able to update the described solution and samples according to the XAF and DevExtreme updates and also provide immediate solutions or support if something stops working in the future. If you are interested in using the new XAF security system with non-XAF applications, you will probably be interested in adding this ticket to your Support Center Favorites list or subscribe to our team blog, as we may provide additional details there. 

         

         

        Our questions to you…

        Q1. Would you be interested in having a prebuilt project template for the OData authentication service we created?

        Q2. Would you use DXTREME to build a mobile client in addition to your existing XAF desktop app (this is a continuation of an earlier discussion; nevertheless, I wanted to get specific input on this from other users)? Or you would be all right with an XAF ASP.NET application optimized for mobile/tablet use via specialized templates with large buttons, an adaptive menu and other elements providing a superior experience on touch devices (see here for more details)?

        Please post your answers to these questions and your thoughts about this in-depth how-to-series in your comments. Happy XAFing!Smile

      1. Status Update for Entity Framework Support in XAF v2012 Vol 2

        In the v2012 vol 2 release, we have significantly improved Entity Framework (EF) support in XAF. You can start using EF in your production applications instead of XPO, but with certain limitations, as described below.

        What are the current EF limitations in XAF?

        1. EF support was recently introduced in the 12.1 release of XAF, while XPO has been used in XAF from the beginning. This is why the majority of our demos and examples are based on XPO.
        2. With EF, you still cannot use complex security system configurations that involve a server-side security engine or XPO-specific integrated mode (the UI-Level mode is supported; see the EFDemoCodeFirst and EFDemoModelFirst demos).
        3. Although the most frequently used XAF modules are compatible with EF, there are several extra XAF modules that have not been upgraded/tested for EF compatibility.  The table below lists these modules, and indicates which data access technologies are currently supported by XPO, DC and EF.

          Extra Module \ Data Access Technology XPO DC EF
          Audit Trail Module  
          Chart Module
          Clone Object Module  
          Conditional Appearance Module
          File Attachments Module
          HTML Property Editor Module
          KPI Module  
          Pivot Chart Module
          Pivot Grid Module
          Reports Module
          Scheduler Module
          Script Recorder Module
          State Machine Module
          TreeList Editors Module
          Validation Module
          View Variants Module
          Workflow Module  

        Choosing between EF and XPO

        If you’re trying to decide which ORM to learn (XPO or EF), and you don’t have a preference for either ORM, we recommend XPO. XPO is well-supported by our XAF product, and we provide extensive documentation on how to use XPO in XAF. However, if you are already proficient in EF and would prefer to use this ORM, you can use EF, but with certain limitations. We will be improving support for EF in upcoming releases.

         

        Getting Started with EF in XAF

        To learn how to use EF in XAF v12.2, refer to the following topics:

      2. Dressing up our classes – Date with a model tonight!

        Classes are in a sense behavior libraries. To initialize or configure them, we can use their properties. Serialization is a technic that helps distribute the state of our classes. In this post we will discuss how to serialize any class in the multi-layer XAF model! Then we finish with the model synchronizers which will control the class. Having a class or a part of a class serialized in the model makes it possible to continue development there without writing more code (declarative/runtime approach).

        Runtime classes

        In .NET is super easy to create runtime classes or interfaces. The following snippet will create a assembly with a Dynamic class.

        var provider = CodeDomProvider.CreateProvider("c#");

        var results = provider.CompileAssemblyFromSource(new CompilerParameters(), new[] { @"public class DynamicClass{}"        });

        Assembly compiledAssembly = results.CompiledAssembly;

        Modeling

        XAF’s super advanced and simple model technology is using interfaces for design and further extension. This is well documented How to: Extend Application Model. To serialize any class on the XAF model we simply need to use reflection, extract an interface from the class and extend any part of the model we want with it.

        Let’s see a few examples using XAF build-in components.

        1) GridListEditor

        XAF’s GridListEditor is based on the GridView class which is a very sophisticated component with a lot of properties. Serializing it into the model allow us to control it from there. Using the model it is possible to work at runtime and since we have a lot of properties it is wise to serialize their help as well (see red arrow).

        image

        2) ChartListEditor

        This is a very useful editor that also needs better control from our model. Note that this model serialization is reusable from all our projects!

        image

        This technic can boost our runtime experience and make it possible to work side by side with our customer building really fast. Furthermore it is applicable to any class or component. Let’s a few more interesting examples.

        3) Repository Items

        XAF’s DX PropertyEditors render DX controls which store their configuration in RepositoryItem classes. We can serialize all ReporitoryItem descendants and extend the IModelColumn. As a result by simply changing a model attribute we can do things at runtime that with a code approach would be very time consuming.

        image

        4) The SchedulerListEditor

        image

        5) The PivotListEditor

        This one uses the same serialization approach however since this is a very useful editor we modeled a few extra behaviors – the pivot rules!. We would probably create a separate post about them.

        image

        Furthermore here we need to notice that we can model business meanings such as NetIncome, so for the following model modifications,

        image

        XAF will render the View bellow without any coding or dependency to any object!

        image

        Another example, when we talk about the TopObject meaning, we can model it like

        image

        and XAF will give us the following View again without writing not even one line of code (no code==no bugs)

        image

        Model synchronization

        ModelSynchronizers can be used to:

        a) set up an entity according to the configuration stored in the Application Model
        b) Persists the configuration of an entity into the Application Model

        Model’s nature makes it very abstract so since the attributes of the model are automatically generated from the properties of a class, its rather easy to do the opposite. Therefore we can write an abstract class that will implement IModelSynchronizable interface. The method SynchronizeValues is reading from any class and updates the model. The method ApplyValues is reading the values of the model and synchronize any class that properties match model’s attribute Ids.

        protected void SynchronizeValues(ModelNode modelNode, object component, PropertyDescriptorCollection properties) {

            foreach (var valueInfo in GetModelValueInfos(modelNode)) {

                var propertyDescriptor = properties.Find(valueInfo.Name, false);

                if (propertyDescriptor != null) {

                    var propertyValue = GetPropertyValue(component, propertyDescriptor, valueInfo, modelNode);

                    var modelValue = GetSynchronizeValuesNodeValue(modelNode, valueInfo, propertyDescriptor, valueInfo.PropertyType.IsNullableType(), component);

                    if (modelValue != null && !modelValue.Equals(propertyValue)) {

                        modelNode.SetValue(valueInfo.Name, propertyValue);

                    }

                }

            }

        }

         

        protected void ApplyValues(ModelNode node, object component, PropertyDescriptorCollection properties) {

            foreach (var valueInfo in GetModelValueInfos(node)) {

                var propertyDescriptor = properties.Find(valueInfo.Name, false);

                if (propertyDescriptor != null) {

                    var nodeValue = GetApplyModelNodeValue(node, valueInfo);

                    if (nodeValue != null) {

                        var propertyType = propertyDescriptor.PropertyType;

                        var propertyValue = propertyDescriptor.GetValue(component);

                        if ((!IsDefaultCoreValue(nodeValue, propertyType) || (!nodeValue.Equals(propertyValue))) && propertyType.IsValidEnum(nodeValue)) {

                            if (!nodeValue.Equals(propertyValue))

                                propertyDescriptor.SetValue(component, nodeValue);

                        }

                    }

                }

            }

        }

         

        Finally for each component we want to synchronize we create a descendant of the above implementation. For example,

        AdvBandedListEditor

        public class AdvBandedViewOptionsSynchronizer : ComponentSynchronizer<DevExpress.XtraGrid.Views.BandedGrid.AdvBandedGridView, IModelOptionsAdvBandedView> {

            public AdvBandedViewOptionsSynchronizer(AdvBandedListEditor control)

                : base(control.GridView, control.Model.OptionsAdvBandedView, ((IColumnViewEditor)control).OverrideViewDesignMode) {

            }

        }

         

        ChartControlListEditor

        public class ChartControlSynchronizer : ComponentSynchronizer<DevExpress.XtraCharts.ChartControl, IModelOptionsChart> {

            readonly XafApplication _application;

         

        We have discussed ways to model components into a distributable layer that describes a XAF. Even more we show how simple would be a model representation of business meanings (eg. NetIncome). It is clear that these tools are reusable from any project. We only need to feed XAF with a business domain and a few model modifications even in runtime to be able to have a working solution! A solution so rich in features that it is impossible to implement using the traditional approach.

        The code and screen shots are taken from the XVideoRental application. This demo that can be found along with the other XAF demos in v12.2. Next posts will be about this new demo that really proves how XAF can save our time by lifting the development to the runtime!

        P.S. If you are a facebook fun you can support us with a like for our new XAF page.

        We are happy to read your feedback about this!. Remember that your questions are the best candidates for future posts.

        Happy XAFing!

      LIVE CHAT

      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, write to us at info@devexpress.com or call us at +1 (818) 844-3383

      FOLLOW US

      DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, ASP.NET, WinForms, HTML5 or Windows 10, DevExpress tools help you build and deliver your best in the shortest time possible.

      Copyright © 1998-2017 Developer Express Inc.
      All trademarks or registered trademarks are property of their respective owners