Developing Database Applications with an O-R Mapping Framework Part 1

Developing Database Applications with an O-R Mapping Framework Part 1

Download this article in Word format

Aaron Lau
September 2004

Applied to:
Microsoft .NET Framework 1.1
Microsoft SQL Server 2000

** Summary: ** this is the part one of a two-part article which introduces how to develop database applications with an O-R (Object-Relational) mapping framework. The first part introduces an O-R mapping framework called DAP (Data Access Process) Framework; and the second part introduces an O-R mapping tool called SuperType which is used to generate from database schemas files of business types and configurations that will be used as the mete-data by the DAP Framework. At the time this article was written, the latest version of the DAP Framework and the SuperType is 2.0.

Content

Introduction

Design of the DAP Framework 2.0

Developing with the DAP Framework 2.0

Deployment and Operations

Conclusion

Introduction

Multi-Layered architecture has for a long time been proved to be an effective way to build reliable, maintainable and flexible enterprise applications. Originally, we had tree layers in the applications we built, they are Presentation (UI) layer, BL (Business Logic) layer and Data layer. As we proceeded, we found that another two layers are needed. One is User Interface Process layer between UI and BL layers, and the three together work as the MVC pattern which is a very good approach that can effectively separate UI and BL layers; another is Data Access layer that exists between BL and Data layers, and that Data Access layer is what the DAP Framework is all about.

The DAP Framework is an O-R mapping framework for the .NET enterprise development. It can assist you with developing flexible and extensible Multi-Layered Windows and Web applications which involve relational databases development. It can be used with SuperType, an O-R mapping tool, together to build intricate enterprise applications in a record time. As an O-R mapping framework, the DAP Framework has the following features, and all of them will be described in detail throughout the rest of this article.

Materialization and dematerialization of a single object

Materialization and dematerialization of a collection of objects

Paging is enabled

Object state management

Cache is enabled

Distributed transaction

Multi-User access and locking strategies

Who Should Read This Article

This guide is targeted at software architects who has already been familiar with architecture design and can use this framework as an alternative way to realize the O-R mapping, or developers who is interested Data Access layer design and can get a first view of what the O-R mapping is primarily about.

In order to fully benefit from this guide, potential readers should have a good understanding of the object-oriented technology and relational databases, such as SQL-Server, basic knowledge of OOD (object-oriented design) and Multi-Layered development methodology. The specific technologies mentioned in this article are C#, the .NET Framework, and XML.

Why O-R mapping?

Because of the prevalence and maturity of the relational databases, they are widely used in the database applications. A number of problems, however, arose due to the mismatch between the record-oriented and the object-oriented representations of data. And that’s the main reason why O-R mapping was introduced as a solution.

What Is an O-R mapping anyway?

Before you go any further, you need to get familiar with the following key ideas, and in the sections to come, each of them is to be described in detail. How they can be achieved and how we should employ them in the real-world enterprise applications will also be introduced.

Mapping: There must be some mapping between a class and its persistence store (for example, a table in a database), and between object attributes and the fields (columns) in a record. That is, there must be a schema mapping between the two schemas.

Materialization and dematerialization: Materialization is the act of transforming a non-object representation of data (for example, a record) from a persistence store into an object. Dematerialization is the opposite activity (also known as passivation).

Caches: Persistence services cache materialized objects for performance.

Transaction state of object: It is useful to know the state of an object in terms of its relationship to the current transaction. For example, it is useful to know whether an object has been modified (is dirty) so that it is possible to determine if it need to be saved back to its persistent store.

Transaction operations: Commit and rollback operations.

Lazy materialization: Not all objects are materialized at once; a particular instance is only materialized on-demand, when needed.

Now, you may be able to figure out what an O-R mapping is.
O-R mapping is a mechanism that can map an object to a record in a relational database table and map that object’s attributes to the fields of that record, and unique values are used to relate that object to that record.

Design of the DAP Framework 2.0

To make the most of the DAP Framework, you need to understand the design of it. The DAP Framework was built with C# and designed to be an extensible and reusable package that can be freely deployed in any project developed with the programming languages that conform to Common Language Specification (CLS). The DAP Framework works in Multi-Layered architecture as an O-R mapping framework through which records in database table can be easily mapped to objects, which also provide functionalities of object caching, object states management, distributed transaction, and etc.

How Does an O-R Mapping Work in the DAP Framework?

In spite of knowing what an O-R mapping is, some of you, especially those who are new in this area, may still be puzzled by how it works. What’s the secret behind an O-R mapping framework? To begin, let’s make a comparison between the following two diagrams in figure 1.


Figure 1. Comparison between a RDB table and a class

On the left side is a UML class diagram. Instances of the Project class are objects we work with in Business Logic layer. There are 4 fields that represent the attributes of the BO (Business Object) Project in the real world. On the right side, it is a RDB table diagram. It also has 4 fields which are used to save the attributes of Project objects, e.g. id, name, number and cost. Instances of this table schema are records that hold many projects, so we can see records in RDB table as the persistent format of BOs. Let’s proceed to see what the C# code of Project class looks like.

public class Project
{
    private Guid id;
    private string name;
    private string number;
    private decimal cost;

    public Project(Guid id, string name, string number, decimal cost)
    {
        this.id = id;
        this.name = name;
        this.number = number;
        this.cost = cost;
    }

    public Guid ID
    {
        get
        {
            return this.id;
        }
        set
        {
            this.id = value;
        }
    }

    public string Name
    {
        get
        {
            return this.name;
        }
        set
        {
            this.name = value;
        }
    }

    public string Number
    {
        get
        {
            return this.number;
        }
        set
        {
            this.number = value;
        }
    }

    public decimal Cost
    {
        get
        {
            return this.cost;
        }
        set
        {
            this.cost = value;
        }
    }
}

In this Project class, there are 4 fields and 4 properties each of which corresponds to one field, so we can now map these 4 properties to those 4 fields in that RDB table in figure 2.1.

Ordinarily, there are three ways to implement an O-R mapping framework.

One way is to write some database access methods, such as Select , Save , etc. in Project class itself. This approach is convenient, for Project object has the necessary information for itself to be saved to or retrieved from database. The logical extension of this approach is that each business class has it own methods to access to database. But this leads to problems in cohesion, coupling, and duplication. For example, the Project class must now contains logic related to database handling. This class is no longer focused on just the pure application logic of “being a project”, for it now has other kinds of responsibilities, which lowers its cohesion. The class must be coupled to the database service component, such as ADO.NET, rather than just being coupled to other objects in the domain layer of software objects, which raises its coupling. And it is likely that similar database logic would be duplicated in many persistent classes.

Another way is that we can use another class, such as ProjectORMapper , as the mapper between Project objects and RDB records. In this way, Project class works as an Information Expert which means an individual has necessary information to fulfill a task. Database handling was removed to a separate class, called O-R mapper class such as ProjectORMapper , where Project objects are selected from and updated to the database. Thus some OOD principles , such as High Cohesion, Low Coupling, are conserved. But the issue of code duplication remains unresolved. Even though we move database handling code into another class, there are still possibilities that duplicated database logic would exist through out many classes, since every business class has its own O-R mapper and each O-R mapper has it own code to access a database.

So we come to the third way. We can use only one O-R mapper and an XML file recording the mete-data of different classes, then this mapper can read the mete-data to map different kinds of objects to records in RDB. In this way, those three OOD issues, cohesion, coupling, and code duplication are resolved. An O-R mapping framework is responsible of mapping different objects to RDB records. The mete-data file is essential to the framework, for it provides the knowledge of how a specific object can be mapped to a RDB record. Actually, with an O-R mapping framework, we don’t have to care about SQL statements and ADO.NET, because all these things are handled by the framework. SQL statements are generated from the mete-data automatically. This third way is what the DAP Framework 2.0 use to implementan O-R mapping framework.

Architecture Overview

Figure 2. illustrates the overall architecture of the DAP Framework.


Figure 2. The DAP Framework 2.0 architecture

There are two parts in that figure. Panes in olivedrab represent the components in the DAP Framework. They are the essential components that work together to realize the mapping between objects and RDB records. What are in turquoise color indicate a configuration file and code that will access this framework. The configuration file is extremely important to the DAP Framework, because it contains the necessary mete-data that the DAP framework will use to map objects to RDB records; and code in BL layer will use this framework to select objects from or update objects to Data layer.

Persistence Facade

Primarily because the DAP Framework is an independent layer in Multi-Layered system architecture, it needs to provide a facade, a common pattern to provide a unified interface to a subsystem, that upper layers can use consistently to access the Data layer underneath.

Figure 3 is the UML diagram of the PersistenceFacade class which works as the facade of the DAP Framework. Its primary responsibilities are retrieving objects from and updating objects to the Data layer. This class is a Singleton class, which means there is only one instance of this class exists throughout the application domain. You might be confused by the methods in the class diagram, for there are definitely no methods called Insert and Update , how objects can be inserted or updated to the database. Actually there are indeed some methods called Insert and Update , your can not seeing them is just because they are marked as internal . You can not invoke them outside the framework assembly. Every class who wants their instances to be mapped to RDB records must inherits from the PersistentObject class which provides some methods, such as Save or Delete , that will internally invoke the corresponding methods in this PersistenceFacade class. This is the internal logic of the framework, you don't have to care about it while you are using it.


**Figure 3. UML diagram of the PersistenceFacade class **

Notice that when you use Select method to retrieve object from the data layer, only one object will be returned, even if there are many records in the database are true of the passed conditions, and that one will be the first in the group. By contrast, when you use SelectCollection method, a collection will be retrieved, even though only one record in the database is true of the conditions passed, and it will be the only one element in the returned collection.

O-R Mapper

This component is the exact place where mapping between objects and records is realized. With the helps from other two components, Sql Factory and SqlParameter Factory , it knows how objects can be retrieved from databases and how those objects can be subsequently updated into databases. Reflection is used in this component to dematerialize objects to databases. An application block from Microsoft, called Data Access Block or SqlHelper , is used in this component. It wraps all ADO.NET database commands.

Sql Factory

This component works as the factory that's used to generate SQL statements. For the performance reason, SQL statements generated from this factory will be cached, so no need to generate a new SQL statement if it has already been requested. This component internally calls other factories for getting the mete-data from the configuration file.

SqlParameter Factory

This components is used to create SqlParameter objects which will be passed as parameters to ADO.NET commands. This component will internally call other factories for getting the mete-data from the configuration file.

Config

This component is used to read the mete-data from the configuration file . And at the very beginning, the format of the configuration will be validated against a predefined schema validation file . This is to make sure the format of the configuration is correct, not incomplete. If the validation fails, a meaningful exception will be thrown to inform developers what the problem is. Thus, less codes are needed to prevent the system built on the framework from being cracked down.

Configuration File

There is a heavy use of configuration file in the DAP Framework. The configuration file contains the mete-data that will be used dynamically at run time. Following is a sample configuration file.

 1<dapconfiguration>
 2<cachepolicies>
 3<cachepolicy cacheexpirationinterval="23:59:59" cacheexpirationmode="Absolute" name="Absolute"></cachepolicy>
 4</cachepolicies>
 5<databases>
 6<database constring="Initial Catalog=pubs;Data Source=localhost;User ID=sa;Password=pwd" name="pubs"></database>
 7</databases>
 8<types>
 9<type cachepolicy="Absolute" database="pubs" name="Author" table="authors">
10<properties>
11<property dbfield="au_id" field="id" isprimary="true" name="ID" type="String"></property>
12<property dbfield="au_lname" field="lastName" name="LastName" type="String"></property>
13<property dbfield="au_fname" field="firstName" name="FirstName" type="String"></property>
14<property dbfield="phone" field="phone" name="Phone" type="String"></property>
15<property dbfield="address" field="address" name="Address" type="String"></property>
16<property dbfield="city" field="city" name="City" type="String"></property>
17<property dbfield="state" field="state" name="State" type="String"></property>
18<property dbfield="zip" field="zip" name="Zip" type="String"></property>
19<property dbfield="contract" field="contract" name="Contract" type="Boolean"></property>
20</properties>
21</type>
22</types>
23</dapconfiguration>

**

  1<cachepolicies> ** that contains one or more child tags, each of which represents a cache policy that will be used to cache objects retrieved from databases. Each <cachepolicy> tag has three attributes. _name_ is used to uniquely represent a cache policy, and a type can be related to a cache policy through this attribute. _cacheExpirationMode_ will be used with _cacheExpirationInterval_ together to indicate how long objects should be cached. There are following examples: 
  2    
  3    
  4      <cachepolicy cacheexpirationinterval="23:59:59" cacheexpirationmode="Absolute" name="Absolute"></cachepolicy>
  5
  6Cached objects with this cache policy will expire at _23:59:59_ every day. 
  7    
  8    
  9      <cachepolicy cacheexpirationinterval="20" cacheexpirationmode="Sliding" name="Sliding"></cachepolicy>
 10
 11Cached objects with this cache policy will expire in 20 minutes. 
 12    
 13    
 14      <cachepolicy cacheexpirationmode="None" name="None"></cachepolicy>
 15
 16Cached objects with this cache policy will never expire. 
 17
 18**NOTE** that if _Absolute_ is used as the cache expiration mode, a time-format value, e.g. _23:59:59_ , should be assigned to the cache expiration interval attribute. But if _Sliding_ is used as the cache expiration mode, an integer value should be assigned to the cache expiration interval attribute. No need to assign the cache expiration interval attribute if _None_ is used as the cache expiration mode. 
 19
 20** <databases> ** that contains one or more child tags, each of which represents a relational database that will be used. Each <database> tag has two attributes. _name_ is used to uniquely represent a database, and a type can be related to a database through this attribute. _conString_ is used to save the connection string of the database. 
 21
 22** <types> ** that contains one or more child tags, each of which represents a business type or business class. Each <type> tag has four attributes and one child tag. _name_ is used to indicate the name of the type, it is actually the class name. _database_ is used to relate this type to a database specified in the <database> tag. _table_ is used to indicate which table in the specified database is mapped to this type. _cachePolicy_ is used to relate this type to a cache policy specified in the <cachepolicy> tag. This <type> tag has a child tag <properties> which also contains many child tags, each of which represents a property of this type. Each <property> tag can have at most five attributes. _name_ is used to indicate the property name. _field_ is used to indicate the name of the class field that this property will internally access. _dbField_ is used to indicate the name of the field in the specified database table, and this database field is mapped to the class field just mentioned. _type_ is used to indicate the property type, and it is also the type of the class field. This type will be converted to its counterpart type in the database at run time. The last attribute is _isPrimary_ . If the database field just mentioned is the primary key in its table, this attribute will be set to true, otherwise, false.   
 23  
 24**NOTE** that the sequence of the properties MUST be the same as that of the parameters in a constructor of the type. 
 25
 26That is all about the configuration file. Actually, you don't have to code this file manually, SuperType, an O-R mapping tool, can be used to generate this file from database automatically. 
 27
 28###  PersistenObject 
 29
 30In the DAP Framework, each business class who wants its instances to be mapped to RDB records must inherit from the _PersistentObject_ class which provides many implementations that derived class can use. Figure 4 is the UML diagram of the _PersistentObject_ class. 
 31
 32![](http://218.106.186.67:8004/typedev/Images/PersistentObject.gif)   
 33**Figure 4. UML diagram of the _PersistentObject_ class **
 34
 35###  States Management 
 36
 37The DAP Framework adopts the  State  pattern and each persistent object has its own state. There are in all five states: 
 38
 39**New State** which means a persistent object is new, not retrieved from the database.   
 40  
 41**OldClean State** which means a persistent object was retrieved from the database, but without any operation to it.   
 42  
 43**OldDirty State** which means a persistent object was retrieved from the database and has been modified.   
 44  
 45**OldDelete State** which means a persistent object was retrieved from the database and will be deleted.   
 46  
 47**Deleted State** which means a persistent object has already been deleted from the database.   
 48  
 49
 50
 51Figure 5 shows the state chart for a persistent object. From the state chart, you may know that the response to an operation depends on the state of the persistent object. If a persistent object is selected from a database, its state is _OldClean_ , and after its saved, its state transforms to _OldDirty_ , then its committed, at this time, its updated to the database, and its state goes back to _OldClean_ . If this persistent object is totally new, nothing will happen if its saved, and its state remains the same, then it's committed, at this time, it is inserted into the database, and its state is changed to _OldClean_ . 
 52
 53![](http://218.106.186.67:8004/typedev/Images/StateChart.gif)   
 54**Figure 5. State chart of a persistent object**
 55
 56In the DAP Framework, a persistent object created explicitly by its constructor has the default state _New_ ; and a persistent object retrieved from the database has the default state _OldClean_ . 
 57
 58###  Overall 
 59
 60The DAP Framework provides developers the ability to clearly separate Data layer and Business Logic layer. Developers have a reasonable and convenient way to retrieve objects from and update objects to the Data layer through the facade of the framework. The framework can work with  SuperType  , an O-R mapping tool, to develop intricate enterprise application in a record time. 
 61
 62##  Developing with the DAP Framework 2.0 
 63
 64###  Feature List of the DAP Framework 2.0 
 65
 66The DAP Framework 2.0 has the following major features.   
 67  
 68Materialization and dematerialization of a single object    
 69  
 70Materialization and dematerialization of a collection of objects    
 71  
 72Paging is enabled    
 73  
 74Object state management    
 75  
 76Cache is enabled    
 77  
 78Distributed transaction    
 79  
 80Multi-User access and locking strategies 
 81
 82###  A Sample Class and a Sample Configuration File 
 83
 84In order to demonstrate the features of the DAP Framework, we need to at first have a sample class and a sample configuration file.   
 85  
 86Following is a class _Author_ , and the database table this class mapping to is a sample table called _authors_ in the public database called _pubs_ in the SQL-Server 2000. 
 87    
 88    
 89    using System;
 90    using TypeDev.DAProcess;
 91    
 92    public sealed class Author : PersistentObject
 93    {
 94        private string id;
 95        private string lastName;
 96        private string firstName;
 97        private string phone;
 98        private string address;
 99        private string city;
100        private string state;
101        private string zip;
102        private bool contract;
103    
104        public Author()
105        {
106        }
107    
108        public Author(string id, string lastName, string firstName, string phone, string address, string city, string state, string zip, bool contract)
109        {
110            this.id = id;
111            this.lastName = lastName;
112            this.firstName = firstName;
113            this.phone = phone;
114            this.address = address;
115            this.city = city;
116            this.state = state;
117            this.zip = zip;
118            this.contract = contract;
119        }
120    
121        public string ID
122        {
123            get
124            {
125                return this.id;
126            }
127        }
128    
129        public string LastName
130        {
131            get
132            {
133                return this.lastName;
134            }
135            set
136            {
137                this.lastName = value;
138            }
139        }
140    
141        public string FirstName
142        {
143            get
144            {
145                return this.firstName;
146            }
147            set
148            {
149                this.firstName = value;
150            }
151        }
152        
153        public string Phone
154        {
155            get
156            {
157                return this.phone;
158            }
159            set
160            {
161                this.phone = value;
162            }
163        }
164    
165        public string Address
166        {
167            get
168            {
169                return this.address;
170            }
171            set
172            {
173                this.address = value;
174            }
175        }
176    
177        public string City
178        {
179            get
180            {
181                return this.city;
182            }
183            set
184            {
185                this.city = value;
186            }
187        }
188    
189        public string State
190        {
191            get
192            {
193                return this.state;
194            }
195            set
196            {
197                this.state = value;
198            }
199        }
200    
201        public string Zip
202        {
203            get
204            {
205                return this.zip;
206            }
207            set
208            {
209                this.zip = value;
210            }
211        }
212    
213        public bool Contract
214        {
215            get
216            {
217                return this.contract;
218            }
219            set
220            {
221                this.contract = value;
222            }
223        }
224    }
225
226Following is a configuration file which has already been introduced. 
227    
228    
229    <?xml version="1.0" encoding="utf-8"?>
230<dapconfiguration>
231<cachepolicies>
232<cachepolicy cacheexpirationinterval="23:59:59" cacheexpirationmode="Absolute" name="Absolute"></cachepolicy>
233</cachepolicies>
234<databases>
235<database constring="Initial Catalog=pubs;Data Source=localhost;User ID=sa;Password=pwd" name="pubs"></database>
236</databases>
237<types>
238<type cachepolicy="Absolute" database="pubs" name="Author" table="authors">
239<properties>
240<property dbfield="au_id" field="id" isprimary="true" name="ID" type="String"></property>
241<property dbfield="au_lname" field="lastName" name="LastName" type="String"></property>
242<property dbfield="au_fname" field="firstName" name="FirstName" type="String"></property>
243<property dbfield="phone" field="phone" name="Phone" type="String"></property>
244<property dbfield="address" field="address" name="Address" type="String"></property>
245<property dbfield="city" field="city" name="City" type="String"></property>
246<property dbfield="state" field="state" name="State" type="String"></property>
247<property dbfield="zip" field="zip" name="Zip" type="String"></property>
248<property dbfield="contract" field="contract" name="Contract" type="Boolean"></property>
249</properties>
250</type>
251</types>
252</dapconfiguration>
253
254**NOTE** that if the application you are building is a Windows application, this configuration file should be placed into the folder where the _EXE_ file is; if you are building an ASP.NET Web application, this configuration file should be placed into the root folder of the Web application, and its extension should changed to _config_ to prevent it from being access through client browsers. Also, the name of this configuration file should be configured in the application configuration file, _app.config_ or _web.config_ , as the following way: 
255    
256    
257    <configuration>
258      ...
259      <appsettings>
260        ...
261        <add key="ConfigFileName" value="configFileName.config"></add>
262        ...
263      </appsettings>
264      ...
265    </configuration>
266
267###  Materialization and Dematerialization of a Single Object 
268
269The following code snippet first materializes an _Author_ instance based on the property _ID_ and its value passed to the database, then the first name of this author is changed to Aaron, and at last, this author is dematerialized to the database. 
270    
271    
272    TypeParameter param = new TypeParameter(typeof(Author), "ID", "172-32-1176");
273    Author author = PersistenceFacade.Instance.Select(param) as Author;
274    if(author != null)
275    {
276         author.FirstName = "Aaron";
277         author.Save();
278         author.Commit();
279    }
280
281**NOTE** that if there are more than one records in the database are true of the passed condition, only the first one will be converted into an object and returned. 
282
283###  Materialization and Dematerialization of a Collection of Objects 
284
285The following code is used to materialize a collection of _Author_ instances. All authors whose first name contains the letter a will be returned. Subsequently their phone numbers are changed, and then they are dematerialized to the database. 
286    
287    
288    TypeParameter param = new TypeParameter(typeof(Author), "FirstName", "%a%", Connector.Like);
289    ArrayList authors = PersistenceFacade.Instance.SelectCollection(param);
290    foreach(Author author in authors)
291    {
292         author.Phone = "13366676543";
293         author.Save();
294         author.Commit();
295    }
296
297**NOTE** that if there is no record in the database is true of the passed condition, the returned _ArrayList_ will contain no author, but the list itself has already been instantiated. 
298
299The folowing code demonstrates how to retrieve those authors whose first name contains the letter "a", and also they must live in the state CA. 
300    
301    
302    TypeParameter param = new TypeParameter(typeof(Author));
303    param.PropertyParams.Add(new PropertyParameter("FirstName", "%a%", Connector.Like));
304    param.PropertyParams.Add(new PropertyParameter("State", "CA"));
305    ArrayList authors = PersistenceFacade.Instance.SelectCollection(param);
306    foreach(Author author in authors)
307    {
308         //Code here to operate every author.
309    }
310
311The folowing code demonstrates how to retrieve those authors whose first name or last name contains letter "a". 
312    
313    
314    TypeParameter param = new TypeParameter(typeof(Author));
315    param.PropertyParams.Add(new PropertyParameter("FirstName", "%a%", Connector.Like));
316    param.PropertyParams.Add(new PropertyParameter("LastName", "%a%", Connector.Like));
317    param.IsOr = true;
318    ArrayList authors = PersistenceFacade.Instance.SelectCollection(param);
319    foreach(Author author in authors)
320    {
321         //Code here to operate every author.
322    }
323
324###  Paging Is Enabled 
325
326Paging is enabled in the DAP Framework 2.0. The following code demonstrate how to separately retrieve the first ten authors and the second ten authors. 
327    
328    
329    TypeParameter param = new TypeParameter(typeof(Author));
330    param.PropertyParams.Add(new PropertyParameter("FirstName", "%a%", Connector.Like));
331    param.PropertyParams.Add(new PropertyParameter("LastName", "%a%", Connector.Like));
332    param.IsOr = true;
333    param.PageEnabled = true;
334    param.CountInPage = 10;
335    
336    //The first ten authors are retrieved. 
337    param.PageIndex = 0;
338    ArrayList authors = PersistenceFacade.Instance.SelectCollection(param);
339    foreach(Author author in authors)
340    {
341         //Code here to operate every author.
342    }
343    
344    //The second ten authors are retrieved. 
345    param.PageIndex = 1;
346    authors = PersistenceFacade.Instance.SelectCollection(param);
347    foreach(Author author in authors)
348    {
349         //Code here to operate every author.
350    }
351
352###  Object State Management 
353
354In the following code, there are two authors, _author1_ and _author2_ . One is constructed explicitly using the constructor, and the other is retrieved from the database. When both methods, _Save_ and _Commit_ , are invoked on these two object, the first one, _author1_ , is inserted to the database, the second one, _author2_ however, is updated into the database. How could that happened? I mean how the framework is able to know which object should be inserted into the database and which object should be updated to the database. That's because these two _Author_ objects have their states respectively. The _Author_ object instantiated using the constructor has the default state _New_ , and the _Author_ object retrieved from the database has the default state _OldClean_ , so the framework knows how to separately handle these two _Author_ objects. 
355    
356    
357    Author author1 = new Author("888-88-8888", "Aaron", "Lau", "13366", "Beijing", "Beijing", "BJ", "10000", true);
358    author1.Save();
359    author1.Commit();
360    
361    TypeParameter param = new TypeParameter(typeof(Author), "ID", "172-32-1176");
362    Author author2 = PersistenceFacade.Instance.Select(param) as Author;
363    if(author != null)
364    {
365         author2.FirstName = "Aaron";
366         author2.Save();
367         author2.Commit();
368    }
369
370The following code shows that an _Author_ object is retrieved from the database, and it is subsequently committed without any operation on that object. In this case, that object is not updated to the database, actually, no database access happens. Because the framework knows that the object's state is _OldClean_ , no operation happened on it. For more information about the state management, please review the figure 5 _State chart of a persistent object_ . 
371    
372    
373    TypeParameter param = new TypeParameter(typeof(Author), "ID", "172-32-1176");
374    Author author = PersistenceFacade.Instance.Select(param) as Author;
375    if(author != null)
376    {
377         author.Commit();
378    }
379
380###  Cache Is Enabled 
381
382Objects retrieved from the database can be cached by the framework automatically. If some kind of objects are requested frequently, they can be cached to promote system performance. In order to cache objects of a specified type, you should configured that type in the configuration file. Take the _Author_ type for example. Looking at the _Author_ tag in the sample configuration file, you can see that an attribute called _cachePolicy_ has the value _Absolute_ , the name of a cache policy, and that cache policy, configured in the <cachepolicy> tag, tells that cached objects with this cache policy will expire at _23:59:59_ every day.   
383  
384Looking at the following code snippet, if objects of _Author_ type are configured to be cached, that _equal_ boolean value will finally be true, whichh means the object _author1_ has a reference equality with one _Author_ object in the collection that is retrieved at the second time with the same query parameter. 
385    
386    
387    TypeParameter param = new TypeParameter(typeof(Author), "FirstName", "%a%", Connector.Like);
388    Author author1 = PersistenceFacade.Instance.Select(param) as Author;
389    ArrayList authors = PersistenceFacade.Instance.SelectCollection(param);
390    bool equal = false;
391    foreach(Author author in authors)
392    {
393        if(object.ReferenceEquals(author1, author))
394        {
395             equal = true;
396        }
397    }
398
399The same result can be illustrated by the following code, which means that if an author is requested several times, the same cached author will always be returned no matter which query parameter is used. 
400    
401    
402    TypeParameter param1 = new TypeParameter(typeof(Author), "FirstName", "%a%", Connector.Like);
403    Author author1 = PersistenceFacade.Instance.Select(param1) as Author;
404    TypeParameter param2 = new TypeParameter(typeof(Author), "ID", author1.ID);
405    Author author2 = PersistenceFacade.Instance.Select(param2) as Author;
406    bool equal = object.ReferenceEquals(author1, author2);
407
408###  Distributed Transaction 
409
410If transaction were not supported by an O-R mapping framework, developers using it would finally come to a place where something is a mission impossible. Fortunately, distributed transaction is realized in the DAP Framework 2.0 in a reasonable and seemingless way.   
411  
412Suppose that we had another type called _Book_ . The following code shows how an _Author_ object and a _Book_ object are inserted into the database in a transaction. 
413    
414    
415    Author author = new Author("888-88-8888", "Aaron", "Lau", "13366", "Beijing", "Beijing", "BJ", "10000", true);
416    Book book = new Book("bookName");
417    author.Save();
418    book.Save();
419    Transaction trans = new Transaction();
420    trans.AddObject(author);
421    trans.AddObject(book);
422    try
423    {
424         trans.Commit();
425    }
426    catch
427    {
428         trans.Rollback();
429         throw;
430    }
431    finally
432    {
433         trans.Dispose();
434    }
435
436**NOTE** that the sequence that objects are added into the transaction is the sequence that those objects are going to be committed into databases, so objects MUST be added into the transaction in a reasonable sequence to prevent exceptions from being thrown. 
437
438###  Multi-User Access and Locking Strategies 
439
440If the DAP Framework 2.0 is being used in an application that is running on a server, there might be the cases when many users will access this framework simultaneously. This concern was considered the first time this framework was designed. For example, there can be only one transaction being executed at a certain time while the framework is running to prevent databases from being locked; if objects of a specified type are configured to be cached, selecting those objects will be synchronized to prevent duplicated objects from being instantiated. 
441
442###  Pet Shop Built on the DAP Framework 2.0 
443
444The  .NET Pet Shop  was originally built by Microsoft as a sample application for .NET developers, and therefore many developers are so familiar with it, some of them even looked into the code of it. This is the main reason why it's been chosen as the sample application built on the DAP Framework. Actually only two days were used to rebuild this application, partially because that the UI and database remain changed, and partially because it is so straightforward to build database applications on the DAP Framework. In this newly created  Pet Shop  , new Data Access layer, Business Logic layer and controller were used, and almost all features the framework has have been used in it. I personally think that the new Business Logic layer is more reasonable than that of the original one, for it presents clearer associations among different classes. Figure 6 shows the architecture of this new Pet Shop. 
445
446![](http://218.106.186.67:8004/typedev/Images/PetShopArchitecture.gif)   
447**Figure 6. Pet Shop architecture**
448
449**UI and Data layers** remain unchanged. In UI layer, there exist many ASP.NET web forms and some user and custom controls. And Data layer is the SQL-Server 2000 database.   
450  
451**Controller** is a class with only static methods which are used to communicate between UI layer and Business Logic layer. Users log in the system through this controller; account, product and order information are saved and retrieved through this controller; session and cache state are maintained through this controller.   
452  
453**Business Logic layer** mainly involves business objects and associations among them. It is the place where objects work together to get their job done.   
454  
455**Data Access layer** is where the DAP Framework is. Its used to retrieve objects from or update objects to the Data layer underneath. 
456
457##  Deployment and Operations 
458
459###  Software Requirements 
460
461This framework package has been applied in several real-world projects, and it has been fully tested in the following software environment, so you need to make sure that your system meets the following minimum software requirements: 
462
463Microsoft Windows 2000 / XP / 2003.   
464  
465Microsoft .NET Framework version 1.1.   
466  
467Microsoft SQL-Server 2000.   
468  
469Microsoft Application Data Block 2.0. 
470
471###  Database Restriction 
472
473Database tables should not have fields that are auto-incremental, all fields must be inserted explicitly. 
474
475##  Conclusion 
476
477A major advantage of adopting an O-R mapping framework is that it can minimize the complexity of systems and thus help developers build real OO applications. Developers dont have to care about how to write SQL statements, they can even have no knowledge of what ADO.NET is, they primarily work on how objects can work together to get something done. 
478
4791\.  The Information Expert is a pattern which likes many things in object technology has a real-world analogy. We commonly give responsibility to individuals who have the information necessary to fulfill a task. For example, in a business, who should be responsible for creating a profit-and-loss statement? The person who has access to all the information necessary to create it  perhaps the chief financial officer. And just as software objects collaborate because the information is spread around, so it is with people. The company's chief financial officer may ask accountants to generate reports on credits and debits.   
480  
4812\.  Practically, there are three principles with OOD. They are High Cohesion, Low Coupling and Protected Variations. More information about these principles can be found from Applying UML and Patterns, an excellent book by Craig Larman.   
482  
4833\.  Singleton is one of the GOF patterns, more information can be found from _Design Patterns Elements of Reusable Object-Oriented Software_ .   
484  
4854\.  State is one of the GOF patterns, more information can be found from _Design Patterns Elements of Reusable Object-Oriented Software_ .   
486  
487---</cachepolicy></property></properties></type></cachepolicy></database></type></types></database></databases></cachepolicy></cachepolicies>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus