{"id":100279,"date":"2017-04-29T22:09:05","date_gmt":"2017-04-29T14:09:05","guid":{"rendered":"http:\/\/www.ilzhi.cn\/?p=100279"},"modified":"2017-04-29T22:22:59","modified_gmt":"2017-04-29T14:22:59","slug":"xpo-%e6%9c%80%e4%bd%b3%e5%ae%9e%e8%b7%b5%ef%bc%88xpo-best-practices%ef%bc%89","status":"publish","type":"post","link":"http:\/\/www.ilezhi.cn\/?p=100279","title":{"rendered":"XPO \u6700\u4f73\u5b9e\u8df5\uff08XPO Best Practices\uff09"},"content":{"rendered":"<pre class=\"lang:vbnet decode:true\">\u6765\u6e90\uff1a<a style=\"font-size: 12pt; font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 1.625; background-color: #ffffff;\" href=\"https:\/\/www.devexpress.com\/Support\/Center\/Question\/Details\/A2944\">https:\/\/www.devexpress.com\/Support\/Center\/Question\/Details\/A2944<\/a><\/pre>\n<p style=\"background: white;\"><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\"><strong>1. Always define a constructor with a Session parameter in your persistent objects.<br \/>\n<\/strong>This will help you prepare for point 4 (see below) and will also allow you to avoid the exception explained in the\u00a0<\/span><span style=\"color: #043f97;\">A751<\/span><span style=\"color: #252525;\">\u00a0article.<br \/>\n<\/span><\/span><\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[C#]<br \/>\n<\/strong><\/span><\/p>\n<pre class=\"lang:c# decode:true \">public class OrderDetail : XPObject {\r\n   public OrderDetail(Session session) : base(session) { \r\n   }\r\n   \/\/ ...\r\n}<\/pre>\n<p><strong>[VB.NET]<\/strong><\/p>\n<pre class=\"lang:vbnet decode:true \">Public Class OrderDetail\r\n   Inherits XPObject\r\n   Public Sub New(ByVal session As Session)\r\n       MyBase.New(session)\r\n   End Sub\r\n   ' ...\r\nEnd Class<\/pre>\n<p><!--more--><\/p>\n<p><strong>2. Use the SetPropertyValue method in persistent property setters.<\/strong><\/p>\n<p style=\"background: white;\"><span style=\"color: #252525; font-family: Segoe UI; font-size: 12pt;\">Here is the recommended code for a persistent property:<br \/>\n<\/span><\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[C#]<br \/>\n<\/strong><\/span><\/p>\n<pre class=\"lang:c# decode:true \">string fProductName;\r\npublic string ProductName {\r\n   get { return fProductName; }\r\n   set { SetPropertyValue(\"ProductName\", ref fProductName, value); }\r\n}<\/pre>\n<p><strong>[VB.NET]<\/strong><\/p>\n<pre class=\"lang:vbnet decode:true \">Private fProductName As String\r\nPublic Property ProductName() As String\r\n   Get\r\n       Return fProductName\r\n   End Get\r\n   Set(ByVal value As String)\r\n       SetPropertyValue(\"ProductName\", fProductName, Value)\r\n   End Set\r\nEnd Property<\/pre>\n<p><strong style=\"color: #252525; font-family: 'Segoe UI'; font-size: 12pt; line-height: 1.625;\">Q.<\/strong>\u00a0Why must I define a property rather than just a field (<em style=\"color: #252525; font-family: 'Segoe UI'; font-size: 12pt; line-height: 1.625;\">public string ProductName;<\/em>)? Why should I use a SetPropertyValue rather than simply setting a value to a class field?<\/p>\n<p><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\"><span style=\"background-color: white;\"><strong>A.<\/strong>\u00a0XPO expects an Object Changed notification, when a persistent property&#8217;s value is changed. For instance, this notification is used to include the modified object into the UnitOfWork&#8217;s Objects To Save collection. The\u00a0<strong>SetPropertyValue<\/strong>\u00a0method sends this notification by calling the object&#8217;s\u00a0<strong>OnChanged<\/strong>\u00a0method, while a simple field assignment does not.\u00a0<\/span><br \/>\n<span style=\"background-color: white;\"><strong>Q.<\/strong>\u00a0Why not to use a GetPropertyValue in a property&#8217;s getter?\u00a0<\/span><br \/>\n<span style=\"background-color: white;\"><strong>A.<\/strong>\u00a0Code like\u00a0<em>return fProductName;<\/em>\u00a0is faster than a call to the GetPropertyValue method. When it comes to reading persistent properties, the access time is critical, because it&#8217;s a frequent operation. Please refer to the\u00a0<\/span><\/span><span style=\"color: #043f97;\">XPO Simplified property syntax<\/span><span style=\"color: #252525;\"><span style=\"background-color: white;\">\u00a0article to learn more.<\/span><br \/>\n<span style=\"background-color: white;\"><strong>Q.\u00a0<\/strong>Should I use the GetPropertyValue\/SetPropertyValue methods for delayed properties?\u00a0<\/span><br \/>\n<span style=\"background-color: white;\"><strong>A.<\/strong>\u00a0You must use the GetDelayedPropertyValue\/SetDelayedPropertyValue or GetPropertyValue\/SetPropertyValue methods to correctly handle delayed loading.<\/span><br \/>\n<span style=\"background-color: white;\"><strong>Q.\u00a0<\/strong>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?\u00a0<\/span><br \/>\n<span style=\"background-color: white;\"><strong>A.\u00a0<\/strong>In this scenario, the following construction can be used:<br \/>\n<\/span><\/span><\/span><\/p>\n<p><strong>[C#]<\/strong><\/p>\n<pre class=\"lang:c# decode:true \">private XPDelayedProperty fPicture = new XPDelayedProperty();\r\n[Delayed(\"fPicture\", \"GroupAttributes\")]\r\npublic byte[] Picture {\r\n    get { return (byte[])fPicture.Value; }\r\n    set { fPicture.Value = value; }\r\n}<\/pre>\n<p><strong>[VB.NET]<\/strong><\/p>\n<pre class=\"lang:vbnet decode:true \">Private fPicture As New XPDelayedProperty()\r\n&lt;Delayed(\"fPicture\", \"GroupAttributes\")&gt; _\r\nPublic Property Picture() As Byte()\r\n    Get\r\n        Return CType(fPicture.Value, Byte())\r\n    End Get\r\n    Set(ByVal value As Byte())\r\n        fPicture.Value = value\r\n    End Set\r\nEnd Property<\/pre>\n<p>&nbsp;<\/p>\n<p><code>\t\t<\/code><\/p>\n<p style=\"background: white;\"><span style=\"color: #252525; font-family: Segoe UI; font-size: 12pt;\"><strong>3. Explicitly set the XpoDefault.DataLayer property in the entry point of your application.<\/strong><br \/>\n<\/span><\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[C#]<br \/>\n<\/strong><\/span><\/p>\n<pre class=\"cr-code\"><code><span class=\"cr-aspx-string\">[<\/span><span class=\"cr-aspx-string\">STAThread<\/span><span class=\"cr-aspx-string\">]<\/span>\r\n<span class=\"cr-aspx-text\">static<\/span> <span class=\"cr-aspx-text\">void<\/span> <span class=\"cr-aspx-string\">Main<\/span><span class=\"cr-aspx-string\">(<\/span><span class=\"cr-aspx-string\">)<\/span>\r\n<span class=\"cr-aspx-string\">{<\/span>\r\n   <span class=\"cr-aspx-text\">string<\/span> <span class=\"cr-aspx-string\">conn<\/span> <span class=\"cr-aspx-string\">=<\/span> <span class=\"cr-aspx-string\">AccessConnectionProvider<\/span><span class=\"cr-aspx-string\">.<\/span><span class=\"cr-aspx-string\">GetConnectionString<\/span><span class=\"cr-aspx-string\">(<\/span><span class=\"cr-aspx-tag\">@\"ApplicationData.mdb\"<\/span><span class=\"cr-aspx-string\">)<\/span><span class=\"cr-aspx-string\">;<\/span>\r\n   <span class=\"cr-aspx-string\">XpoDefault<\/span><span class=\"cr-aspx-string\">.<\/span><span class=\"cr-aspx-string\">DataLayer<\/span> <span class=\"cr-aspx-string\">=<\/span> <span class=\"cr-aspx-string\">XpoDefault<\/span><span class=\"cr-aspx-string\">.<\/span><span class=\"cr-aspx-string\">GetDataLayer<\/span><span class=\"cr-aspx-string\">(<\/span><span class=\"cr-aspx-string\">conn<\/span><span class=\"cr-aspx-string\">,<\/span> <span class=\"cr-aspx-string\">AutoCreateOption<\/span><span class=\"cr-aspx-string\">.<\/span><span class=\"cr-aspx-string\">DatabaseAndSchema<\/span><span class=\"cr-aspx-string\">)<\/span><span class=\"cr-aspx-string\">;<\/span>\r\n<span class=\"cr-aspx-string\">.<\/span><span class=\"cr-aspx-string\">.<\/span><span class=\"cr-aspx-string\">.<\/span>      \r\n<span class=\"cr-aspx-string\">}<\/span><\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[VB.NET]<br \/>\n<\/strong><\/span><\/p>\n<pre class=\"lang:vbnet decode:true \">&lt;STAThread()&gt; _\r\nShared Sub Main()\r\n   Dim conn As String = AccessConnectionProvider.GetConnectionString(\"ApplicationData.mdb\")\r\n   XpoDefault.DataLayer = XpoDefault.GetDataLayer(conn, AutoCreateOption.DatabaseAndSchema)\r\n...\r\nEnd Sub<\/pre>\n<p>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:<\/p>\n<p style=\"background: white;\"><span style=\"color: #252525; font-family: Segoe UI; font-size: 12pt;\">1. Establishing a connection to a database is a long lasting operation. It may cause performance issues.<br \/>\n2. Your application may exceed of the maximum DB connection limit.<br \/>\nWhen 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&#8217;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&#8217;s constructor as a parameter and use this Session as your needs dictate.<br \/>\n<\/span><\/p>\n<p style=\"background: white;\"><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\"><strong>4. Use a new Session \/ UnitOfWork instance to fully control loading, modifying and saving data.<\/strong>\u00a0XPO 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.\u00a0See an example here:\u00a0<\/span><span style=\"color: #043f97;\">How to view persistent objects in the XtraGrid and edit them in a separate window<\/span><span style=\"color: #252525;\">.<br \/>\n<\/span><\/span><\/p>\n<p style=\"background: white;\"><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\"><strong>5. Avoid the use of a default session.<\/strong>\u00a0The XPO default Session is accessible via the\u00a0<strong>XpoDefault.Session<\/strong>\u00a0or\u00a0<strong>Session.Default<\/strong>\u00a0static property. The default session is used internally when objects or XPCollection instances are created without a Session parameter. This may result in a\u00a0<strong>SessionMixingException<\/strong>and\/or make you write cumbersome code for reloading data (see\u00a0<\/span><span style=\"color: #043f97;\">How XPO reloads objects and collections<\/span><span style=\"color: #252525;\">). To avoid these problems, please don&#8217;t use the default session:<br \/>\n1. Set XpoDefault.Session to null (Nothing) in the entry point of your application:<br \/>\n<\/span><\/span><\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[C#]<\/strong><\/span><\/p>\n<pre class=\"cr-code\"><code><span class=\"cr-aspx-string\">XpoDefault<\/span><span class=\"cr-aspx-string\">.<\/span><span class=\"cr-aspx-string\">Session<\/span> <span class=\"cr-aspx-string\">=<\/span> <span class=\"cr-aspx-text\">null<\/span><span class=\"cr-aspx-string\">;<\/span><\/code><\/pre>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[VB.NET]<\/strong><\/span><\/p>\n<pre class=\"cr-code\"><code>XpoDefault.Session = <span class=\"cr-vb-keyword\">Nothing<\/span><\/code><\/pre>\n<p style=\"background: white;\"><span style=\"color: #252525; font-family: Segoe UI; font-size: 12pt;\">2. Remove default constructors from your persistent classes.<br \/>\n<\/span><\/p>\n<p style=\"background: white;\"><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\"><strong>6. Use a UnitOfWork rather than Session.<\/strong>\u00a0When a Session is used, and its transaction isn&#8217;t explicitly started, a persistent object is immediately persisted in the data store upon the\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">Save<\/span><span style=\"color: #252525;\">\u00a0method call. Unlike Session, UnitOfWork doesn&#8217;t persist changes until its CommitChanges method is called. Thus, the UnitOfWork gives you more control over\u00a0<em>what<\/em>\u00a0and\u00a0<em>when<\/em>\u00a0to save. See also:\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">Unit of Work<\/span><span style=\"color: #252525;\">.<br \/>\n<\/span><\/span><\/p>\n<p style=\"background: white;\"><span style=\"color: #252525; font-family: Segoe UI; font-size: 12pt;\"><strong>7. Create a separate application for database maintenance and schema updates.<\/strong>\u00a0For 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:<br \/>\n<\/span><\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[C#]<\/strong><\/span><\/p>\n<pre class=\"lang:c# decode:true \">string conn = ...;\r\nIDataLayer dl = XpoDefault.GetDataLayer(conn, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema);\r\nusing(Session session = new Session(dl)) {\r\n   System.Reflection.Assembly[] assemblies = new System.Reflection.Assembly[] {\r\n       typeof(AnyPersistentObjectFromAssemblyA).Assembly,\r\n       typeof(AnyPersistentObjectFromAssemblyB).Assembly\r\n   };\r\n   session.UpdateSchema(assemblies);\r\n   session.CreateObjectTypeRecords(assemblies);\r\n}<\/pre>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[VB.NET]<br \/>\n<\/strong><\/span><\/p>\n<pre class=\"lang:vbnet decode:true \">Dim conn As String = ...\r\nDim dl As IDataLayer = XpoDefault.GetDataLayer(conn, DevExpress.Xpo.DB.AutoCreateOption.DatabaseAndSchema)\r\nUsing session As Session = New Session(dl)\r\n   Dim assemblies As System.Reflection.Assembly() = New System.Reflection.Assembly() _\r\n       { GetType(AnyPersistentObjectFromAssemblyA).Assembly, GetType(AnyPersistentObjectFromAssemblyB).Assembly }\r\n   session.UpdateSchema(assemblies)\r\n   session.CreateObjectTypeRecords(assemblies)\r\nEnd Using<\/pre>\n<p><strong>8. Creating criteria<\/strong><\/p>\n<p style=\"background: white;\"><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\">Refer to the\u00a0<\/span><span style=\"color: #043f97;\">Creating Criteria<\/span><span style=\"color: #252525;\">\u00a0and 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 &#8220;UnitPrice&#8221; field I can use the following criteria:<br \/>\n<\/span><\/span><\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[C#]<br \/>\n<\/strong><\/span><\/p>\n<pre class=\"lang:c# decode:true \">CriteriaOperator criteria = new BinaryOperator(\"UnitPrice\", 20, BinaryOperatorType.GreaterOrEqual);\r\nCriteriaOperator criteria = new OperandProperty(\"UnitPrice\") &gt;= new OperandValue(20);\r\nCriteriaOperator criteria = CriteriaOperator.Parse(\"UnitPrice &gt;= ?\", 20);\r\nCriteriaOperator criteria = CriteriaOperator.Parse(\"UnitPrice &gt;= 20\");\r\nCriteriaOperator criteria = CriteriaOperator.Parse(string.Format(\"UnitPrice &gt;= {0}\", 20));<\/pre>\n<p><span style=\"color: #252525;\">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\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">Simplified Criteria Syntax<\/span><span style=\"color: #252525;\">\u00a0help topic and\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">this video<\/span><span style=\"color: #252525;\">. 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&#8217;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\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">criteria language syntax<\/span><span style=\"color: #252525;\">. 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.<\/span><\/p>\n<p style=\"background: white;\"><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\"><span style=\"text-decoration: underline;\"><strong>Bonus: Persistent objects etiquette for enterprise applications<\/strong><\/span><br \/>\nHere,\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">Etiquette<\/span><span style=\"color: #252525;\">\u00a0means a simple set of rules, which are not enforced, but if followed, will simplify your life significantly:<br \/>\n\u2022 Never use Session.DefaultSession for actual persistent objects.<br \/>\n\u2022 Never use an XPBaseObject if not really required. Use XPCustomObject or XPObject as base classes.<br \/>\n\u2022 Never produce side effects on a persistent property assignment. At the very least, don&#8217;t change or check directly or indirectly other persistent properties between OnLoading and OnLoad (while IsLoading == true).<br \/>\n\u2022 If you need to enforce your business rules in the properties setters or getters, always check the\u00a0<\/span><span style=\"color: #043f97;\">IsLoading<\/span><span style=\"color: #252525;\">\u00a0value:<br \/>\n<\/span><\/span><\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[C#]<br \/>\n<\/strong><\/span><\/p>\n<pre class=\"lang:c# decode:true \">private string name;\r\npublic string Name {\r\n    get { return name; }\r\n    set {\r\n        bool changed = SetPropertyValue(\"Name\", ref name, value);\r\n        if (!IsLoading &amp;&amp; changed) {\r\n            ... YourBusinessRule...\r\n        }\r\n    }\r\n}<\/pre>\n<p><strong>[VB.NET]<\/strong><\/p>\n<pre class=\"lang:vbnet decode:true \">Private _name As String\r\nPublic Property Name() As String\r\n    Get\r\n        Return _name\r\n    End Get\r\n    Set(ByVal value As String)\r\n        Dim changed As Boolean = SetPropertyValue(\"Name\", _name, value)\r\n        If (Not IsLoading) AndAlso changed Then\r\n            ...YourBusinessRule...\r\n        End If\r\n    End Set\r\nEnd Property<\/pre>\n<p>or use the following technique:<\/p>\n<p><span style=\"color: #252525; font-family: Courier New; font-size: 12pt;\"><strong>[C#]<br \/>\n<\/strong><\/span><\/p>\n<pre class=\"lang:c# decode:true \">[Persistent(\"Name\")]\r\nprivate string PersistentName {\r\n   get { return name; }\r\n   set { SetPropertyValue(\"PersistentName\", ref name, value); }\r\n}\r\n[PersistentAlias(\"PersistentName\")]\r\npublic virtual string Name {\r\n   get { return PersistentName; }\r\n   set {\r\n       DoMyBusinessTricksBeforePropertyAssignement();\r\n       PersistentName = value;\r\n       DoMyBusinessTricksAfterPropertyAssignement();\r\n   }\r\n}<\/pre>\n<p><strong>[VB.NET]<\/strong><\/p>\n<pre class=\"lang:vbnet decode:true \">&lt;Persistent(\"Name\")&gt; _\r\nPrivate Property PersistentName() As String\r\n   Get\r\n       Return name\r\n   End Get\r\n   Set(ByVal value As String)\r\n       SetPropertyValue(\"PersistentName\", name, value)\r\n   End Set\r\nEnd Property\r\n&lt;PersistentAlias(\"PersistentName\")&gt; _\r\nPublic Overridable Property Name() As String\r\n   Get\r\n       Return PersistentName\r\n   End Get\r\n   Set(ByVal value As String)\r\n       DoMyBusinessTricksBeforePropertyAssignement()\r\n       PersistentName = value\r\n       DoMyBusinessTricksAfterPropertyAssignement()\r\n   End Set\r\nEnd Property<\/pre>\n<p>\u2022 It&#8217;s a bad idea to throw exceptions inside your persistent properties. At the very least, don&#8217;t do it between OnLoading and OnLoaded (IsLoading == true).<\/p>\n<p style=\"background: white;\"><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\">\u2022 Don&#8217;t use the Session&#8217;s Save method whenever possible. Use the UnitOfWork&#8217;s CommitChanges whenever possible.<br \/>\n\u2022 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.<br \/>\n\u2022 Never do any complex work on persistent object .ctor(Session) and persistent properties assignments (at least between OnLoading and OnLoaded (until IsLoading == true)) &#8212; if you want Xpo to be effective, persistent objects must be created as quickly as possible from their Data Store images.<br \/>\n\u2022 If your algorithm requires ten persistent objects &#8212; load them all at once at the beginning of the method<br \/>\n\u2022 Never load more objects than actually needed.<br \/>\n\u2022 Use Grids and Lookups in ServerMode when appropriate.<br \/>\n\u2022 Always use IDataStore for remote scenarios.<br \/>\n\u2022 Don&#8217;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?<br \/>\n\u2022 Never use XmlWebServices until all your clients have access to assemblies provided by you. Expose IDataStore via WebService, and allow your clients to work with persistent objects.<br \/>\n\u2022 If you need to disallow your customers from changing something through remotable IDataStore &#8212; create an IDataStore wrapper, which will throw an InvalidOperationException on each call to ModifyData.<br \/>\n\u2022 Use\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">Domain Model<\/span><span style=\"color: #252525;\">\u00a0whenever possible.<br \/>\n\u2022 The OnSaving and OnDeleting methods of your persistent objects are valuable in two ways:<br \/>\n1. Custom keys generating<br \/>\n2. Last-chance checking.<br \/>\nDefense checks must not be violated in the normal workflow &#8211; if such a check fires, it means something is bad with your program.<br \/>\nPlease 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 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\u00a0<\/span><span style=\"color: #043f97;\">How to generate a sequential and user-friendly identifier field within an XPO business class<\/span><span style=\"color: #252525;\">\u00a0article for some example code.\u00a0Alternatively, you can move your logic directly into dependent property setters. Refer to the\u00a0<\/span><span style=\"color: #043f97;\">Task-Based Help<\/span><span style=\"color: #252525;\">\u00a0&gt;\u00a0<\/span><span style=\"color: #043f97;\">How to: Calculate a Property Value Based on Values from a Detail Collection<\/span><span style=\"color: #252525;\">\u00a0article for some example code as well.<br \/>\n\u2022 Don&#8217;t mix the process of objects saving\/persisting and business logic (including business rules validation) &#8212; this is a\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">code smell<\/span><span style=\"color: #252525;\">\u00a0of the\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">Transaction Script pattern<\/span><span style=\"color: #252525;\">. 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.<br \/>\n\u2022 Don&#8217;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.<br \/>\n\u2022 Don&#8217;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(&#8220;Male&#8221;) and Gender(&#8220;Female&#8221;) it&#8217;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 &#8212; make the collection property private or protected, and return null from it (undocumented and unsupported but a working and useful feature).<br \/>\n\u2022 If your class structure is highly coupled and it&#8217;s actually possible to raise a complete database accessing the single object\/property, break this net using<\/span><span style=\"color: #043f97; text-decoration: underline;\">delayed loading<\/span><span style=\"color: #252525;\">.<br \/>\n\u2022 Don&#8217;t use Delayed loading if not really needed.<br \/>\n\u2022 Don&#8217;t make your code dependent on the order of records returned by the XPCollection\/XPView\/XPCursor objects, unless you explicitly\u00a0<\/span><span style=\"color: #043f97; text-decoration: underline;\">sorted<\/span><span style=\"color: #252525;\">\u00a0them. 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.<br \/>\n<\/span><\/span><\/p>\n<p style=\"background: white;\"><span style=\"font-family: Segoe UI; font-size: 12pt;\"><span style=\"color: #252525;\"><strong>See Also:<\/strong><br \/>\n<\/span><span style=\"color: #043f97; text-decoration: underline;\">XPO Fundamentals<\/span><span style=\"color: #252525;\"><br \/>\n<\/span><span style=\"color: #043f97;\">K18061<\/span><span style=\"color: #252525;\"><br \/>\n<\/span><span style=\"color: #043f97;\">XPO Worst Practices<\/span><span style=\"color: #252525;\"><br \/>\n<\/span><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u6765\u6e90\uff1ahttps:\/\/www.devexpress.com\/Support\/Center\/Question\/D &hellip; <a href=\"http:\/\/www.ilezhi.cn\/?p=100279\" class=\"more-link\">\u7ee7\u7eed\u9605\u8bfb<span class=\"screen-reader-text\">XPO \u6700\u4f73\u5b9e\u8df5\uff08XPO Best Practices\uff09<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-100279","post","type-post","status-publish","format-standard","hentry","category-wordpress"],"_links":{"self":[{"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=\/wp\/v2\/posts\/100279","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=100279"}],"version-history":[{"count":0,"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=\/wp\/v2\/posts\/100279\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=100279"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=100279"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.ilezhi.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=100279"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}