Don Wibier's Blog
  • DevExtreme TreeList - Paging and State Persisting (v18.1)

    We've included some really cool features in the DevExtreme TreeList widget in our upcoming v18.1 release.

    Paging

    If you're dealing with a lot of data in the TreeList, you might not want to scroll all the way down (or up).
    For this reason, we decided to implement a pager in the TreeList similar to the one found in the DataGrid widget. Not only does it allow you to page through the nodes, but it also allows you to select the page size and it shows you the number of nodes and pages.

    You can configure the use of the pager by specifying the paging and pager configuration options as shown below:

    {
        //...
        scrolling: {
            mode: "standard"
        },
        paging: {
            enabled: true,
            pageSize: 10
        },
        pager: {
            showPageSizeSelector: true,
            allowedPageSizes: [5, 10, 20],
            showInfo: true
        }
    }
    

    The TreeList widget has the same API as the DataGrid widget.

    If you have questions or suggestions, feel free to take part in the discussion about this feature at GitHub.

    Note: Due to the difficulty of remote hierarchical data paging, this feature is only available on the client-side.

    State Persisting

    Suppose you have made some run-time customizations in the TreeList widget like filter-settings, column visibility or even expanded/collapsed/selected nodes.

    You can now re-apply these settings in a later stage of the application with our v18.1 version of the TreeList widget.
    Below is a code fragment on how to persist this information in the browser's localStorage:

    {
        //...
        stateStoring: {
            enabled: true,
            type: 'localStorage',
            storageKey: 'treeListStorage'
        }
    }
    

    Angular, Vue, React, ASP.NET MVC / Core and more

    All of these new features will be available on all the frameworks we support like Angular, Vue, React, jQuery, Knockout, ASP.NET MVC and .NET Core.

    Try it now?

    If you want to give these features a test-drive, simply add it to your project by entering:

    npm install --save devextreme@18.1-unstable
    

    If you're not using npm, check out this post.

    Like it?

    Let me know by replying to this post.

  • Microsoft Identity and XPO Continues: Support for .NET Core available

    Some time ago I wrote an article on an XPO-based storage provider for Microsoft Identity. This provider is available as NuGet package, but at the time of writing, we didn’t have XPO available for .NET Core.

    One of the things we released in v17.2 is XPO for .NET Core on which I did a webinar and a blog post. An obvious next step for the Identity Storage provider would be to make it  available on .NET Core as well.

    Some additional goals to achieve

    With the desire to support .NET Core as well as .NET Framework, there were some additional wishes I had with this project:

    One code base for .NET Framework and .NET Core

    Before I started with the .NET Core support, I had the impression that .NET Core version would be similar to the .NET Framework version. I was for 75% correct with that assumption.

    After investigating the Identity source code on GitHub, it seemed that there is some extra functionality in the .NET Core version (Claims on Roles, and UserTokens). Also the signature on several interface methods was changed. (Cancellation token parameter)

    As expected, the references in the .NET Framework version are different from the references for the .NET Core version.

    One NuGet package for .NET Framework and .NET Core

    Since the NuGet package format supports multi .NET version support, it would be nice to have everything packed up in one package.

    Use .NET Core Dependency Injection to configure XPO and Storage provider

    One of the sweet new things in ASP.NET Core is the Dependency Injection which comes with it. It seems obvious to use the D.I. to initialize the XPO datalayer as well as the storage provider in a similar way as the M.S. project template is doing for Entity Framework.

    What needs to be done?

    To have one project supporting both .NET Framework and .NET Core, the project file needs to be converted to the new csproj file format. We also need to change one of the default options to  support different .NET versions.

    Change the contents of the csproj file to the new format

    The old csproj file format looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" 
           Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
      <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
        <ProjectGuid>{4A9EC437-F8F0-4C53-B288-DE26E58F061F}</ProjectGuid>
        <OutputType>Library</OutputType>
        <AppDesignerFolder>Properties</AppDesignerFolder>
        <RootNamespace>DX.Data.Xpo.Identity</RootNamespace>
        <AssemblyName>DX.Data.Xpo.Identity</AssemblyName>
        <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
        <FileAlignment>512</FileAlignment>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <OutputPath>bin\Release\</OutputPath>
        <DefineConstants>TRACE</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
      </PropertyGroup>
      <ItemGroup>
        <Reference Include="DevExpress.Data.v17.1, Version=17.1.5.0, Culture=neutral, 
             PublicKeyToken=b88d1754d700e49a, processorArchitecture=MSIL">
          <HintPath>..\packages\DevExpress.Data.17.1.5.0\lib\DevExpress.Data.v17.1.dll</HintPath>
        </Reference>
        <!-- the rest of the file is omitted -->
    
      

    Together with a .nuspec file and a post build event, it is possible to build the project and the NuGet package in one go.

    The new csproj format, looks much cleaner and it encapsulates the .nuspec file function right into the project. I think that’s a nice improvement. To start using this new file format, the most easy way is to unload the solution and edit the source code. The DX.Utils project will look like below:

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <Version>1.0.0.6</Version>
        <FileVersion>1.0.0.6</FileVersion>
        <Authors>Don Wibier (DevExpress)</Authors>
        <Description>Several C# utility classes and extension methods</Description>
        <Copyright>Copyright (c) 2017 Don Wibier</Copyright>
      </PropertyGroup>
      
      <PropertyGroup>
        <TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
        <PackageLicenseUrl>https://github.com/donwibier/DXWeb/blob/master/LICENSE</PackageLicenseUrl>
        <PackageProjectUrl>https://github.com/donwibier/DXWeb/tree/master/DX.Utils</PackageProjectUrl>
        <PackageIconUrl>https://www.devexpress.com/favicon.ico</PackageIconUrl>
        <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
        <PackageTags>DXWeb Wibier DevExpress</PackageTags>
        <PackageReleaseNotes>
          1.0.0.6: Changed .NET Framework to v4.6.1
          1.0.0.5: Initial dual mode package for .NET Framework and .NET Standard 2.0
    Some features don't work yet on .NET Standard 2.0</PackageReleaseNotes>
        <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
      </PropertyGroup>
    
      <ItemGroup Condition="'$(TargetFramework)'=='net461'">
        <Reference Include="System" />
        <Reference Include="System.Configuration" />
        <Reference Include="System.Core" />
        <Reference Include="System.Drawing" />
        <Reference Include="System.Web" />
        <Reference Include="System.Xml.Linq" />
        <Reference Include="System.Data.DataSetExtensions" />
        <Reference Include="Microsoft.CSharp" />
        <Reference Include="System.Data" />
        <Reference Include="System.Xml" />    
      </ItemGroup>
    
      <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
        <PackageReference Include="System.Configuration.ConfigurationManager">
          <Version>4.4.1</Version>
        </PackageReference>
      </ItemGroup>
    </Project>
    
    
      

    I mentioned earlier that we need to make a small change in the default project format. It is the TargetFrameworks node; it contains both netstandard2.0 and net461. These values can be used for conditional ItemGroups to e.g. include framework dependent assembly and NuGet package references as well as source files. Because I included the Package information nodes, once I build the project, it will build the NuGet package automatically which will hold everything for .NET Framework and .NET Core.

    Once we reload the project and open one of the cs files, you will notice a small addition to de code editor of VisualStudio. It is the combobox to select the framework you’re working on:

    image

    If we build the project now, you will have a NuGet package which includes the multi-version content:

    image

    Now we need to do some conditional coding because of the changed signature of the Interface methods which I have implemented on the XPUserStore as well as the XPRoleStore. On the XPUserStore there are some additional interfaces implemented for 2 Factor Authentication.

    The easiest way to accomplish that is by using the #if (NETSTANDARD2_0) compiler directive to include or exclude specific code. Below is a fragement of the XPRoleStore which includes this conditional coding construct:

    #if (NETSTANDARD2_0)
        public class XPRoleStore<TKey, TRole, TXPORole, TXPORoleClaim> : XpoStore<TXPORole, TKey>,
            IQueryableRoleStore<TRole>,
            IRoleClaimStore<TRole>
            where TKey : IEquatable<TKey>
            where TRole : XPIdentityRole<TKey, TXPORole, TXPORoleClaim>, IRole<TKey>
            where TXPORole : XPBaseObject, IDxRole<TKey>, IRole<TKey>
            where TXPORoleClaim: XPBaseObject, IDxRoleClaim<TKey>
    #else
        public class XPRoleStore<TKey, TRole, TXPORole> : XpoStore<TXPORole, TKey>,
        	IQueryableRoleStore<TRole, TKey>
        	where TKey : IEquatable<TKey>
        	where TRole : XPIdentityRole<TKey, TXPORole>, IRole<TKey>
        	where TXPORole : XPBaseObject, IDxRole<TKey>, IRole<TKey>
    #endif
    {
        public XPRoleStore() : base()
        {}
    
        public XPRoleStore(string connectionName) :
          base(connectionName)
        {}
        public XPRoleStore(string connectionString, string connectionName) :
          base(connectionString, connectionName)
        {}
        public XPRoleStore(XpoDatabase database) :
          base(database)
        {}
    #if (NETSTANDARD2_0)
        public async virtual Task<IdentityResult> CreateAsync(TRole role, 
    						CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            await CreateAsync(role);
            return IdentityResult.Success;
        }
    #endif
        public virtual Task CreateAsync(TRole role)
        {
          ThrowIfDisposed();
          if (role == null)
          {
            throw new ArgumentNullException("role");
          }
          return Task.FromResult(XPOExecute((db, wrk) =>
          {
            var xpoRole = XPOCreateRole(wrk);
            xpoRole.Assign(role, 0);
            return null;
          }));
        }
        // rest of the code is omitted ...
    }
      

    What you see is that the .NET Core method calls the actual original .NET Framework method to maintain the one codebase strategy.

    Datamodel Changes

    As I mentioned earlier, Identity for .NET Core has some additional functionality. The most noticeable ones are the support for Claims on Roles and the implementation of UserTokens. Another smaller detail is the use of normalized fields in certain circumstances.

    What this means in that for essential lookup fields like username and email addresses, there is an extra field NormalizedEmail and NormalizedName. I implemented similar functionality in the first release of this provider by implementing the UserNameUpper and EmailUpper fields.

    The idea behind this is quite straightforward. Whenever we need to lookup records by name or email, we use the Upper (or Normalized for .NET Core) for selecting. Remember that some database back ends are sorting and filtering case-sensitive.We don’t want this on the accounts and roles. A technical side effect is that the index files of the specific tables only contain uppercase characters, so less combinations to compare which improves the search speed.

    The Normalized addition is even slightly better as the Upper approach I used. It should also remove the accents from characters which is common in certain languages to overcome sorting issues. .NET Core comes with a configurable Normalizer class which can be changed to suit your needs.

    If we take a look at the adapted datamodel, it will look like below:

    image

    One of the really cool things with XPO is that the datamodel will be changed automatically in the database once we have added the extra classes and fields. No need for migrations.

    Configuring Identity to use the XPO Storage provider

    If we create an ASP.NET Core project through the project template, you can see in the ~/Startup.cs file how the storage provider for E.F. is configured.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
        // Add application services.
        services.AddTransient<IEmailSender, EmailSender>();
        services.AddMvc();
    }
      

    The AddDbContext<T>(..) method is an extension method which comes with Entity Framework, and the .AddEntityFrameworkStores<ApplicationDbContext>() is another extension method which comes with the E.F. storage provider.

    I have created similar extension methods to first configure the XPO Datalayer, and also an extension method which enables you to inject a UnitOfWork into your controller or other parts of you application which support DI.

    public static class XpoCoreExtensions
    {
        public static IServiceCollection AddXpoDatabase(this IServiceCollection serviceCollection, string connectionName, string connectionString)
        {
            return serviceCollection.AddSingleton<XpoDatabase>(new XpoDatabase(connectionString, connectionName));
        }
        public static IServiceCollection AddXpoUnitOfWork(this IServiceCollection serviceCollection, string connectionName)
        {
            return serviceCollection.AddScoped<UnitOfWork>((sp) => sp.GetService<XpoDatabase>().GetUnitOfWork());
        }
    }
      

    The extension method for configuring the storage provider is a bit more complicated. In the ConfigureServices method, there is a call to services.AddIdentity<ApplicationUser, IdentityRole>(). In our case, these 2 types specify the DTO classes for our Storage provider.

    When configuring the XPO Storage provider, we need to pass in the persistent types for the ApplicationUser and Role. To follow the same approach, I’d like to do that through generic type parameters. The initialization will look like this:

    services
            .AddIdentity<ApplicationUser, ApplicationRole>()
            .AddXpoIdentityStores<XpoApplicationUser, XpoApplicationRole>()                
            .AddDefaultTokenProviders();
      

    The extension method contains some code to determine the generic types and pass those into the XPUserStore and XPRoleStore constructors. It furthermore contains some code to determine the types which where specified in the AddIdentity method. You can check how this is done by viewing the soure code on GitHub.

    How to use this in your own .NET Core App

    Thanks to the Dependency Injection in .NET Core, it’s pretty straightforward to configure the XPO Storage provider. Once you have created a new project or opened an existing one, you can remove all references to Entity Framework including the using statements. Also all code related to Entity Framework can be removed like the DBContext class and the migrations files. This is basically everything in the ~/Data folder.

    Next make sure you have configured your environment to use your personal DevExpress NuGet feed as described here.

    We can now add a reference to the DX.Data.Xpo.Identity NuGet package to the project. This will get all dependencies like DevExpress.Xpo as well.

    In the ~/Models/ApplicationUser.cs file, replace the ApplicationUser class with the following code:

    public class ApplicationUser : XPIdentityUser<XpoApplicationUser>
    {
        public ApplicationUser(XpoApplicationUser source) : base(source)
        {}
    
        public ApplicationUser(XpoApplicationUser source, int loadingFlags) : base(source, loadingFlags)
        {}
    
        public ApplicationUser()
        {}
    
        public override void Assign(object source, int loadingFlags)
        {
            base.Assign(source, loadingFlags);
            //XpoApplicationUser src = source as XpoApplicationUser;
            //if (src != null)
            //{
            //	// additional properties here
            //	this.PropertyA = src.PropertyA;
            //	// etc.				
            //}
        }
    }
      

    And add the following code to the file as well:

    public class ApplicationRole : XPIdentityRole<XpoApplicationRole>
    {
        public ApplicationRole(XpoApplicationRole source, int loadingFlags) : base(source, loadingFlags)
        {}
    
        public ApplicationRole(XpoApplicationRole source) : base(source)
        {}
    
        public ApplicationRole()
        {}
        public override void Assign(object source, int loadingFlags)
        {
            base.Assign(source, loadingFlags);
            //XpoApplicationRole src = source as XpoApplicationRole;
            //if (src != null)
            //{
            //	// additional properties here
            //	this.PropertyA = src.PropertyA;
            //	// etc.				
            //}
        }
    }
    
    // This class will be persisted in the database by XPO
    // It should have the same properties as the ApplicationUser
    [MapInheritance(MapInheritanceType.ParentTable)]
    public class XpoApplicationUser : XpoDxUser
    {
        public XpoApplicationUser(Session session) : base(session)
        {
        }
        public override void Assign(object source, int loadingFlags)
        {
            base.Assign(source, loadingFlags);
            //ApplicationUser src = source as ApplicationUser;
            //if (src != null)
            //{
            //	// additional properties here
            //	this.PropertyA = src.PropertyA;
            //	// etc.				
            //}
        }
    }
    
    [MapInheritance(MapInheritanceType.ParentTable)]
    public class XpoApplicationRole : XpoDxRole
    {
        public XpoApplicationRole(Session session) : base(session)
        {
        }
        public override void Assign(object source, int loadingFlags)
        {
            base.Assign(source, loadingFlags);
            //ApplicationUser src = source as ApplicationUser;
            //if (src != null)
            //{
            //	// additional properties here
            //	this.PropertyA = src.PropertyA;
            //	// etc.				
            //}
        }
    }
      

    The last thing we need to do is to change the service configuration in ~/Startup.cs to include the following:

    public void ConfigureServices(IServiceCollection services)
    {
        //Initialize XPODataLayer / Database
        services
            .AddXpoDatabase("DefaultConnection", Configuration.GetConnectionString("DefaultConnection"));
        //Initialize identity to use XPO
        services
            .AddIdentity<ApplicationUser, ApplicationRole>(options => {
                options.Lockout.AllowedForNewUsers = true;
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                options.Lockout.MaxFailedAccessAttempts = 3;
            })
            .AddXpoIdentityStores<XpoApplicationUser, XpoApplicationRole>()                
            .AddDefaultTokenProviders();
    
        // Add other application services.
        services.AddTransient<IEmailSender, EmailSender>();
        services.AddMvc();
    }
      

    With these changes in place, we're ready to boot up our app running on XPO!

    Did I mention that the source of this project is available on GitHub? Feel free to clone this repo and make a pull request in case you encounter an issue.

  • Impressions of BASTA! Spring 2018 Frankfurt

    image

    Last week, John and I put up our awesome booth in the Marriot Hotel and Conference center in the city center of Frankfurt, Germany.

    We had lots of talks with prospects and existing customers. I did quite a number of demos on DevExtreme with Angular as well as our WPF controls. A couple of customers where really pleased to see the fastest DataGrid for WinForms with our new DirectX rendering engine.

    Oliver joined us as well but was also quite busy during the Conference with a full day workshop and several well attended sessions. I did a session about Dependency Injection on ASP.NET Core v2.

    Besides our daily raffles, I had the honor of pulling an attendee’s card out of the bowl of the BASTA main raffle to receive our Universal Subscription.

    Below is an impression of BASTA! Spring 2018:

    2018-02-20 12.10.442018-02-20 10.00.322018-02-20 13.58.232018-02-20 14.01.252018-02-22 14.02.47IMG_2682IMG_2687IMG_2686IMG_26902018-02-20 13.58.092018-02-20 14.02.352018-02-20 10.07.39
  • DevExpress @ NDC London this week

    2018 is kicking off in Europe with the NDC London conference. DevExpress is proud to be a partner of it.




    As always, NDC has a top notch line-up with speakers like Scott Guthrie, Steve Sanderson, Scott Hanselman and about 90 more!

    John and I will be available at our booth to show you all the awesome features in our v17.2 release and we'll be handing out some cool goodies.
    Make sure to come by and say hi!

    See you at NDC!

  • XPO for .NET Core 2 Webinar on Channel 9 @ Microsoft

    I got some questions last week about where to find the webinar video "Object-Relational Mapping for .NET Core with DevExpress XPO" I did as kick-off on our v17.2 release webinar series. Well, the reason it took some time is because we prepared it to be hosted on the Microsoft Channel 9 site.

    To show how cross-platform XPO and .NET Core 2 are, I decided to use a Linux Mint environment in the first part, Windows 10 in the second part and Mac OS in the third (Xamarin) part.

    I didn't even mention in the webinar that I was running my MS-SQL Server instance in a Docker container on my Asustor NAS Device (which is Linux based), so it was cross-platform all the way!

    Webinar outline

    After the general introduction on Object Relational Mapping, the coding part took of. I started with a small console application to show the absolute basics of XPO.

    Connecting to the datastore

    The first step is to connect to a datastore which is done throught the DataLayer. XPO has several difference DataLayer implementations for different kinds of applications.
    In the console application, we can use the simple DataLayer and initialize it like this:

       XpoDefault.DataLayer = XpoDefault.GetDataLayer(
          SQLiteConnectionProvider.GetConnectionString("console.db"),
          AutoCreateOption.DatabaseAndSchema);
      

    In the Web API example, there was some more code involved for initializing the DataLayer. Because we're dealing with a multi-threaded web application, we want to initialize a singleton instance of a ThreadSafeDatalayer. Besides that, we also want to setup a database connection pool to make the app as performant as possible:

       string pooledConnectionString = XpoDefault.GetConnectionPoolString(connectionString);
          var dataStore = XpoDefault.GetConnectionProvider(pooledConnectionString,
                                AutoCreateOption.SchemaAlreadyExists);
          var dataLayer = new ThreadSafeDataLayer(dictionary, dataStore); ;
          return dataLayer;
      

    With some extension methods, I'm using the .NET Core dependency injection to create that singleton instance, and I inject a UnitOfWork into any Web API Controller which has a constructor with a parameter of type UnitOfWork:

       public static class CustomXpoExtensions {
          public static IServiceCollection AddXpoPooledDataLayer(this IServiceCollection serviceCollection, string connectionString) {
             return serviceCollection.AddSingleton(XpoHelper.CreatePooledDataLayer(connectionString));
          }
          public static IServiceCollection AddXpoDefaultUnitOfWork(this IServiceCollection serviceCollection) {
             return serviceCollection.AddScoped((sp) => new UnitOfWork());
          }
          public static IServiceCollection AddXpoUnitOfWork(this IServiceCollection serviceCollection) {
             return serviceCollection.AddScoped((sp) => new UnitOfWork(sp.GetService()));
          }
          public static IApplicationBuilder UseXpoDemoData(this IApplicationBuilder app) {
             using(var scope = app.ApplicationServices.GetService().CreateScope()) {
                XpoHelper.CreateDemoData(() => scope.ServiceProvider.GetService());
             }
             return app;
          }
       }
      

    We can use these extension methods in the Startup.cs like this:

       public void ConfigureServices(IServiceCollection services)
       {
          services
             .AddXpoPooledDataLayer(MSSqlConnectionProvider.GetConnectionString("sql-server-ip", "user", "test123", "WebAppDemo"))
             .AddXpoUnitOfWork()
             .AddMvc()
             .AddJsonOptions(options =>
             {
                 // use the custom resolver (above)
                 options.SerializerSettings.ContractResolver = new XpoContractResolver();
                 // don't kill yourself over loop properties (probably not really needed now, I
                 // inserted this originally to work around the This property on the XPO types)
                 options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
              });
       }
      

    The AddJsonOptions in the above code adds a custom Json serializer to the App to make sure certain fields of our XPO entities are not being serialized to Json. This fields are part of the infrastructure or XPO.

    For the Xamarin example, I used again a ThreadSafe Datalayer because a Xamarin app will be multi-threaded as well.

    Defining entities

    In the examples, I used an Order type together with the OrderItem type. This gave me the opportunity to show how to setup a Master-Detail relation ship as well.

    The first thing you see is that I derived my entities from the XPO based type XPObject.

       public class Order : XPObject
       {
          public Order(Session session) : base(session) { }
    
          //... code omitted
       }
      

    The reason for using this base type is that several nice features of XPO will be made available then like "Change Notifications". This behaviour comes in quite handy when doing some caching to get the most out of your application.

    Please note that the XPObject requires you to supply a Session (or UnitOfWork) to its constructor. This is necessary to let the infrastructure of XPO dot its thing with the object being instantiated.

    In case you have an existing class hierarchy that you want to persist to a database and you don't want to use constructor and Sessions etc. in there, you can also work with plain simple objects (POCO) which is similar as with Entity Framework.

    An example of this is the Xamarin demo which works with the POCO objects created by the project template.

        public class Item
        {
            public string Id { get; set; }
            public string Text { get; set; }
            public string Description { get; set; }
        }
    
        using (var uow = XpoHelper.CreateUnitOfWork())
        {
           item.Id = Guid.NewGuid().ToString();
           // I'm using the Save(..) method which receives the POCO we want to persist
           uow.Save(item);
           uow.CommitChanges();
        }
      

    Another interesting thing is the implementation of properties:

       public class Order : XPObject
       {
          //... code omitted 
    
          public Order(Session session) : base(session) { }
          private DateTime _OrderDate;
          public DateTime OrderDate
          {
             get { return _OrderDate; }
             set { SetPropertyValue("OrderDate", ref _OrderDate, value); }
          }
       }
      

    This SetPropertyValue(..) method is one of the methods made available through the XPObject and deals with the Change Notifications.

    Defining relationships between entities is pretty straight forward. In a master-detail relationship, the master entity (Order) has a collection of detail entities (OrderItem), while the detail entity holds a single reference to its master entity like shown below:

       public class Order : XPObject
       {
          public Order(Session session) : base(session) { }
    
          //... code omitted 
    
          [Association("Order-OrderItems"), Aggregated]
          public XPCollection<OrderItem> OrderItems
          {
             get { return GetCollection<OrderItem>("OrderItems"); }
          }
       }
    
       public class OrderItem : XPObject
       {
          public OrderItem(Session session) : base(session) { }
    
          //... code omitted 
    
          private Order _Order;
          [Association("Order-OrderItems")]
          public Order Order
          {
             get { return _Order; }
             set { SetPropertyValue("Order", ref _Order, value); }
          }
       }
      

    Also note the Association and Aggregated attributes. They tell XPO what kind of relation we are trying to setup.

    Another powerful feature that comes with XPO is the use of the PersistentAlias attribute. With this you can specify that certain (readonly) fields need to be determined/evaluated by the database system being used.

    I used 2 examples in the project, one which calculated the total price per order item, which is Qty * UnitPrice:

        public class OrderItem : XPObject
        {
            //... code omitted 
    
            public int Qty {
               //... code omitted
            }
            public decimal UnitPrice{
               //... code omitted
            }
    
            [PersistentAlias("Qty * UnitPrice")]
            public decimal Price
            {
                get { return Convert.ToDecimal(EvaluateAlias("Price")); }
            }
        }
      

    The second one is doing an aggregated calculation as part of the Order entity which sums the Alias from the detail set OrderItems:

        public class Order : XPObject
        {
    
            //... code omitted 
    
            [Association("Order-OrderItems"), Aggregated]
            public XPCollection OrderItems
            {
                get { return GetCollection("OrderItems"); }
            }
            [PersistentAlias("OrderItems.Sum(Price)")]
            public decimal TotalAmount
            {
                get { return Convert.ToDecimal(EvaluateAlias("TotalAmount")); }
            }
        }
      

    Please not that the syntax being used is part of our database agnostic criteria language. More about it can be found here.

    Querying the datastore

    There are several ways of querying the datastore. In the examples I've used the Linq querying mechanism. This query is initiated through the Session.Query<T>() method:

       using (var uow = new UnitOfWork())
       {
          var orders = from o in uow.Query<Order>()
                where o.OrderDate < DateTime.Now.AddDays(-10)
                orderby o.OrderDate
                select o;
    
          foreach (var o in orders) {
             Console.WriteLine($"Order #{o.OrderNo} / {o.OrderDate}, client {o.Client}, Total Amount { o.TotalAmount }");
             foreach(var i in o.OrderItems) {
                Console.WriteLine($"   {i.Qty} x {i.Description} ({i.UnitPrice}) = {i.Price}");
             }
          }
       }
      

    XPO also supports another query mechanism by using the CriteriaOperator classes. This are super-powerful and they also allow you to select programmatically. This tends to be a problematic with Linq.
    XPO CriteriaOperators allow you to create a very complex filtering clause which will be transformed to the SQL WHERE clause when executed.
    I could rewrite the above code fragement like this:

       using (var uow = new UnitOfWork())
       {
          XPCollection<Order> orders = new XPCollection<Order>(uow,
                new BinaryOperator("OrderDate", DateTime.Now.AddDays(-10), BinaryOperatorType.Less),
                new SortProperty("OrderDate", SortingDirection.Ascending));
       }
    

    Or by using the CriteriaOperator.Parse(...) construct:
    I could rewrite the above code fragement like this:

       using (var uow = new UnitOfWork())
       {
          XPCollection<Order> orders = new XPCollection<Order>(uow,
                CriteriaOperator.Parse("OrderDate < ?", DateTime.Now.AddDays(-10)),
                new SortProperty("OrderDate", SortingDirection.Ascending));
       }
    

    Do note the '?' which is used as a parameter indicator in the query language.

    More on the XPO Criteria Language can be found here.

    This Webinar was only short introduction since XPO involves much more. What to think about inheritance, interface support, a whole set of Attributes to decorate your classes and properties with, support for Views and StoredProcedures.

    I even didn't mention our Visual Designer which comes with VisualStudio as well as our XPO Profiler tool to analyze which SQL is being fired against the database:

    If you want to play around with the samples I did in the webinar, check out my Github repo at: https://github.com/donwibier/DXXPOCore.

    Do note that if you clone the repo, you will need to have your personal DevExpress NuGet feed configured like instructed here.

    Let me know if you want to know more!

  • DevExpress Meetups: Impressions from Munich

    John and I also organized our third meetup at the Wirthaus am Bavariapark in Munich, Germany. Again, we had a full house with customers for all of our product lines and we managed to talk with all. I kicked off with some small welcome words and highlighted a number of features of our v17.2 release.

    It was fun to see the first snow of the season come down on Munich during the day so when we started, it looked quite white outside.

    Below is an impression of the meetup:

     

    And again, if Munich isn't in your neighborhood and you want to meet us? Let me know your location. We are looking to organize some more meetups in the beginning of next year.

  • DevExpress Meetups: Impressions from Frankfurt

    Last week, John and I organized another meetup in Frankfurt Germany.  We hosted this one in the Ambassador Club on the 7th floor of Fleming’s Selection Hotel in Frankfurt-City. Almost everybody who registered showed up and we talked about virtually all the product lines we have.

    Below is an impression of the Frankfurt Meetup:

     
     
     
     

    We are looking to organize some more meetups. Let me know which location would be convenient for you!

  • DevExpress Meetups: Impressions from Utrecht

    Last week, John and I organised the first of a series of meetups. This one was in The Colour Kitchen Oudegracht in Utrecht, The Netherlands.

    There was a lot of interest and we had a full Colour Kitchen. I started with a short welcome presentation where I talked about some of the v17.2 release highlights. After that, we had some interesting conversations about pretty much all of our tools and products.

    Below is an impression of the meetup

     
     

    We hope to see you on one of the future meetups!

  • Germany Meetups: November 29th Frankfurt, November 30th Munich

    After our initial kick-off meetup in Utrecht, The Netherlands on November 27th, John and I will be in Frankfurt at the 29th of November.

    Frankfurt Meetup

    We will be hosting our second meetup in the Ambassador Club on the 7th floor of Fleming’s Selection Hotel at the Eschenheimer Tor 2 in Frankfurt-City.

    You will be informed on the latest features of our v17.2 release, and you’ll be able to discuss them with me or other DevExpress customers. The drinks and snacks are on us and we’re looking forward meeting you.

    If you want to join this meetup, make sure to register you seat by clicking here.

    image
    image

    Munich Meetup

    On November 30, John and I will be in Munich for our 3rd Meetup. We’ll host this one in the Augustiner Keller und Biergarten, and while we go over the awesome new features of v17.2, you’ll be paying attention while having a beer and some snacks.

    I’d love to talk with all of you and hear your comments and feedback, and maybe even some suggestions for our next major release.

    If you want to join this meetup, make sure to register you seat by clicking here.

    If you can't make it to any of these locations, let me know so we can investigate possible other locations for additional meetups.

    I hope to see you in Frankfurt or Munich!

  • DevExtreme: New TypeScript declarations and improved Angular typings (v17.2)

    One of the things we support from the very first release of DevExtreme is TypeScript.

    new-TypeScript-declarations-typings-for-angular-blog

    As many of you know, TypeScript and its tooling allow us to use compile-time type checks and it offers support for classes and interfaces. Because of these features, one is able to build large scale JavaScript applications. In fact Google is using TypeScript for developing Angular.

    With our v17.2 release we have improved our TypeScript declarations for the DevExtreme widgets API as well as the Angular integration layer.

    What does this mean?

    In the previous releases, most of the classes in the DevExtreme API had weakly typed properties (of type ‘any’). This means that compile time type checking always resulted in a successful build even if you try to bind a string value to a property which needs a number.

    With v17.2 we have made the DevExtreme API strongly typed which results in improved code editor hints:

    wrongtype

    One of the cool features of TypeScript is the use of union types (‘|’ sign). This allows us to specify a number of types to be used like shown in the following table:

      v17.1 v17.2
    dxDateBoxOptions value? : any value? : Date | number | string
    All widgets dataSource? : any dataSource? : Array | DataSource | DataSourceOptions | string;

    To make the DevExtreme API more fluent, we’ve also introduced the use of ‘type’ declarations like:

        
         export type format = string | ((value: number | Date) => string) | {        
            type?: string,
            precision?: number,
            currency?: string,
            formatter?: ((value: number | Date) => string),
            parser?: ((value: string) => number | Date)
         };
         //...
         // in dxDateBoxOptions
         displayFormat?: format;
    
      
     

    While we were working on these developer improvements, we worked on the IntelliSense support as well. All the information you normally look up on our documentation site will now show up in the Quick Info tooltip and it is used in the code-completion to increase your productivity.

    uniontype

    What about Angular?

    Because Angular is written in TypeScript, all these developer sweetness works for Angular as well!

    We have strongly typed the Angular Component properties which results in less runtime errors because the build process of an Angular application will do the type-checking for us.

    data-image-png;base…

    And while coding, we are now able to give you short descriptions through the Quick Info panel in IntelliSense as well.

    desc_light

    Try it now?

    So ready for a test-drive?

    These new features are included in the v17.2 pre-beta that is available through npm right now. Please note that this pre-beta may contain some bugs and is not intended to be used in production: 

    npm install devextreme@17.2.1-pre-beta

    Learn more about DevExtreme's pre-releases in this blog post.

    Like it?

    Let me know by replying to this post if you like the features outlined in this post.

1
2 3 4 5 6 7 8 9
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, Delphi, HTML5 or iOS & Android development. 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-2018 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners