XPO

April 2006 - Posts

  • CodeRush templates for XPO properties

    Prompted by Stefan Renzewitz in the CodeRush newsgroup, I’m making available a number of CodeRush templates, which allow you to create persistent properties of various types, including collections and association properties for “the other side” of the collection property. I guess a variant of these templates will be included in the default distribution of CodeRush in the future, but until that happens, please import the file from the download below. Sorry, C# only for now.

    These are the templates:

    pp(b|c|d|dt|f|i|o|s):

      private <type> propertyName;
      public <type> PropertyName {
        get {
          return propertyName;
        }
        set {
          if (propertyName != value) {
            <type> oldValue = propertyName;
            propertyName = value;
            OnChanged("PropertyName", oldValue, propertyName);
          }
        }
      }

    Depending on the postfix used, the “<type>” in the code is expanded into bool, char, decimal, DateTime, float, int, object or string.

    pcoll:

      [Association("PersistentClass-Relations")]
      public XPCollection<RelationType> Relations {
        get {
          return GetCollection<RelationType>("Relations");
        }
      }
    

    ppa (the “a” is for “association”):

      private object propertyName;
      [Association("object-PersistentClasss")]
      public object PropertyName {
        get {
          return propertyName;
        }
        set {
          if (propertyName != value) {
            object oldValue = propertyName;
            propertyName = value;
            OnChanged("PropertyName", oldValue, propertyName);
          }
        }
      }
    

    Of course it’s a little difficult to describe how exactly these templates work, so I suggest you download the file below, import it into CodeRush and try them out! Oh yes, for the import you just enter the CodeRush Options dialog, switch to the Templates page, right-click anywhere in the hierarchical list of templates and click the “Import Templates…” context menu command. Have fun!

    File Attachment: CSharp_Oliver_XPO.xml (48 KB)

  • ToString() and Parse() - readable criteria

    I believe it’s not yet common knowledge that the two methods ToString() and Parse() of the CriteriaOperator form a pair that can be used to convert whole hierarchies of criteria to and from a string representation. This can be pretty useful in many situations, but let’s first look at an example. Here’s a line of code from my recent Advanced queries post:

    view.AddProperty("OrderValue",
      "Entries.Sum(iif(RebatePercent == 0, ArticlePrice, ArticlePrice - (ArticlePrice * RebatePercent / 100)) * ArticleCount)");
    

    In this case, the fact that the CriteriaOperator.Parse() method is used behind the scenes is not visible, but that’s exactly what happens. The complete criteria returned by the Parse() method could be created from code like this, in the same context of adding a property to a view:

    view.AddProperty("OrderValue", 
      new AggregateOperand(new OperandProperty("Entries"), 
        new BinaryOperator(
          new FunctionOperator(FunctionOperatorType.Iif,
            new BinaryOperator("RebatePercent", 0, BinaryOperatorType.Equal),
            new OperandProperty("ArticlePrice"),
            new BinaryOperator(
              new OperandProperty("ArticlePrice"), 
              new BinaryOperator(
                new BinaryOperator(
                  new OperandProperty("ArticlePrice"), 
                  new OperandProperty("RebatePercent"), 
                  BinaryOperatorType.Multiply),
                new OperandValue(100), 
                BinaryOperatorType.Divide),
              BinaryOperatorType.Minus)), 
          new OperandProperty("ArticleCount"),
        BinaryOperatorType.Multiply),
      Aggregate.Sum, null));
    

    So, to make this clear: for every CriteriaOperator derived class, it is true that if it is converted into its string representation using the ToString() method, and the string is then converted back using CriteriaOperator.Parse(), the same CriteriaOperator derived class will result. That includes all “child criteria”, i.e. the complete hierarchy starting from the operator on which ToString() was called.

    The syntax of the string representation is largely based on the specification for OPath, which Microsoft made available when ObjectSpaces was still to become part of their .NET 2 release. The specification is no longer available from Microsoft AFAIK, but a copy is available here. For specific syntax questions, the best way is to just create a CriteriaOperator of a certain type in code and then use the ToString() method on it.

    Just a note: As usual, when reading database queries from external sources you should be extremely careful about the origin. If there’s a chance that criteria might be manipulated before you load them into your application and use them, this can be a very big security problem.

    And a second note: Obviously there’s a performance overhead associated with the process of parsing the criteria from strings. This is certainly not relevant for one or a few criteria, but you should give this some thought in situations where you use criteria in loops – pre-parsing criteria for later use is easily done and can be a major performance advantage.

  • Using .NET Remoting with XPO

    In my recent post XPO is good for distributed applications I explained the basics of support for distributed applications in XPO 6.1. This post is going to show how .NET Remoting can be used to make the interface IDataStore available remotely – of course the same approaches can be used to publish ICacheToCacheCommunicationCore instead, if the caching structure is to be used.

    The server setup

    The setup for a Remoting server can be extremely simple:

      IDataStore dataStore = new DataStoreLogger(new InMemoryDataStore(new DataSet( ),
        AutoCreateOption.SchemaOnly), Console.Out);
    
      int port = 8880;
      ChannelServices.RegisterChannel(new TcpChannel(port), false);  
      MarshalByRefObject serverObject = (MarshalByRefObject) dataStore;
      RemotingServices.Marshal(serverObject, "DataSetServer");
    

     Note that in this example, a combination of the DataStoreLogger and the InMemoryDataStore classes is used to implement the server-side data store. This makes for a good test setup, but in a real-world application you would most probably want to replace that with creation code for a more useful data store, like SQL Server:

      IDataStore dataStore = XpoDefault.GetConnectionProvider(
        MSSqlConnectionProvider.GetConnectionString("server", "database"),
        AutoCreateOption.DatabaseAndSchema);
      ...
    

    All data store implementations in XPO implement the interface IDataStore, and all are derived from MarshalByRefObject – the latter is very important to be able to marshal the object as the sample shows, which is why the explicit cast in the sample code makes sure that the given class has the correct ancestor. 

    Instantiation

    In the sample above, the object in question is being marshalled as a singleton pre-existing instance. This is a good thing if you want to marshal an existing class (that you didn’t create yourself) and the creation logic is somewhat complicated – and of course if you want to use singleton objects server-side. If you want to use a server-activated object (SOA) as a singleton instead, you’d have to create a class that can be instantiated with a default constructor, and make that available as a well-known service type. And you have to override the standard lease time to make sure the object isn’t disposed after a few minutes.

      public class MyDataStore : DataStoreLogger {
        public MyDataStore( ) : base(
          new InMemoryDataStore(new DataSet(), AutoCreateOption.SchemaOnly), Console.Out) { }
    
        public override object InitializeLifetimeService( ) {
          // make sure the object is never discarded
          return null;
        }
      }
    
      ...
    
      // instead of the RemotingServices.Marshal( ... ) call:
      RemotingConfiguration.RegisterWellKnownServiceType(
        typeof(MyDataStore), "DataSetServer", WellKnownObjectMode.Singleton);

    Combinations of these approaches are also possible, or per-client instantiation – the point is, XPO doesn’t really have anything to do with this, you can use standard features of .NET Remoting as you see fit.

    The client

    Assuming we have a server-side singleton that can be directly accessed, like in the samples given above, the client-side code is extremely simple:

      IDataStore dataStore = (IDataStore) Activator.GetObject(typeof(IDataStore),
        "tcp://localhost:8880/DataSetServer");
      XpoDefault.DataLayer = new SimpleDataLayer(dataStore);

    The client simply fetches a reference to the data store that the server is publishing (assuming in the sample that the server is running on the same system, but of course “localhost” could be replaced by any other computer name or address) and creates a data layer in the normal way. As usual, all sessions that are now created will use the data layer referenced by XpoDefault.DataLayer by default, effectively accessing the server-side data store through the Remoting connection.

    Channels and deployment

    In the samples above we have been using the TCP channel all the time. Out of the box, .NET Remoting also includes an HTTP channel and (in .NET 2) an IPC channel for local communication. The IPC channel is not very important here due to its restricted use (which is not to say that it doesn’t work nicely when remoting across AppDomain boundaries), but the HTTP channel is definitely very interesting, because it allows .NET Remoting services to be deployed in IIS. This is in no way XPO specific, I just thought it might be worth mentioning, to show how flexibly .NET Remoting solutions can be deployed.

    Creating and registering an HTTP channel is just as easy as the TCP channel in the examples. The HTTP channel uses SOAP formatting by default – it might be a good idea to use binary formatting instead for performance reasons, but be aware that firewalls may sometimes be configured to perform special filtering on SOAP traffic, so binary formatting may not be an option.

    This is how you would add an HTTP channel with a binary formatter on the server side:

      Dictionary props = new Dictionary( );
      props["port"] = "8880";
      HttpChannel channel = new HttpChannel(props, null, 
        new BinaryServerFormatterSinkProvider( ));
      ChannelServices.RegisterChannel(channel, false);

    And this is the code to add the channel on the client side, and to get the published interface via HTTP:

      ChannelServices.RegisterChannel(
        new HttpChannel(null, new BinaryClientFormatterSinkProvider(), null), false);
      IDataStore dataStore = (IDataStore) Activator.GetObject(typeof(IDataStore),
        "http://localhost:8880/DataSetServer");
      ...

    I’m not going to go into the details of deploying to IIS here. There’s a pretty good description available on MSDN: Hosting Remote Objects in Internet Information Services (IIS)

    Links

  • XPO is good for distributed applications

    XPO 6.1 introduces a number of changes that do a lot for its support of distributed applications. This is going to be the first in a series of posts around the topic of distributed applications involving XPO. I hope I can shed some light on the structures we provide in XPO for this purpose and the scenarios in which various platforms like .NET Remoting, XML Web Services and Windows Communication Foundation (WCF) can be used in conjunction with XPO.

    As always, please feel free to comment on these posts as much as possible – at some point, I might summarize all the information in a more formal technical article, and it would be good to have as many different perspectives in there as possible.

    So why does XPO work together well with distributed systems in general? The answer to this question requires some knowledge about the architectural requirements of a performant setup of such systems, as well as about the new layered structure of XPO’s inner workings.

    Making distributed systems perform well

    To make distributed systems perform well, we should make sure that as little data as possible is transferred over the wire. (Depending on his/her definition of “performing well”, the programmer might also give some thought to topics like platform and/or protocol independence, but that’s not what I’m talking about here.) To make as little data go over the wire as possible, it is important to define those interfaces that are called remotely very efficiently. This means two things:

    1. The data that is being transferred must be structured specifically for this purpose.
    2. The structure of the application, and the interface itself, must be designed in such a way that calls to the remote interfaces are minimized.

    The result of this will be an interface that’s coarse-grained and that works with data types different from those that are being used on either side of the communication.

    So what does XPO do in this regard?

    XPO defines two different interfaces that are structured in the way described above: IDataStore and ICacheToCacheCommunicationCore.

    Xpo-distributed-layers

    This is how IDataStore is defined:

    public interface IDataStore {
      AutoCreateOption AutoCreateOption { get; }
      ModificationResult ModifyData(params ModificationStatement[] dmlStatements);
      SelectedData SelectData(params SelectStatement[] selects);
      UpdateSchemaResult UpdateSchema(bool dontCreateIfFirstTableNotExist, params DBTable[] tables);
    }
    

    As you can see, the data types that are being passed to and returned by these methods are generally (with the exception of the AutoCreateOption enum and the bool primitive data type) not the same that you are used to handling in your own applications, i.e. business objects. They are also not the same that are being handled by your database backend, for example ADO.NET structures or even SQL code. These are data types strictly designed for the purpose of being passed through the IDataStore interface, of course with the idea in mind that the interface may be implemented remotely.

    ICacheToCacheCommunicationCore is only minimally bigger than IDataStore:

      public interface ICacheToCacheCommunicationCore {
        DataCacheModificationResult ModifyData(DataCacheCookie cookie, ModificationStatement[] dmlStatements);
        DataCacheResult NotifyDirtyTables(DataCacheCookie cookie, params string[] dirtyTablesNames);
        DataCacheResult ProcessCookie(DataCacheCookie cookie);
        DataCacheSelectDataResult SelectData(DataCacheCookie cookie, SelectStatement[] selects);
        DataCacheUpdateSchemaResult UpdateSchema(DataCacheCookie cookie, DBTable[] tables, bool dontCreateIfFirstTableNotExist);
      }
    

    These two interfaces make it possible to cross boundaries to other systems in two places in the layer structure, depending on whether you want to use caching or not.

  • Advanced queries

    This has been posted before to the XPO beta newsgroup, but now that the beta is over, I thought it might be interesting to all XPO users. I updated everything to the current state of things, so the description should match the current release 6.1.

    XPO 6.1 is able to perform advanced queries, enabling the following features:

    • Filter criteria can use several fields at once and perform calculations
    • Database queries can be performed that don't create objects on the client
    • Aggregates can be queried directly from the database backend

    Filter criteria

    Assuming this business object:

      public class OrderEntry : XPObject {
        public OrderEntry() { }
        public OrderEntry(Session session) : base(session) { }
    
        public OrderEntry(string articleName, decimal articlePrice, int articleCount, int rebatePercent) {
          this.articleName = articleName;
          this.articlePrice = articlePrice;
          this.articleCount = articleCount;
          this.rebatePercent = rebatePercent;
        }
    
        private string articleName;
        public string ArticleName {
          get { return articleName; }
          set { articleName = value; }
        }
    
        private decimal articlePrice;
        public decimal ArticlePrice {
          get { return articlePrice; }
          set { articlePrice = value; }
        }
    
        private int articleCount;
        public int ArticleCount {
          get { return articleCount; }
          set { articleCount = value; }
        }
    
        private int rebatePercent;
        public int RebatePercent {
          get { return rebatePercent;}
          set { rebatePercent = value; }
        }
      }
    

    It is possible to create XPCollection instances like these:

      new XPCollection(typeof(OrderEntry), 
        CriteriaOperator.Parse("ArticlePrice * ArticleCount > 400"));
    
      new XPCollection(typeof(OrderEntry),
        CriteriaOperator.Parse("iif(RebatePercent == 0, ArticlePrice, ArticlePrice - (ArticlePrice * RebatePercent / 100)) > 50"));
    

    Querying from the backend without creating objects

    The new XPView class can query information from the database and make it available in a number of "fields", which have to be defined for the view beforehand.

    This code snippet would construct a view with a calculated field:

      XPView view = new XPView(session, typeof(OrderEntry));
      view.AddProperty("RealPrice", 
        "iif(RebatePercent == 0, ArticlePrice, ArticlePrice - (ArticlePrice * RebatePercent / 100))");
    

    XPView implements IBindingList, so it can be bound to visual components just like an XPCollection can. The most important difference is that the XPView's result set doesn't contain objects of the given type, but instead a dedicated object type called ViewRecord. From code, the field of the view above could be accessed like this:

      foreach(ViewRecord record in view)
        Console.WriteLine(record["RealPrice"]);
    

    Even though objects are not constructed automatically by the XPView, it is possible to access the corresponding object for a ViewRecord. It is important that the key property of the class that is being queried (the property with the KeyAttribute, called “Oid” for the standard XPObject base class) must be included in the field list of the XPView, if you want to be able to query the object directly from the ViewRecord.

      view.AddProperty("Oid");
      foreach (ViewRecord record in view) {
        OrderEntry oe = (OrderEntry) record.GetObject();
        ... // now do something with oe
      }
    

    If a filter criteria is used to access one of the fields of an XPView, the name of the field must be used (instead of the field value expression being repeated). Like here:

      view.Filter = CriteriaOperator.Parse("RealPrice > 50");
    

    Querying aggregate values

    The properties that are added to the XPView support aggregates directly. Assume the following class in addition to the OrderEntry, and the corresponding association property in the OrderEntry class (see details about associations here).

      public class Order: XPObject {
        ...
    
        private DateTime orderDate;
        public DateTime OrderDate {
          get { return orderDate; }
          set { orderDate = value; }
        }
    
        [Association("Order-Entries"), Aggregated]
        public XPCollection<OrderEntry> Entries {
          get { return GetCollection<OrderEntry>("Entries"); }
        }
    
        ...
      }
    

    This would allow for the following view to be used to find out the total values of all orders:

      XPView view = new XPView(session, typeof(Order));
      view.AddProperty("OrderDate");
      view.AddProperty("OrderValue", 
        "Entries.Sum(iif(RebatePercent == 0, ArticlePrice, ArticlePrice - (ArticlePrice * RebatePercent / 100)) * ArticleCount)");
      foreach (ViewRecord record in view)
        Console.WriteLine("Order: {0}, total value: {1}", record["OrderDate"], record["OrderValue"]);
    

    Top-level aggregates can also be queried, but in this case a method of the Session object must be used:

      int numOrderEntry = (int)
        session.Evaluate<OrderEntry>(CriteriaOperator.Parse("Count()"), null);
    
  • Relations and custom collections

    A recent discussion in the newsgroup prompted me to summarize the important things to know about creating relationships between classes, using collection type properties, and how to use derived collection types instead of the standard ones.

    One-to-many relationships

    A one-to-many relationship needs at least the reference on the “many” side. As an example, consider two classes Artist and Album. The Album class must have this:

      public class Album : XPObject {
        ...
        Artist artist;
        [Association("Artist-Albums")]
        public Artist Artist {
          get { return artist; }
          set {
            if (artist != value) {
              Artist oldValue = artist;
              artist = value;
              OnChanged("Artist", oldValue, artist);
            }
          }
        }
        ...
      }
    

    On the other side of the relation, the collection type property is optional. You'll normally add it for one of three reasons:

    1. You actually need the direct access to all the albums of an artist.
    2. You like the the neatness of having everything defined completely.
    3. You want to aggregate the collection by using the AggregatedAttribute, which works only on the "one" side of the relation.

    The code on the “one” side looks like this:

      public class Artist : XPObject {
        ...
        [Association("Artist-Albums", typeof(Album)]
        public XPCollection Albums {
          get { return GetCollection("Albums"); }
        }
        ...
      }
    

    There are two interesting variants of the syntax above:

    1. If you use XPO 6.1 with VS 2005, you can use the generic variants of the XPCollection and the GetCollection method. In turn, you can leave out the type parameter of the AssociationAttribute, because the type is then inferred by the generic type attribute. This is how it would look:
        public class Artist : XPObject {
          ...
          [Association("Artist-Albums"]
          public XPCollection<Album> Albums {
            get { return GetCollection<Album>("Albums"); }
          }
          ...
        }
      
    2. To aggregate the collection, you could add the AggregatedAttribute on the collection property.

    Many-to-Many relationships

    Based on the above, many-to-many relationships are very easy to explain: they need a collection type property on both ends, each of them similar to the one described for the “one” side of the one-to-many relationship.

    Custom collection types

    Sometimes there might be reasons to derive custom collection types from the standard XPCollection or the XPCollection<T>. This is a lot easier to do in XPO 2006 than in XPO 1 because only one such type (if we neglect the “standard” vs. the generic variant for a moment) is used, i.e. the GetCollection method always return an XPCollection and GetCollection<T> always returns XPCollection<T>. In XPO 1 this used to be different, in that GetCollection would return XPOneToManyCollection or XPManyToManyCollection, depending on the relation type of the property – so the developer was forced to create three different derived types to cover all eventualities.

    The standard types XPCollection and XPCollection<T> have a special constructor that is used for the case where they must be initialized as relationship collections. This specific collection constructor is relevant in two ways:

    1. You must make sure that your derived collection type implements it. This is not difficult, because you just need boilerplate code to pass through the parameters to the base class constructor, but you must not leave it out.
    2. In relation properties on the “one” side of a one-to-many relationship, or in the properties on both sides of a many-to-many relationship, you must use this constructor to create the collection of your derived type. This is also boilerplate code, but you must get it right.

    So, assuming you had a derived collection class MyCollection<T>, this might look similar to this code (of course there might be other constructors and probably other code that implements your specific functionality):

      public class MyCollection<T> : XPCollection<T> {
        ...
        public MyCollection<T>(Session session, object theOwner, XPMemberInfo refProperty) :
          base(session, theOwner, refProperty) { }
        ...
      }
    

    And to use this collection type in relation property, you’d have to write your code like this:

      public class Artist : XPObject {
        ...
        MyCollection<Album> myCollection;
        [Association("Artist-Albums"]
        public MyCollection<Album> Albums {
          get { 
            if (myCollection == null)
              myCollection = new MyCollection<Album>(Session, this, ClassInfo.GetMember("Albums"));
            return myCollection;
          }
        }
        ...
      }

    The code in the property getter is essentially the same code that's contained in the GetCollection<T> method – but because that method doesn’t return our own collection type we have to replace it.

    References

  • My persistent classes are real classes - or not?

    When designing classes for persistence with XPO, it’s easy to be confused about certain aspects, decisions that have to be made about the layout of the class hierarchy. There are two common outcomes that typically result in problems:

    1. The class hierarchy is designed without giving (much) thought to the requirements of XPO and persistence in general. I’m going to refer to this as the non-persistent hierarchy case.
    2. Classes are created as pure data containers, similar to Data Transfer Objects (DTOs), without functionality, and often without being organized in any real hierarchy. This is going to be the DTO case from here on.

    The non-persistent hierarchy case

    Let’s look at these cases in some detail, beginning with the non-persistent hierarchy case. What are common problems here?

    • Classes implement elaborate creation logic, for example by exposing various constructors for specific purposes and with large numbers of parameters, or by hiding their constructors altogether to force creation by use of static methods. This doesn’t play together with XPO well – it wants to have a constructor with just a Session type parameter.
    • Properties don’t have public setters because setting property values is only allowed for constructors or a set of tightly controlled methods. XPO doesn’t like this because it needs direct access to properties when objects are being loaded from the database.
    • Property setters are implemented to have lots of potential side-effects. This can be a problem because XPO uses the property setters during object loading and and when committing changes from nested units of work, which might not have been anticipated by the class designer. It can also result in performance problems, or if the potential side effects include exceptions being thrown, XPO can react to that by interrupting some process unexpectedly.
    • Algorithms are implemented to work with collections of certain abstract base class types, which might end up being non-persistent when the class hierarchy is handled by XPO. It is not possible to query collections of non-persistent types with XPO, which makes it difficult to select the data for said algorithms.

    The DTO case

    In this case it is a bit more difficult to enumerate the precise mistakes that are being made, but essentially the persistent classes are treated as equivalents of the standard .NET DataRow – they contain data, but that’s all they are allowed to do, because all functionality relevant to that data is implemented elsewhere. This violates paradigms of OO design and the developer is wasting his time – in extreme cases the benefits of an object/relational mapping system like XPO are small or non-existent.

    A warning sign for this case is usually that a very tight coupling can be detected between certain persistent types and other classes. Method calls tend to have effects in the wrong places. Consider the following piece of code:

    MyType1 object1 = ... ;
    MyType1 object2 = ...;
    
    object1.DoSomething(object2);
    

    What do you expect which objects should be changed, if any, by the call to the DoSomething() method? Right, it should be object1, more often than not. Of course the code in DoSomething() could also set properties of object2 or call further methods. But in a majority of cases, specifically when we’re talking about persistent classes (which are usually closely related to stored data), a call to a method of an object should result in some kind of change to that same object.

    After this explanation it should be clear what the warning sign is that was mentioned above: if you find a lot of method calls in your application where changes occur only in the objects that are being passed in, not in the object on which the method is called, then it is very probable that you have implemented functionality in the wrong place.

    So what’s the right way?

    That’s the nice thing about programming – there’s never a single right way. Some ways are just less wrong than others. My suggestion is, when designing classes for use with XPO, just keep a few things in mind:

    • It is not true that persistent classes don’t have any requirements of their own, above those that OO class hierarchies generally have. The list of common problems in the non-persistent hierarchy section above should give you a good starting point.
    • On the other hand, don’t forget that your persistent classes are really classes, and they can do a lot more than a dumb data container can. Don’t forget you are constructing a hierarchy, not just a single class. Things like virtual and abstract methods can mostly be used in persistent classes just as well as in non-persistent ones.

    Do you have any comments on this? I’m sure I forgot to mention something. Please post your feedback below!

LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

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

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