Current filter:
                                You should refresh the page.
                                  • Description:

                                    1. Always define a constructor with a Session parameter in your persistent objects.
                                    This will help you prepare for point 4 (see below) and will also allow you to avoid the exception explained in the A751 article.

                                    public class OrderDetail : XPObject { public OrderDetail(Session session) : base(session) { } // ... }
                                    Public Class OrderDetail Inherits XPObject Public Sub New(ByVal session As Session) MyBase.New(session) End Sub ' ... End Class

                                    2. Use the SetPropertyValue method in persistent property setters.
                                    Here is the recommended code for a persistent property:

                                    string fProductName; public string ProductName { get { return fProductName; } set { SetPropertyValue("ProductName", ref fProductName, value); } }
                                    Private fProductName As String Public Property ProductName() As String Get Return fProductName End Get Set(ByVal value As String) SetPropertyValue("ProductName", fProductName, Value) End Set End Property

                                    Q. Why must I define a property rather than just a field (public string ProductName; )? Why should I use a SetPropertyValue rather than simply setting a value to a class field?
                                    A. XPO expects an Object Changed notification, when a persistent property's value is changed. For instance, this notification is used to include the modified object into the UnitOfWork's Objects To Save collection. The SetPropertyValue method sends this notification by calling the object's OnChanged method, while a simple field assignment does not.
                                    Q. Why not to use a GetPropertyValue in a property's getter?
                                    A. Code like return fProductName; is faster than a call to the GetPropertyValue method. When it comes to reading persistent properties, the access time is critical, because it's a frequent operation. Please refer to the XPO Simplified property syntax article to learn more.
                                    Q. Should I use the GetPropertyValue/SetPropertyValue methods for delayed properties?
                                    A. You must use the GetDelayedPropertyValue/SetDelayedPropertyValue or GetPropertyValue/SetPropertyValue methods to correctly handle delayed loading.
                                    Q. The GetDelayedPropertyValue/SetDelayedPropertyValue methods does not provide overloaded method to specify the property holder. How to implement the extended variant of the delayed property, and include different delayed properties in one group?
                                    A. In this scenario, the following construction can be used:

                                    private XPDelayedProperty fPicture = new XPDelayedProperty(); [Delayed("fPicture", "GroupAttributes")] public byte[] Picture { get { return (byte[])fPicture.Value; } set { fPicture.Value = value; } }
                                    Private fPicture As New XPDelayedProperty() <Delayed("fPicture", "GroupAttributes")> _ Public Property Picture() As Byte() Get Return CType(fPicture.Value, Byte()) End Get Set(ByVal value As Byte()) fPicture.Value = value End Set End Property

                                    3. Explicitly set the XpoDefault.DataLayer property in the entry point of your application.

                                    [STAThread] static void Main() { string conn = AccessConnectionProvider.GetConnectionString(@"ApplicationData.mdb"); XpoDefault.DataLayer = XpoDefault.GetDataLayer(conn, AutoCreateOption.DatabaseAndSchema); ... }
                                    <STAThread()> _ Shared Sub Main() Dim conn As String = AccessConnectionProvider.GetConnectionString("ApplicationData.mdb") XpoDefault.DataLayer = XpoDefault.GetDataLayer(conn, AutoCreateOption.DatabaseAndSchema) ... End Sub

                                    Without this code, each new Session or UnitOfWork will create a new data layer for its exclusive use. The data layer creates a new connection to your database. This is not advisable for at least for two reasons:

                                    1. Establishing a connection to a database is a long lasting operation. It may cause performance issues.
                                    2. Your application may exceed of the maximum DB connection limit.
                                    When the XpoDefault.DataLayer is set, all Session and UnitOfWork objects, which were created with their default constructors (without a DataLayer parameter), share a given XpoDefault.DataLayer object. This will also help you get prepare for Best Practices #4 (see below). Please note that setting an XpoDefault.DataLayer doesn't prevent you from creating a new connection to your database when you need one (e.g. for emulating a multi-user application for testing purposes): You can create a new SimpleDataLayer instance, pass it to the Session's constructor as a parameter and use this Session as your needs dictate.
                                    4. Use a new Session / UnitOfWork instance to fully control loading, modifying and saving data. XPO Session caches objects. By creating a new Session/UnitOfWork instance for data processing, you acquire better control over reloading data and saving changes. We advise that you utilize a separate UnitOfWork instance in all visual modules (Forms and UserControls) of your application. See an example here: How to view persistent objects in the XtraGrid and edit them in a separate window.
                                    5. Avoid the use of a default session. The XPO default Session is accessible via the XpoDefault.Session or Session.Default static property. The default session is used internally when objects or XPCollection instances are created without a Session parameter. This may result in a SessionMixingException and/or make you write cumbersome code for reloading data (see How XPO reloads objects and collections). To avoid these problems, please don't use the default session:
                                    3. Set XpoDefault.Session to null (Nothing) in the entry point of your application:
                                    XpoDefault.Session = null;
                                    XpoDefault.Session = Nothing
                                    2. Remove default constructors from your persistent classes.
                                    6. Use a UnitOfWork rather than Session. When a Session is used, and its transaction isn't explicitly started, a persistent object is immediately persisted in the data store upon the Save method call. Unlike Session, UnitOfWork doesn't persist changes until its CommitChanges method is called. Thus, the UnitOfWork gives you more control over what and when to save. See also: Unit of Work.
                                    7. Create a separate application for database maintenance and schema updates. For security reasons, you may wish to deny access to system tables and disable modifications to the database schema for the database account used in your end-user application. Please use the AutoCreateOption.SchemaAlreadyExists option when creating a DataLayer in your XPO application. In this case, you can grant fewer privileges to the database user account used in your application. To create a database schema, please write a separate application, which calls the UpdateSchema and CreateObjectTypeRecords methods:
                                    string conn = ...; IDataLayer dl = XpoDefault.GetDataLayer(conn, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema); using(Session session = new Session(dl)) { System.Reflection.Assembly[] assemblies = new System.Reflection.Assembly[] { typeof(AnyPersistentObjectFromAssemblyA).Assembly, typeof(AnyPersistentObjectFromAssemblyB).Assembly }; session.UpdateSchema(assemblies); session.CreateObjectTypeRecords(assemblies); }
                                    Dim conn As String = ... Dim dl As IDataLayer = XpoDefault.GetDataLayer(conn, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema) Using session As Session = New Session(dl) Dim assemblies As System.Reflection.Assembly() = New System.Reflection.Assembly() _ { GetType(AnyPersistentObjectFromAssemblyA).Assembly, GetType(AnyPersistentObjectFromAssemblyB).Assembly } session.UpdateSchema(assemblies) session.CreateObjectTypeRecords(assemblies) End Using

                                    8. Creating criteria
                                    Refer to the Creating Criteria and all related topics to master this fundamental element in the DevExpress components and XAF/XPO frameworks world. Take special note that the same criteria can be represented by many ways. For example, if I want to filter only objects, which have a value equal to or greater than 20 in their "UnitPrice" field I can use the following criteria:

                                    CriteriaOperator criteria = new BinaryOperator("UnitPrice", 20, BinaryOperatorType.GreaterOrEqual); CriteriaOperator criteria = new OperandProperty("UnitPrice") >= new OperandValue(20); CriteriaOperator criteria = CriteriaOperator.Parse("UnitPrice >= ?", 20); CriteriaOperator criteria = CriteriaOperator.Parse("UnitPrice >= 20"); CriteriaOperator criteria = CriteriaOperator.Parse(string.Format("UnitPrice >= {0}", 20));

                                    The first three approaches are the best here because they lead to less errors. From them the first two are even strongly-typed which is yet better. To learn more about this, check out the Simplified Criteria Syntax help topic and this video. The third one utilizing positional parameters (they are specified via the ? character) is also safe enough. The last two approaches are also correct but not so safe. We don't recommend using them widely. Normally, you can use them only if you know exactly what you are doing and you are an expert in the criteria language syntax. This is because, for example, you should be attentive about the correct syntax when specifying values of various types as literals and so lead to errors. See the Literals section in the previous help topic for more information.
                                    Bonus: Persistent objects etiquette for enterprise applications
                                    Here, Etiquette means a simple set of rules, which are not enforced, but if followed, will simplify your life significantly:

                                    • Never use Session.DefaultSession for actual persistent objects.
                                    • Never use an XPBaseObject if not really required. Use XPCustomObject or XPObject as base classes.
                                    • Never produce side effects on a persistent property assignment. At the very least, don't change or access directly or indirectly other persistent properties between OnLoading and OnLoad (while IsLoading == true) and between OnSaving and OnSaved (while IsSaving == true).
                                    • If you need to enforce your business rules in the properties setters or getters, always check the IsLoading and IsSaving values:
                                    private string name; public string Name { get { return name; } set { bool changed = SetPropertyValue("Name", ref name, value); if (!IsLoading && !IsSaving && changed) { ... YourBusinessRule... } } }
                                    Private _name As String Public Property Name() As String Get Return _name End Get Set(ByVal value As String) Dim changed As Boolean = SetPropertyValue("Name", _name, value) If (Not IsLoading) AndAlso Not IsSaving AndAlso changed Then ...YourBusinessRule... End If End Set End Property

                                    or use the following technique:

                                    [Persistent("Name")] private string PersistentName { get { return name; } set { SetPropertyValue("PersistentName", ref name, value); } } [PersistentAlias("PersistentName")] public virtual string Name { get { return PersistentName; } set { DoMyBusinessTricksBeforePropertyAssignement(); PersistentName = value; DoMyBusinessTricksAfterPropertyAssignement(); } }
                                    <Persistent("Name")> _ Private Property PersistentName() As String Get Return name End Get Set(ByVal value As String) SetPropertyValue("PersistentName", name, value) End Set End Property <PersistentAlias("PersistentName")> _ Public Overridable Property Name() As String Get Return PersistentName End Get Set(ByVal value As String) DoMyBusinessTricksBeforePropertyAssignement() PersistentName = value DoMyBusinessTricksAfterPropertyAssignement() End Set End Property
                                    • It's a bad idea to throw exceptions inside your persistent properties. At the very least, don't do it between OnLoading and OnLoaded (IsLoading == true) and between OnSaving and OnSaved (IsSaving == true).
                                    • Don't use the Session's Save method whenever possible. Use the UnitOfWork's CommitChanges whenever possible.
                                    • Share a single DataLayer between all your sessions within same AppDomain whenever possible. Assigning XpoDefault.DataLayer in the Main() function of your program is good style.
                                    • Never do any complex work on persistent object .ctor(Session) and persistent properties assignments (at least between OnLoading and OnLoaded (IsLoading == true) and between OnSaving and OnSaved (IsSaving == true)) -- if you want XPO to be effective, persistent objects must be created as quickly as possible from their data store images.
                                    • If your algorithm requires ten persistent objects -- load them all at once at the beginning of the method
                                    • Never load more objects than actually needed.
                                    • Use Grids and Lookups in ServerMode when appropriate.
                                    • Always use IDataStore for remote scenarios.
                                    • Don't expose your business objects instances remotely, in scenarios where this is possible. Use eXpress Persistent Objects, when available, if you absolutely need to transfer objects over the wire, but think twice: do you really need it?
                                    • Never use XML Web Services until all your clients have access to assemblies provided by you. Expose IDataStore via WebService, and allow your clients to work with persistent objects.
                                    • If you need to disallow your customers from changing something through remotable IDataStore -- create an IDataStore wrapper, which will throw an InvalidOperationException on each call to ModifyData.
                                    • Use Domain Model whenever possible.
                                    • The OnSaving and OnDeleting methods of your persistent objects are valuable in two ways:
                                       1. Custom keys generating
                                       2. Last-chance checking.
                                    Defense checks must not be violated in the normal workflow - if such a check fires, it means something is bad with your program.
                                    Please note that if you still decide to use these methods to execute your business logic, take into account that in a general case, they may be called multiple times (e.g., when an object is first saved in a nested Session and then committed to the database in the root Session or in a middle-tier Application Server/SecuredObjectSpaceProvider scenario). This may lead to incorrect results, unless you handle this appropriately in your business logic or if multiple execution is irrelevant for it. For instance, you can check whether a value was already assigned to a property or not. Refer to the How to generate a sequential and user-friendly identifier field within an XPO business class article for some example code. Alternatively, you can move your logic directly into dependent property setters. Refer to the Task-Based Help > How to: Calculate a Property Value Based on Values from a Detail Collection article for some example code as well. Finally, you can migrate your logic from the business class into the ViewController (e.g., handle the IObjectSpace > Committed event) or another suitable UI-related entity.
                                    • Don't mix the process of objects saving/persisting and business logic (including business rules validation) -- this is a code smell of the Transaction Script pattern. The examples of defensive validation described in the previous point are exceptions, rather than good rules. You should not use this in your applications often. The best practice is to validate your objects before the saving process is started. Usually, it should be done either right after the changes are applied (for example, property setters), or later, with the help of a specific validator class that has knowledge of the contexts, in which a business object exists and is validated.
                                    • Don't work with the same persistent object instance from different threads. Create a separate session for each thread, and work with different instances of the same persistent object.
                                    • Don't create a class structure which will result in loading half of the database on accessing a single object/property. For instance if you have a class Gender with two instances, Gender("Male") and Gender("Female") it's a bad idea to create a collection of all persons of a specific sex in the Gender class. If you need to do it for building criteria -- make the collection property private or protected, and return null from it (undocumented and unsupported but a working and useful feature).
                                    • If your class structure is highly coupled and it's actually possible to raise a complete database accessing the single object/property, break this net using delayed loading.
                                    • Don't use Delayed loading if not really needed.
                                    • Don't make your code dependent on the order of records returned by the XPCollection/XPView/XPCursor objects, unless you explicitly sorted them. By default, if the XPCollection/XPView/XPCursor is NOT sorted, records will be returned in an arbitrary order. This mimics the behavior of the SQL SELECT statement in the same circumstances.
                                    See Also:
                                    XPO Fundamentals
                                    XPO Worst Practices
                                Show all comments
                                • Phil Burgin 12.14.2012

                                  Is this document still relevant considering its age?

                                • Michael (DevExpress Support) 12.14.2012

                                  Yes. We are reviewing this article periodically to keep it up to date.

                                • YALIN SOFTWARE 08.28.2013

                                  If you fix broken links, that would be great ;)

                                • Anatol (DevExpress Support) 08.28.2013

                                  I have corrected the links, thank you.

                                • Jim Clay 08.28.2013

                                  You give a link to the EasyFields plug in, which I have used extensively in the past. But it had some issues in 13.1 and with the new visual designer, it does not appear to be usable. Also, it doesn't appear that Michael has worked on it for 2 years. It would be nice if DevExpress would consider this as on official part of the XPO and support it properly or remove the references to it. So far there have been 2 "Unofficial" plug-ins for this from Oliver and Michael, and it appears that both have been pushed to irrelevance.

                                • Anatol (DevExpress Support) 08.29.2013

                                  Thank you for your suggestion. We do not have immediate plans to include this functionality in XPO out-of-the-box. I suggest that you contact Michael (e.g. via the group) and describe problems you have encountered in version 13.1.

                                • Nassos Katsaounis 06.10.2016


                                  In #4 you state "Please review the attached sample project" but I do not see any attachment. An attachment would be great help.

                                  Also, there are still, several links broken in the article.

                                  Thank you,

                                • Anatol (DevExpress Support) 06.10.2016

                                  I have updated the 4th paragraph and all outdated links I could find. Thank you for pointing us to these issues.

                                • Andrew Bingham 2 08.06.2018

                                  There are 2 points mentoned above which somewhat contradict the ORM designer

                                  1‍. Remove default constructors. These are (optionally) added by the Designer
                                  2‍. Use return field name as property getter. The Designer adds the GetPropertyValue() method


                                  Also....the property getter and setter methods would be better implemented using nameof() ??

                                • Michael (DevExpress Support) 08.07.2018

                                  @Andrew: In the current version, the ORM Data Model Designer operates according to these recommendations.

                                  1. Default constructors are not generated by default. However, they can be useful in certain scenarios. To add them, set the Generate Default Constructors option of the model to True.
                                  2. Persistent property getters return the field value directly.
                                  3. Persistent property setter use nameof() by default (in Visual Studio 2015 and later versions). To use string literals, set the Enable nameof option of the model to False.
                                • Andrew Bingham 2 08.09.2018

                                  Thanks Michael

                                  That is very clear.

                                  I have v16.2 which does not have "Enable nameof " in the Model?
                                  * I can't see this in the documentation either?

                                • Andrey K (DevExpress Support) 08.09.2018


                                  You are right. We supported the 'Nameof' operator in 17.2. Let me know if you have any additional questions.


                                • Daniel Garcia 20 04.05.2019

                                  >Q. Why not to use a GetPropertyValue in a property's getter?
                                  >A. Code like return fProductName; is faster than a call to the GetPropertyValue method. When it comes to reading persistent properties, the access time is critical, because it's a frequent operation. Please refer to the XPO Simplified property syntax article to learn more.

                                  Is this statement still valid? My team doesn't want to create more variables than are necessary, and if this no longer gives a significant performance advantage, we would prefer to simply call GetPropertyValue.

                                  I'm running DevExpress.ExpressApp version 18.1.6.

                                • Anatol (DevExpress Support) 04.05.2019

                                  Yes, it is valid. Returning a field value will always be faster than getting a property value through the GetPropertyValue method. Note that this is a best practice rather than a strict recommendation. In most scenarios, the GetPropertyValue method's execution time is insignificant.

                                0 Solutions

                                Creation Date Importance Sort by