Blogs

XPO

eXpress Persistent Objects
  • LINQPad is XPO’s new best friend!

         

    LINQ is .NET Language-Integrated Query. It's included in .NET Framework 3.5 and you can use it in Visual Studio 2008 projects. XPO has officially supported LINQ since v7.3 and since then we have continued to improve and support it. Nowadays LINQ to XPO is very mature, extendable (How to: Implement Custom Functions and Criteria in LINQ to XPO) and powerful (Free Joins). In the latest version we make it even easier to use LINQ to XPO since we only distribute one assembly, DevExpress.XPO.dll, which includes everything! (custom providers as well).

    XPO can talk transparently to a large list of database systems. It was already possible to make complex queries using our standard criteria syntax, however using LINQ offers some great advantages.

    1. No magic strings, like you have in standard criteria syntax,
    2. Intellisense support making it easier and faster to construct your queries,
    3. Compile time checking,
    4. Learning to write LINQ queries/lambdas is a must learn for .NET developers,

    The benefits are clear, however there is a caveat when using LINQ. It is not possible to use Edit and Continue and with even the smallest change you need to restart your debugging session. As a result your development speed is decreased dramatically.

    The solution as always is to use the right tool and in this case the right tool is LINQPad. It provides a flexible UI allowing you to use LINQ in various ways.

    In our latest version our team released an XPO context driver for LINQPad. Let’s see how to configure it.

    Firstly we need to install the driver found at C:\DevExpress 2011.2\Components\Tools\DXperience\XPOContextDriver.lpx.

    image

    Secondly we need to setup a new connection.

    image

    In this example we are going to use the MainDemo assembly containing the business objects.

    image

    LINQPad populated the domain in a hierarchical treelist. This allows us to enjoy using drag & drop plus intellisence in its expression editor to form the LINQ queries. Furthermore it is possible to use your style of language as demonstrated below.

    image

    The cool part is that LINQPad, written by the great Joe Albahari, is available for free. It is strongly recommended as a great way to learn LINQ.

    Now for the fun part, let’s see it in action. Imagine we have the following code in our VS.

    var queryable = from c in contacts

                    let tasks = c.Tasks.Where(t => t.ActualWork < 0)

                    where tasks.Any()

                    orderby c.LastName

                    select new {

                                   c.LastName,

                                   Orders = tasks.Count(),

                               };

     

    We wish to see the results of queryable variable ,the best way to achieve this is to set a breakpoint then hit F5. Next we wait for our app to load and navigate to the action that will hit the breakpoint. Finally we must select the queryable variable and hit Shift+F9 to invoke the debugger window and examine the result set. Now we can try to make our requirements more complex. Lets say we also want to examine the result set when ActualWork>10. The solution is really easy when using LINQPad, we simply need to copy paste the code from VS to LINQPad the expression editor like this,

    image

    After this we can quickly and easily form queries as we wish and examine their result sets instantly. Using the excellent and cheap LINQPad Autocompletion it is possible to use VS like intelligence to further speed up your development.

    image

    PS: eXpandFrameWork contributors are already using the Autocompletion feature since LINQPad offered a license for its contributors.

    We would appreciate your feedback on this post. Has it been useful to you? Feel free to contact us with any further questions

  • XPO - 11.2 Sneak Peek – XPCollection performance improvements (once again!)

         

    Prerequisites

    As you probably know, when inserting or removing records from collections supporting IBindingList, it is necessary to load the entire collection (that means constructing its persistent objects and their related objects graphs) from the database. It is an understandable requirement (we need to fire the IBindingList.ListChanged event with correct indices), but it might drive to some XPO customers crazy. For example, if we inserted or removed an object from an associated details collection containing a lot of complex objects, it might become a performance problem.

    Though there has been an unofficial solution (based on the use of IList collections instead of collections of XPCollection type) available for many years, it was not final and had some drawbacks (the need to redesign persistent classes, inability to access Criteria and other features of the XPCollection, data binding limitations, etc.). Finally, improving the current state of affairs has also been one of the most popular requests our customers asked  us for in recent years.

    One-to-many associations are significantly faster in 11.2

    Our developers have managed to work around the limitation described above in version 11.2, and trust me, it is something you will notice immediately. Generally, XPCollection will perform significantly faster on inserting and deleting objects into and from a large associated 1-M details collection. Certainly, the exact speed improvement will depend on the collection size, the complexity of your objects graphs and other factors. Note that these changes do not affect M-M details collections at this time, because this is a more difficult case. We will research it the future.

    To test it in action, I used a modified version of the test application (you can download it here) from my previous blog. See my results below:

    11.1.7

    11.2.1

    image image
    image image

    As you see, it is ~100x faster on insert and ~10x faster on removal when the details collection was comprised of 10000 objects. These improvements will be more noticeable, the greater the size of the details collection.

    Do you like the XPO improvements? Please let us know your opinion!

    Happy XPOing!

    Winking smile
  • Performance boost on removing data from a large XPCollection (available in 11.1.7)

         

    I am happy to announce that XPO developers have tweaked XPCollection in version 11.1.7, and trust me, it is something you will notice immediately. This improvement was accomplished while working on a customer's issue and that again proves how customer feedback is invaluable to us.

    Generally, XPCollection will perform several times faster on removing objects from large associated 1-M or M-M details collections. Certainly, the exact speed improvement will depend on the collection size, the complexity of your object graphs and other factors. Also, you will be interested to know that we achieved this by optimizing the removal algorithm that sets a reference to a master object to null (you can improve your familiarity with associations in XPO by reading this KB Article).

    To demonstrate these improvements in action, I built a small console application with a couple of persistent classes, with a 1-M relationship between them. My test applications first created several master objects (5 and 10) with several thousand details (5000 and 10000) in each, committed them all to the Microsoft SQL Server database and then ran another loop to delete each master and its details from the database. Time was measured for the remove operation performed for each master object. Test applications were built in the Release configuration (+ Any CPU) against .NET 4.0 and the 10.2.9 and 11.1.7 versions of XPO. You can download test applications as well as screenshots showing the results using the links below:

    During tests, most noticeable (~3-4 times faster) improvements occurred when the details collection was comprised of 10000 objects:

    10.2.9

    10.2.9_thumb[4]

    11.1.7

    11.1.7

    It was ~2x faster on 5000 details. I expect improvements will be more noticeable (maybe dozen and hundreds times faster) the greater the size of the details collection.

    The good news is that more performance improvements are coming in v2011 vol2! I am preparing another blog post about this, and hope to publish it this week. Stay tuned, and please let us know how much better your real XPO applications perform in 11.1.7. Thank you in advance!

    Happy XPOing!Winking smile

  • Profiling medium trust web applications

         

    Our profiler uses WCF in order to communicate with the client application. One of the benefits of the XPOProfiler is that it supports 2 binding types. Both binding types have their advantages however in this situation only one allows us the flexibility we require.

    NetTcpBinding

    This option provides a secure and reliable binding environment for .Net to .Net cross machine communication. By default it creates a communication stack using WS-ReliableMessaging protocol, TCP for message delivery and windows security for message and authentication at run time.

    image

    One drawback is that in shared host environments there is no way to open a port neither to use Mutexes. Our second option allows us to overcome this by creating a WCF service and hosting it with our web application. 

    WSHttpBinding

    • Defines a secure, reliable, interoperable binding suitable for non-duplex service contracts,
    • Supports WS-* functionality and distributed transactions with reliable and secure sessions using SOAP security,
    • Uses HTTP and HTTPS transport for communication,
    • Reliable sessions are disabled by default

    image

    We have already identified limitations when profiling medium trust web applications, now we must look at modifying our application in order to overcome them.

    The first step is to add the new service using VS add new Item context menu.

    image

    This action will add the following items to the project;

    • IMyLogService.cs - the communication interface description,
    • MyLogService.svc.cs - the communication service class declaration

    Furthermore the Web.config file will be modified by adding <system.serviceModel> section. I am using .NET3.5 right now, be aware that this step may vary according to the .NET version you are using.

    The next step is to remove the IMyLogService.cs from the project.

    image

    Then we are going to modify the MyLogService.svc.cs as  follows;

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class MyLogService : LogService {
        static LoggerBase logger = new LoggerBase(50000);
        static MyLogService() {
            LogManager.SetTransport(logger);
        }
        public MyLogService()
            : base(logger) {        
        }
    }

    The final step is to change the name of the communication contract in <system.serviceModel> section of Web.config file from WebApplicationToProfile.IMyLogService to DevExpress.Xpo.Logger.Transport.ILogSource.

    image

    After that we are ready to run the XPOProfiler and create a new connection to our medium hosted XPO web application.

    image

    Note: Version 11.2 will allow us to carry out all the above processes.

    Happy profiling!

    Related Links
    Blog posts
    Videos

  • Webinar: Profiling a XAF application with XPO Profiler

         

    XPO Profiler, although still in beta can be a great tool when there are performance problems. Today we are going to do an extensive discussion on it. Moreover we are going to see how to deal with the following scenario;

    Our Customer has a XAF application in production using an Access database. He complained that there is a delay when navigating through his customers at detail view. However we cannot reproduce the delay in our environment. Probably because we do not have access to his data.

    Note: If we had access to his data, things may have been easier for us. We could then use the Remote profiling mode. However I am sure we can find the solution to this problem in local mode as well.

    Come and join us!

    You can watch the webinar here

    Related Links
    Blog posts

  • XPO–11.2 Sneak Peek - Auto-generated Int64 keys

         

    We are not far from 11.1 and I presume that you will be surprised to see 11.2 sneak peeks at this timeSmile

    There is not actually not much to say here, because the subject says it all. Starting from version 11.2 you can define a Int64 (long) key property in your class and mark it with the KeyAttribute, whose AutoGenerate parameter will be set to True:

      1: [NonPersistent, MemberDesignTimeVisibility(false)]
    
      2: public abstract class XPObjectWithInt64Key : XPCustomObject {
    
      3:     ...
    
      4:     Int64 _oid = -1;
    
      5:     [Persistent("OID"), Key(AutoGenerate = true)]
    
      6:     public Int64 Oid {
    
      7:         get { return _oid; }
    
      8:         set {
    
      9:             Int64 oldValue = Oid;
    
     10:             if (oldValue == value) return;
    
     11:             _oid = value;
    
     12:             OnChanged("Oid", oldValue, value);
    
     13:         }
    
     14:     }
    
     15:     ...
    
     16: }
    
     17: public class TestPerson : XPObjectWithInt64Key {
    
     18:     public TestPerson(Session session)
    
     19:         : base(session) { }
    
     20:     private string firstName;
    
     21:     public string FirstName {
    
     22:         get { return firstName; }
    
     23:         set { SetPropertyValue("FirstName", ref firstName, value); }
    
     24:     }
    
     25:     private string lastName;
    
     26:     public string LastName {
    
     27:         get { return lastName; }
    
     28:         set { SetPropertyValue("LastName", ref lastName, value); }
    
     29:     }
    
     30: }

    Interested readers will notice that for my base persistent class I grabbed the source code from the standard XPObject class and then just changed its key type from Int32 to Int64.

    Well, if you now create and save a new object, a key will be automatically generated for it:

    screenshot

    I hope you like this small addition to XPO, because it provides you with more flexibility (previously auto generation was supported only for Int32 and Guid keys).

    Please let us know your thoughts on this.

    Happy XPOingWinking smile

  • XPO – 11.1 Sneak Peek - WCF services for IObjectLayer

         

    In the two previous blog posts, we already described how to provide the IDataStore and ICachedDataStore implementations, working via WCF Services. In this blog post, we will talk about implementing a distributed object layer service (IObjectLayer/ISerializableObjectLayer), working via WCF. Creating a WCF service for ISerializableObjectLayer is a bit more complex than for IDataStore and ICachedDataStore, but it is still very similar.

    First, we will create a new Class Library project and add a Customer class via the Persistent Object item template to it (you can see the source code of this class in our first blog). Then, we will create a new WCF Service Application project and remove files with auto-generated interfaces for the service. Since our service needs to know about persistent classes, we will add a project reference to our class library and modify our service class as follows:

      1: using DevExpress.Xpo;
    
      2: using DevExpress.Xpo.DB;
    
      3: using DevExpress.Xpo.Metadata;
    
      4: using ClassLibrary1;
    
      5: ...
    
      6: public class Service1: SerializableObjectLayerService {
    
      7:     public Service1()
    
      8:         :base(new ObjectServiceProxy()){
    
      9:     } 
    
     10:     static Service1() {
    
     11:         string connectionString = MSSqlConnectionProvider.GetConnectionString("localhost", "ServiceDB");
    
     12:         IDataStore dataStore = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.DatabaseAndSchema);
    
     13:         XPDictionary dictionary = new ReflectionDictionary();
    
     14:         dictionary.CollectClassInfos(typeof(Customer).Assembly);
    
     15:         XpoDefault.DataLayer = new ThreadSafeDataLayer(dictionary, dataStore);
    
     16:         XpoDefault.Session = null; 
    
     17:     }
    
     18: }
    
     19: public class ObjectServiceProxy : SerializableObjectLayerProxyBase {
    
     20:     protected override SerializableObjectLayer GetObjectLayer() {
    
     21:         return new SerializableObjectLayer(new UnitOfWork(), true);
    
     22:     }
    
     23: }
    
     24: 

    To finish with the service, we will need to change some binding properties in its Web.config file:

      1: <system.serviceModel>
    
      2:  <services>
    
      3:    <service name="WcfService2.Service1" behaviorConfiguration="WcfService2.Service1Behavior">
    
      4:      <!-- Service Endpoints -->
    
      5:      <endpoint address="" binding="basicHttpBinding"
    
      6: contract="DevExpress.Xpo.DB.ISerializableObjectLayerService">
    
      7:        <identity>
    
      8:          <dns value="localhost"/>
    
      9:        </identity>
    
     10:      </endpoint>
    
     11:    </service>
    
     12:  </services>
    
     13:  <behaviors>
    
     14:    <serviceBehaviors>
    
     15:      <behavior name="WcfService2.Service1Behavior">
    
     16:        <serviceMetadata httpGetEnabled="true"/>
    
     17:        <serviceDebug includeExceptionDetailInFaults="false"/>
    
     18:      </behavior>
    
     19:    </serviceBehaviors>
    
     20:  </behaviors>
    
     21: </system.serviceModel>
    
     22: 

    At this time, our service is ready to use and all that we need to do is to write the client part.

    For the client, we will add a Console Application into our existing solution and add a project reference to the class library with our Customer persistent class. The Main() method should also be modified as follows:

      1: using DevExpress.Xpo;
    
      2: using DevExpress.Xpo.DB;
    
      3: using DevExpress.Xpo.Metadata;
    
      4: using ClassLibrary1;
    
      5: ...
    
      6: namespace ObjectWcfClient {
    
      7:     class Program {
    
      8:         static IObjectLayer ObjectLayer;
    
      9:         static void Main(string[] args) {
    
     10:             ISerializableObjectLayer serializableObjectLayer =  
    
     11:                 new SerializableObjectLayerServiceClient("BasicHttpBinding_ObjectLayer");
    
     12:             serializableObjectLayer.CanLoadCollectionObjects.ToString(); //Check connection
    
     13:             XpoDefault.DataLayer = null;
    
     14:             XpoDefault.Session = null;
    
     15:             ObjectLayer = new SerializableObjectLayerClient(serializableObjectLayer);
    
     16:  
    
     17:             using(UnitOfWork uow = new UnitOfWork(ObjectLayer)) {
    
     18:                 using(XPCollection<Customer> customers = new XPCollection<Customer>(uow)){
    
     19:                     foreach(Customer customer in customers) {
    
     20:                         Console.WriteLine("Company Name = {0}; ContactName = {1}", 
    
     21:                             customer.CompanyName, customer.ContactName);
    
     22:                     }
    
     23:                 }
    
     24:             } 
    
     25:             Console.WriteLine("Press any key...");
    
     26:             Console.ReadKey();
    
     27:         }
    
     28:     }
    
     29: }
    
     30: 

    The final step will be adding the App.config file into the client project and modifying it as follows:

      1: <?xml version="1.0" encoding="utf-8" ?>
    
      2: <configuration>
    
      3:   <system.serviceModel>
    
      4:     <bindings>
    
      5:       <basicHttpBinding>
    
      6:         <binding name="BasicHttpBinding_ObjectLayer" maxBufferSize="2147483647"
    
      7:             maxReceivedMessageSize="2147483647">
    
      8:           <security mode="None" />
    
      9:         </binding>
    
     10:       </basicHttpBinding>
    
     11:     </bindings>
    
     12:     <client>
    
     13:       <endpoint address="http://localhost:64466/Service1.svc" binding="basicHttpBinding"
    
     14:           bindingConfiguration="BasicHttpBinding_ObjectLayer"
    
     15:           contract="DevExpress.Xpo.DB.ISerializableObjectLayerService" name="BasicHttpBinding_ObjectLayer" />
    
     16:     </client>
    
     17:   </system.serviceModel> 
    
     18: </configuration>
    
     19: 

    Now if we run the service and client parts, we will see the following output:

    Result
    As you can see, our client connects to the service and loads serialized persistent objects from it, and not just plain statements (insert, update, delete, select). You can learn more on which capabilities it opens for XPO customers at the end of our introductory blog for the object layer.

    Happy XPOing!Winking smile

  • XPO – 11.1 Sneak Peek – IDataStore WCF Service in Silverlight

         

    In this blog post we will talk about peculiarities of using IDataStore WCF services in Silverlight projects. As you guess, the main restriction is that all inquiries should be performed asynchronously or rather should not block the UI thread.

    Let’s extend the solution from the WCF services for IDataStore blog by adding a new Silverlight Application project to it. Then, we will add references to DevExpress.Data and DevExpress.Xpo assemblies and an existing  Persistent.cs file with the Customer class definition.

    Next, we will drop the GridConrtol (gridControl1) component onto the main page and modify the MainPage.xaml file as shown below:

      1: <UserControl x:Class="SilverlightApplication1.MainPage"
    
      2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    
      3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    
      4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    
      5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    
      6:     mc:Ignorable="d"
    
      7:     d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    
      8:              xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid">    
    
      9:     <Grid x:Name="LayoutRoot" Background="White">
    
     10:         <dxg:GridControl x:Name="gridControl1" AutoPopulateColumns="True">            
    
     11:         </dxg:GridControl>
    
     12:     </Grid>
    
     13: </UserControl>
    
     14: 

    Finally, we will modify the MainPage class code in the following way:

      1: using DevExpress.Xpo;
    
      2: using DevExpress.Xpo.DB;
    
      3: ...
    
      4: namespace SilverlightApplication1 {
    
      5:     public partial class MainPage : UserControl {
    
      6:         Session session = null;
    
      7:         public MainPage() {
    
      8:             InitializeComponent();
    
      9:             //Initialize connection settings in a separate thread.
    
     10:             ThreadPool.QueueUserWorkItem(o => {
    
     11:                 //Create connection to our WCF Service.
    
     12:                 XpoDefault.DataLayer = XpoDefault.GetDataLayer(
    
     13:                     "http://localhost:64466/Service.svc",
    
     14:                     AutoCreateOption.SchemaAlreadyExists
    
     15:                 );
    
     16:                 session = new Session();
    
     17:                 //It is necessary to call UpdataSchema method for all persistent classes.
    
     18:                 session.UpdateSchema(typeof(Customer));
    session.TypesManager.EnsureIsTypedObjectValid();
     19:                 Dispatcher.BeginInvoke(BeginInitializeDataSource);
    
     20:             });
    
     21:         }
    
     22:         void BeginInitializeDataSource() {
    
     23:             var query = from c in session.Query<Customer>()
    
     24:                         where c.Country.Length > 2
    
     25:                         select c;
    
     26:             //Execute the query asynchronously.
    
     27:             query.EnumerateAsync(EndInitializeDataSource);
    
     28:         }
    
     29:         void EndInitializeDataSource(IEnumerable<Customer> result, Exception ex) {
    
     30:             //Assign the data source to the control.
    
     31:             gridControl1.DataSource = result;
    
     32:         }
    
     33:     }
    
     34: }
    
     35: 

    If we run the application, we will see the following results:

    Result

    Happy XPOing in Silverlight!Winking smile

  • XPO – 11.1 Sneak Peek – Data binding in WPF

         

    In this blog post, we are going to demonstrate how to use a new XPObservableAssociationList<T> class in the DevExpress.Xpo.Linq library for data binding to persistent classes in WPF.

    First, we will create a new WPF Application project. Then, we will add references to the DevExpress.Data, DevExpress.Xpo and DevExpress.Xpo.Linq assemblies. Then, we will add a base persistent BaseObject and a couple of persistent classes Person and Order as follows:

      1: using DevExpress.Xpo;
    
      2: using DevExpress.Xpo.DB;
    
      3: using DevExpress.Xpo.Helpers;
    
      4: …
    
      5: [NonPersistent]
    
      6: public class BaseObject : PersistentBase {
    
      7:     int oid;
    
      8:     [Key(true)]
    
      9:     public int Oid {
    
     10:         get { return oid; }
    
     11:         set { SetPropertyValue<int>("Oid", ref oid, value); }
    
     12:     }
    
     13:     protected override IList<T> CreateAssociationList<T>(DevExpress.Xpo.Metadata.XPMemberInfo property) {
    
     14:         return new XPObservableAssociationList<T>(Session, this, property);
    
     15:     }
    
     16:     public BaseObject(Session session)
    
     17:         : base(session) {
    
     18:     }
    
     19: }
    
     20: public class Person : BaseObject {
    
     21:     string firstName;
    
     22:     public string FirstName {
    
     23:         get { return firstName; }
    
     24:         set { SetPropertyValue<string>("FirstName", ref firstName, value); }
    
     25:     }
    
     26:     string lastName;
    
     27:     public string LastName {
    
     28:         get { return lastName; }
    
     29:         set { SetPropertyValue<string>("LastName", ref lastName, value); }
    
     30:     }
    
     31:     int age;
    
     32:     public int Age {
    
     33:         get { return age; }
    
     34:         set { SetPropertyValue<int>("Age", ref age, value); }
    
     35:     }
    
     36:     [Association("Person-Orders")]
    
     37:     public IList<Order> Orders {
    
     38:         get { return GetList<Order>("Orders"); }
    
     39:     }
    
     40:     public Person(Session sesson) : base(sesson) { }
    
     41: }
    
     42: public class Order : BaseObject {
    
     43:     DateTime orderDate;
    
     44:     public DateTime OrderDate {
    
     45:         get { return orderDate; }
    
     46:         set { SetPropertyValue<DateTime>("OrderDate", ref orderDate, value); }
    
     47:     }
    
     48:     Person employee;
    
     49:     [Association("Person-Orders")]
    
     50:     public Person Employee {
    
     51:         get { return employee; }
    
     52:         set { SetPropertyValue<Person>("Employee", ref employee, value); }
    
     53:     }
    
     54:     public Order(Session session) : base(session) { }
    
     55:     public override string ToString() {
    
     56:         return string.Format("Order - {0};", OrderDate);
    
     57:     }
    
     58: }
    
     59: 

    Now, let’s drop the ListBox (listBox1) and Button (button1) components onto the form and handle the Click event for button1. Additionally, we will modify the form’s code as follows:

      1: using DevExpress.Xpo;
    
      2: using DevExpress.Xpo.DB;
    
      3: using DevExpress.Xpo.Helpers;
    
      4: ...
    
      5: public partial class Window1 : Window {
    
      6:     UnitOfWork uowCommon;
    
      7:     Person personToBind;
    
      8:     public Window1() {
    
      9:         InitializeComponent();
    
     10:         XpoDefault.DataLayer = new SimpleDataLayer(
    
     11:             new InMemoryDataStore(AutoCreateOption.DatabaseAndSchema)
    
     12:         );
    
     13:         XpoDefault.Session = null;
    
     14:         PrepareObjects();
    
     15:         uowCommon = new UnitOfWork();
    
     16:         personToBind = uowCommon.FindObject<Person>(null);
    
     17:         if(personToBind != null){
    
     18:             listBox1.ItemsSource = personToBind.Orders;
    
     19:         }
    
     20:     }
    
     21:     private void button1_Click(object sender, RoutedEventArgs e) {
    
     22:         if(personToBind != null && personToBind.Orders.Count > 0){
    
     23:             personToBind.Orders[0].Employee = null;
    
     24:         }
    
     25:     }
    
     26:     void PrepareObjects() {
    
     27:         using (UnitOfWork uow = new UnitOfWork()) {
    
     28:             Person p = new Person(uow);
    
     29:             p.FirstName = "Jhon";
    
     30:             p.LastName = "Smith";
    
     31:             p.Age = 25;
    
     32:             DateTime now = DateTime.Now;
    
     33:             for (int i = 0; i < 10; i++) {
    
     34:                 Order o = new Order(uow);
    
     35:                 o.Employee = p;
    
     36:                 o.OrderDate = now.AddDays(i).AddSeconds(i * 12312);
    
     37:             }
    
     38:             uow.CommitChanges();
    
     39:         }
    
     40:     }
    
     41: }
    
     42: 

    Now we are done. If we run the application, we will see the following results:

    Happy XPOing!Winking smile

  • XPO – 11.1 Sneak Peek - Profile it!

         

    Introduction

    We have one more amazing piece of news for our XPO customers. In version v2011 vol 1, we have implemented XPO Profiler - an external profiling tool for XPO-based business applications, allowing you to track internal events in the underlying XPO layer, such as session methods calls and database interaction commands.

    XPOProfiler will be initially released as a Beta, and once you install the new version of the Suite, you will find it in  the Developer Express v2011 vol 1 | Components | Tools folder under the Start menu:

    1


    In this blog I am going to overview some of its features, demonstrate different profiling modes and finally, show you a practical example on using this tool.

    Getting started

    First, you should modify the application configuration file (this is the App.config file in case of a Windows Forms application or the Web.config file in case of an ASP.NET application) and specify the profiling settings in a special section, as follows:

     

      1: <?xml version="1.0" encoding="utf-8"?>
    
      2: <configuration>
    
      3:     <configSections>
    
      4:         <section
    
      5:                  name="DevExpressXpoProfiler"
    
      6:                  type="DevExpress.Xpo.Logger.ProfilerConfigSection, DevExpress.Data.v11.1, Version=11.1.1.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
    
      7:                  allowLocation="true"
    
      8:                  allowDefinition="Everywhere" />
    
      9:     </configSections>
    
     10:     <DevExpressXpoProfiler
    
     11:         serverType="DevExpress.Xpo.Logger.Transport.LogServer"
    
     12:         serverAssembly="DevExpress.Xpo.v11.1, Version=11.1.1.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
    
     13:         categories="SQL;Session"
    
     14:         port="52934">
    
     15:     </DevExpressXpoProfiler>

    Take special note that the configSections element must be the first child element of the configuration element.

    I anticipate that you already thought that  patching  the  configuration file manually is inconvenient.  Well, we have already considered this! XPOProfiler can also update your configuration file automatically. Just choose the corresponding command in the main menu, and select your configuration file in the opened dialog window:

    2 3

    Now all XPO log messages from our application will be transferred to the port we specified. It is also possible to profile several applications at the same time. In order to do this, you can set different port numbers.

    XPOProfiler can work in two profiling modes: local and remote. The first mode can be used for profiling client applications on a local machine when the latter is valuable for profiling services hosted on a remote machine.

    Local profiling mode

    If you profile a local application, you should set the Server name to localhost and specify the port number according to the profiled application settings:

    4

     After that, you will be able to see profiling messages in the invoked page of the XPOProfiler window:

    5

     

    Remote profiling mode

    To profile your application remotely, you will need an additional tool called XPOProfilerProxy. By default, it is located in the %PROGRAMFILES%\DevExpress 2011.1\Components\Tools\DXperience\ folder. This program must be run on the same computer as your profiled application. You can start XPOProfilerProxy.exe either as a console application or as a service, using the following command line options:

     

    /c – start as console application;

    /i – install as service;

    /u – uninstall service.

     

    The configuration file of the profiled remote application should also be modified as described above. Port number 52927 is reserved for service purposes, so you cannot specify this number for communications.

     

    Once you are done, create a new profiling page in the XPOProfiler. Then, in the Server name field, specify the name of the computer your application is running on and the port number, according to the profiled application settings:

    6

     

    After that, you will be able to see profiling messages in the invoked page of the XPOProfiler window.

     

    Practical usage

    Finally, we would like to show a practical use of the profiler, to easily resolve problems in your XPO application. Let us take a Windows Forms project and declare a persistent class - Person, in it:

      1: using DevExpress.Xpo;
    
      2: 
    
      3: namespace SampleApp {
    
      4:     public class Person : XPObject {
    
      5:         string firstName;
    
      6:         public string FirstName {
    
      7:             get { return firstName; }
    
      8:             set { SetPropertyValue<string>("FirstName", ref firstName, value); }
    
      9:         }
    
     10:         string lastName;
    
     11:         public string LastName {
    
     12:             get { return lastName; }
    
     13:             set { SetPropertyValue<string>("LastName", ref lastName, value); }
    
     14:         }
    
     15:         int age;
    
     16:         public int Age {
    
     17:             get { return age; }
    
     18:             set { SetPropertyValue<int>("Age", ref age, value); }
    
     19:         }
    
     20:         public Person(Session session) : base(session) { }
    
     21:         public Person() : base(Session.DefaultSession) { }
    
     22:     }
    
     23: }

    There will be also a form with the GridControl (gridControl1), Session (session1), XPView (xpView1) and SimpleButton (simpleButton1) components. The XPView object is bound to the GridControl and linked to the Session. The ObjectClassInfo property of the XPView object is set to our Person class. The Session is connected to the local MS SQL Server instance. The screenshot below shows what they look like together at design time:

    7

    Now, we will handle the Click event of our  SimpleButton and write the following code in its  Click event handler:

      1: private void simpleButton1_Click(object sender, EventArgs e) {
    
      2:     Person person = new Person();
    
      3:     person.FirstName = "New first name";
    
      4:     person.LastName = "New last name";
    
      5:     person.Age = 27;
    
      6:     person.Save();
    
      7:     xpView1.Reload();
    
      8: }
    
      9: 

    This code is intended to create a new object and reload the XPView to display the changes in the GridControl. However, if you run the application and test this code, you will notice that it does not work – there are simply no new objects appearing in the grid, after the button is clicked.

    What is wrong in our code? Let’s find the answer with the help of XPOProfier!

    First launch the tool and connect it to our application as we described above (local profiling mode) and retest the problematic area. The following information will appear on the profiler page:  

    8

     Take special note that there are two Sessions, not one (you will remember that we set only one at design time):

    9

    The first Select request is done through the Session object, which is using MSSqlConnectionProvider. Everything functions as expected here and the problem is probably with the second session:

    10

    Our assumption is correct, because the Insert request is done through the Session, using AccessConnectionProvider. You will remember that most likely this session is Session.DefaultSession. After that, we see that we violated the first XPO best practice and created a new Person object with the default constructor, by mistake.  Let’s fix this bug as follows:


      1: private void simpleButton1_Click(object sender, EventArgs e) {
    
      2:     Person person = new Person(session1);
    
      3:     person.FirstName = "New first name";
    
      4:     person.LastName = "New last name";
    
      5:     person.Age = 27;
    
      6:     person.Save();
    
      7:     xpView1.Reload();
    
      8: }
    
      9: 

    Then start our application and retest the fixed code:

    11

    Of course, it is now working as expected.

    Although the example here is quite straightforward, and the test mistake in our code might be solved without the profiler, i.e. by just remembering some XPO specifics or detecting an Access database near the executable file, there are of course lots of other and more complex cases, where profiling XPO internals is vital. XPOProfiler can help you save a significant amount of time when solving problems in these kinds of scenarios, or finding bottlenecks in your application. For instance, one more noticeable feature of the XPO profiler is that it automatically detects and informs you of situations when a single Session is accessed from multiple threads.

    As always, we are eager to hear your thoughts on this addition to the XPO suite!

    Happy XPO-profiling!Winking smile
  • XPO – 11.1 Sneak Peek – Data caching improvements

         

    Introduction

    First, we would like to refresh your memory and return to the distant past and our old posts about data caching in XPO:

    ·        XPO is good for distributed applications

    ·        XPO Beta feature: SQLDependency support

    Today is 2011, and we are excited to announce that in v2011 vol1, we greatly improved data caching support in XPO, and added additional functionality to it, allowing this feature to leave the “beta” state.

    Want data caching via WCF Services? – Now, it’s easy!

    In the previous blog, we already demonstrated how easy it is in 11.1 to create and use WCF Services for IDataStore. If you want to take advantages of XPO data caching using this solution, then the only difference is in using the ICachedDataStore interface and a respective base service class that implements this interface – CachedDataStoreService.

    Let’s demonstrate it in action (the exact steps on how to add a data store service can be taken from the previous blog):

      1: using DevExpress.Xpo;
    
      2: using DevExpress.Xpo.DB;
    
      3: …
    
      4: public class Service1 : CachedDataStoreService {
    
      5:     public static ICachedDataStore MainDataStore;
    
      6:     static Service1() {
    
      7:         string connectionString = MSSqlConnectionProvider.GetConnectionString("localhost", "ServiceDB");
    
      8:         IDataStore dataStore = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.DatabaseAndSchema);
    
      9:         MainDataStore = new DataCacheRoot(dataStore);
    
     10:     }
    
     11:     public Service1()
    
     12:         : base(MainDataStore) {
    
     13:     }
    
     14: }

    Of course, some modifications of the service binding in the web.config file are necessary as well:

      1: <system.serviceModel>
    
      2:  <services>
    
      3:    <service name="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior">
    
      4:      <!-- Service Endpoints -->
    
      5:      <endpoint address="" binding="basicHttpBinding" contract="DevExpress.Xpo.DB.ICachedDataStoreService">
    
      6:        <identity>
    
      7:          <dns value="localhost"/>
    
      8:        </identity>
    
      9:      </endpoint>
    
     10:    </service>
    
     11:  </services>
    
     12:  <behaviors>
    
     13:    <serviceBehaviors>
    
     14:      <behavior name="WcfService1.Service1Behavior">
    
     15:        <serviceMetadata httpGetEnabled="true"/>
    
     16:        <serviceDebug includeExceptionDetailInFaults="false"/>
    
     17:      </behavior>
    
     18:    </serviceBehaviors>
    
     19:  </behaviors>
    
     20: </system.serviceModel>
    
     21: 

    Fortunately, the client part does not need to be modified, because in our example, the service’s name did not change, and in addition, the “data caching domain” of our service is automatically detected by XPO.

    Data caching configuration

    After looking at how easy it is to implement, you may naturally ask how flexible it really is. Of course we did not forget about flexibility!

    For example, it is possible for you to configure which tables need to be cached and which ones don’t. This option can be useful for tables, which are frequently changed – if they are changed a lot, it makes no sense to cache them. The code below demonstrates how the service above can be modified to configure the DataCacheRoot, to cache only the “Customer” table:

      1: using DevExpress.Xpo;
    
      2: using DevExpress.Xpo.DB;
    
      3: using DevExpress.Xpo.DB.Helpers;
    
      4: …
    
      5: public class Service1 : CachedDataStoreService {
    
      6:     public static ICachedDataStore MainDataStore;
    
      7:     static Service1() {
    
      8:         string connectionString = MSSqlConnectionProvider.GetConnectionString("localhost", "ServiceDB");
    
      9:         IDataStore dataStore = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.DatabaseAndSchema);
    
     10:         DataCacheRoot dataCacheRoot = new DataCacheRoot(dataStore);
    
     11:         dataCacheRoot.Configure(
    
     12:             new DataCacheConfiguration(DataCacheConfigurationCaching.InList, "Customer")
    
     13:         );
    
     14:         MainDataStore = dataCacheRoot;
    
     15:     }
    
     16:     public Service1()
    
     17:         : base(MainDataStore) {
    
     18:     }
    
     19: }
    
     20: 

    Leveraging SQLDependency

    Another configuration option for data caching we would like to tell you about is using SqlDependency feature of MS SQL Server (see here for the background on that). Again, a modified service class demonstrates how this can be done:

      1: using DevExpress.Xpo;
    
      2: using DevExpress.Xpo.DB;
    
      3: using DevExpress.Xpo.DB.Helpers;
    
      4: …
    
      5: public class Service1 : CachedDataStoreService {
    
      6:     public static ICachedDataStore MainDataStore;
    
      7:     static Service1() {
    
      8:         string connectionString = MSSqlConnectionProvider.GetConnectionString("localhost", "ServiceDB");
    
      9:         MSSqlConnectionProvider dataStore = (MSSqlConnectionProvider)XpoDefault.GetConnectionProvider(
    
     10:             connectionString,
    
     11:             AutoCreateOption.DatabaseAndSchema
    
     12:         );
    
     13:         MainDataStore = (ICachedDataStore)MSSql2005SqlDependencyCacheRoot.CreateSqlDependencyCacheRoot(
    
     14:             dataStore,
    
     15:             new DataCacheConfiguration(DataCacheConfigurationCaching.InList, "Customer"),
    
     16:             out objectsToDispose
    
     17:         );
    
     18:     }
    
     19:     public Service1()
    
     20:         : base(MainDataStore) {
    
     21:     }
    
     22: }

    That’s it for this blog. Do you like what you see? Please let us know with your comments!

    And happy XPOingWinking smile, as always!;-)

  • XPO – 11.1 Sneak Peek - WCF services for IDataStore

         

    In XPO v2011.1 vol 1, we added support for WCF Services. These improvements provide the capability to create service and client for IDataStore as easy as it can only be.

    In order to support this feature, the DevExpress.Xpo assembly will reference assemblies from .NET 3.0. Unfortunately, Visual Studio 2010 does not allow building projects whose Target Framework is set to 2.0, if they reference .NET 3.0 assemblies. To bypass this, it is necessary to set Target Framework to 3.0. Quite surprisingly, other Visual Studio versions do not demonstrate the above problem.
    This does not mean that an application will not work on a machine that has only .NET 2.0 installed on it. If the project does not use the functionality of .NET 3.0, it will work just as before. Therefore, it should not affect your clients’ installations at all.

    Let’s learn more about the implemented feature from a simple example.
    For the beginning, we will create a new WCF Service Application project. Then, we will add references to the DevExpress.Data and DevExpress.Xpo assemblies and remove files with auto-generated interfaces for the service.

    The next step is modifying the service class as follows: 

    using DevExpress.Xpo;

    using DevExpress.Xpo.DB;

    namespace WcfService1 {

        public class Service1 : DataStoreService {

            public static IDataStore DataStore;

            static Service1() {

                string connectionString = MSSqlConnectionProvider.GetConnectionString("localhost", "ServiceDB");

                DataStore = XpoDefault.GetConnectionProvider(connectionString, AutoCreateOption.DatabaseAndSchema);

            }

            public Service1()

                : base(DataStore) {

            }

        }

    }

    To finish with the service, we will need to change some binding properties in its web.config file:

    <system.serviceModel>

        <services>

            <service name="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior">

                <!-- Service Endpoints -->

                <endpoint address="" binding="basicHttpBinding" contract="DevExpress.Xpo.DB.IDataStoreService">

                    <identity>

                        <dns value="localhost"/>

                    </identity>

                </endpoint>

            </service>

        </services>

        <behaviors>

            <serviceBehaviors>

                <behavior name="WcfService1.Service1Behavior">

                    <serviceMetadata httpGetEnabled="true"/>

                    <serviceDebug includeExceptionDetailInFaults="false"/>

                </behavior>

            </serviceBehaviors>

        </behaviors>

    </system.serviceModel>

    Now our service is ready, and it is time to implement the client part.
     
    For the client, we will add a Console Application into our existing solution. Then, we will use the Persistent Object item template for a  Customer class:

    using DevExpress.Xpo;

    namespace ConsoleApplication1 {

        public class Customer : XPObject {

            public Customer(Session session) : base(session) { }

            string _CompanyName;

            public string CompanyName {

                get { return _CompanyName; }

                set { SetPropertyValue("CompanyName", ref _CompanyName, value); }

            }

            string _CompanyAddress;

            public string CompanyAddress {

                get { return _CompanyAddress; }

                set { SetPropertyValue("CompanyAddress", ref _CompanyAddress, value); }

            }

            string _ContactName;

            public string ContactName {

                get { return _ContactName; }

                set { SetPropertyValue("ContactName", ref _ContactName, value); }

            }

            string _Country;

            public string Country {

                get { return _Country; }

                set { SetPropertyValue("Country", ref _Country, value); }

            }

            string _Phone;

            public string Phone {

                get { return _Phone; }

                set { SetPropertyValue("Phone", ref _Phone, value); }

            }

        }

    }

    The final step is to modify the Main() of our console application as shown in the code below:

    using System;

    using DevExpress.Xpo;

    using DevExpress.Xpo.DB;

    namespace ConsoleApplication1 {

        class Program {

            static void Main(string[] args) {

                XpoDefault.DataLayer = XpoDefault.GetDataLayer("http://localhost:64466/Service1.svc",

                AutoCreateOption.DatabaseAndSchema);

                XpoDefault.Session = null;

                using (UnitOfWork uow = new UnitOfWork()) {

                    using (XPCollection<Customer> customers = new XPCollection<Customer>(uow)) {

                        foreach (Customer customer in customers) {

                            Console.WriteLine("Company Name = {0}; ContactName = {1}", customer.CompanyName, customer.ContactName);

                        }

                    }

                }

                Console.WriteLine("Press any key...");

                Console.ReadKey();

            }

        }

    }

    As you can see, we pass the address of our service into the GetDataLayer method of the XpoDefault class. It is very similar to what was already supported for Web Services.

    Now if we run the service and client parts, we will see the following output:
    screenshot
    Take special note that the port number in the connection string may be different. You can check it in the properties of the service project in the Solution Explorer:
    screenshot

    Happy XPOing!

  • XPO – 11.1 Sneak Peek – Support for the OUTER APPLY operator in SQL queries

         

    We have some good news for our XPO users. In v2011 vol1 we have improved our sql-generator by supporting the OUTER APPLY operator. This SQL operator enables you to create an aggregate’s sub queries outside the WHERE clause and the SELECT expressions. For instance, it provides the capability to perform nested aggregates and aggregates in the group by list of the GROUP BY clause.

     

    How this support is useful for you? It is useful because Microsoft SQL Server and SQL Server Compact are limited  in performing aggregates in these scenarios. For instance, the following queries will throw an exception without the OUTER APPLY operator:

     

    1) Nested aggregate:

     

    select N0.Oid, (select max(N1.Date) from [Orders] N1

    where N1.EmployeeId = N0.EmployeeId

    AND (select count(*) from [Orders] N2 where N2.CustomerId = N1.CustomerId) <= 2)

    from [Employees] N0

     

    2) Aggregate in the group by list of a GROUP BY clause:

     

    select (select count(*) as Res from "dbo"."FreeClass" N2

    where (N0."Data" = N2."Data"))

    from "dbo"."FreeClassSub" N0

    group by (select count(*) as Res from "dbo"."FreeClass" N3

    where (N0."Data" = N3."Data"))

     

    However, if we modify the above queries to use the OUTER APPLY operator, they will perform correctly:

     

    1) Nested aggregate:

     

    select N0.Oid, OA0.Res

    from [Employees] N0

    outer apply

    (select max(N1.Date) as Res from [Orders] N1

    outer apply

    (select count(*) as Res from [Orders] N2 where N2.CustomerId = N1.CustomerId) OA1

    where N1.EmployeeId = N0.EmployeeId AND OA1.Res <= 2)) OA0

     

     

    2) Aggregate in the group by list of a GROUP BY clause:

     

    select OA0.Res from "dbo"."FreeClassSub" N0

    outer apply

    (select count(*) as Res from "dbo"."FreeClass" N3 where (N0."Data" = N3."Data")) OA0

    group by OA0.Res

     

    The OUTER APPLY operator is supported by Microsoft SQL Server and SQL Server Compact. So starting from version 11.1, criteria and group properties (like “[Orders][ [Customers][City = ‘London’].Count() > 10 ].Min(ShippedDate)”) can be performed on those DBMS without any problems. BTW, I recently saw one more practical example from a customer, who incurred the limit of the queries described above. I will post it here, just in case you have similar structures in your projects. So, here are his business classes:

     

    public class ParentObject : XPObject {
        ...
        [
    Aggregated]

        [Association("Parent-Children")]

        public XPCollection<ChildObject> ChildObjects { ... }

        [PersistentAlias("ChildObjects[].Max(LastChange)")]

        public DateTime LastChangeOfChildObjects {

            get { return (DateTime)EvaluateAlias("LastChangeOfChildObjects"); }

        }

        ...

    }

    public class ChildObject : XPObject {
        ...

        [Association("Parent-Children")]

        public ParentObject Parent { ... }

        public DateTime LastChange { ... }

        ...

    }


    The ParentObject was bound to a grid control working in the server mode. If you try to group by the LastChangeOfChildObjects column (take special note of the expression in its PersistentAlias attribute) in 10.2, it will not work, because of the described limitation. There are of course many other examples.

     

    Finally, I should note that support for the OUTER APPLY operator is an internal improvement, and that means that XPO users will not require any additional actions to turn it on.

  • XPO to Database Connectivity: Mastering Fork Etiquette

         

    Check out this how-to article that shows a new way of setting up XPO to work with websites, where a large number of simultaneously connected users is a common occurrence. It may help you reduce server response latency for pages that depend on data supplied by eXpress Persistent Objects.
    Mastering Fork Etiquette
    Let's consider a standard approach to XPO data layer creation in an ASP.NET website:

    // C#
    using System.Configuration;
    using System.Data.SqlClient;
    using DevExpress.Xpo;
    using DevExpress.Xpo.DB;
    using DevExpress.Xpo.Metadata;
    
    IDataLayer GetDataLayer() {
        ReflectionDictionary dict = new ReflectionDictionary();
        dict.CollectClassInfos(typeof(Person).Assembly);
    
        string connStr = ConfigurationManager.ConnectionStrings["PrimaryConnection"].ConnectionString;
        SqlConnection conn = new SqlConnection(connStr);
        IDataStore store = new MSSqlConnectionProvider(conn, AutoCreateOption.SchemaAlreadyExists);
    
        return new ThreadSafeDataLayer(dict, store);
    }
    
    
    ' VB.NET
    Imports System.Data.SqlClient
    Imports DevExpress.Xpo
    Imports DevExpress.Xpo.DB
    Imports DevExpress.Xpo.Metadata
    
    Private Function GetDataLayer() As IDataLayer
        Dim dict As New ReflectionDictionary()
        dict.CollectClassInfos(GetType(Person).Assembly)
    
        Dim connStr As String = Configuration.ConfigurationManager.ConnectionStrings("PrimaryConnection").ConnectionString
        Dim conn As New SqlConnection(connStr)
        Dim store As IDataStore = New MSSqlConnectionProvider(conn, AutoCreateOption.SchemaAlreadyExists)
    
        Return New ThreadSafeDataLayer(dict, store)
    End Function
    

    A single database connection object is created in the code snippet above, which is then used by XPO for data access. 

    Generally, an ASP.NET application is a multi-user application. The situation when two users are requesting a webpage simultaneously is quite common. IIS and ASP.NET support multithreading. Said differently, two user requests can be processed in parallel. However, when it comes to querying the database, the second user is kept waiting while the first user's query is being processed, because a single database connection object has been created for the XPO data layer.

    Creating a separate XPO data layer for each user or even for each page request is one possible solution here. This solution, however, is not perfect: XPO layer creation and establishing a database connection is a slow and resource expensive operation. Moreover, a ThreadSafeDataLayer, as it follows from its name, already supports multithreading. If one could create a pool of multiple connection objects and teach the XPO layer to select the first free available connection for the next database query, that would be really cool.

    Fortunately, ThreadSafeDataLayer already can handle a connection pool called DataStoreFork. That is to say, you create an IDataStore object array, instantiate a DataStoreFork, which is then passed to the ThreadSafeDataLayer constructor:

    // C#
    IDataStore GetDataStore() {
        string connStr = ConfigurationManager.ConnectionStrings["PrimaryConnection"].ConnectionString;
        SqlConnection conn = new SqlConnection(connStr);
        IDataStore store = new MSSqlConnectionProvider(conn, AutoCreateOption.SchemaAlreadyExists);
        return store;
    }
    
    IDataLayer GetDataLayer() {
        ReflectionDictionary dict = new ReflectionDictionary();
        dict.CollectClassInfos(typeof(Person).Assembly);
    
        const int maxConnections = 3;
        IDataStore[] stores = new IDataStore[maxConnections];
        for(int i = 0; i < maxConnections; i++)
            stores[i] = GetDataStore();
    
        return new ThreadSafeDataLayer(dict, new DataStoreFork(stores));
    }
    
    
    ' VB.NET
    Private Function GetDataStore() As IDataStore
        Dim connStr As String = Configuration.ConfigurationManager.ConnectionStrings("PrimaryConnection").ConnectionString
        Dim conn As New SqlConnection(connStr)
        Dim store As IDataStore = New MSSqlConnectionProvider(conn, AutoCreateOption.SchemaAlreadyExists)
        Return store
    End Function
    
    Private Function GetDataLayer() As IDataLayer
        Dim dict As New ReflectionDictionary()
        dict.CollectClassInfos(GetType(Person).Assembly)
    
        Const maxConnections As Integer = 3
        Dim stores(maxConnections - 1) As IDataStore
        For i As Integer = 0 To maxConnections - 1
            stores(i) = GetDataStore()
        Next i
    
        Return New ThreadSafeDataLayer(dict, New DataStoreFork(stores))
    End Function
    

    How many connections should one create for DataStoreFork? We dare say that the number of connections must not exceed the number of processors (CPUs) installed on a given server, where the database is running.

    We also want you to be aware that the use of DataStoreFork may result in not any noticeable effect at all. Usually, the most common database queries in ASP.NET - SELECT and UPDATE - are processed with lightning speed, while data is transferred via fast LAN, to say nothing about situations where IIS and a database server are running on one and the same system. If users of your website experience performance issues, do not immediately introduce DataStoreFork, but execute thorough performance profiling to discover any bottlenecks.

    The use of DataStoreFork may make sense in other multiuser environments with a shared XPO data layer besides ASP.NET, e.g. in Web services or in 3-tier applications with XPO on the middle layer.

     

  • ANN: DXCore XPO plugin 1.2.0.4

         

    Version 1.2.0.4 of the XPO plugin is now available, compiled against DXCore 9.2.6. Please download it here:

    CR_XPOFieldSync-1.2.0.4.zip (22563 bytes)

    As always, if you're not familiar with the purpose of the XPO plugin, please read this description of the "Simplified Criteria Syntax" feature.

    This version fixes two issues:

    • Changed undo unit handling for compatibility with DXCore 9.2.6 (fixes B140294)
    • Fixed an issue where an exclusion created through the "red cross" UI in the editor wasn't persisted correctly.
Next page »
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.