1. Trang chủ
  2. » Công Nghệ Thông Tin

Data Access Layer

22 472 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Data access layer
Thể loại Book chapter
Định dạng
Số trang 22
Dung lượng 644,92 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

It is an example of a many-to-many relationship and contrasts to the to-many relationship between orders and customers an order belongs to a single customer, but acustomer can create mul

Trang 1

Data Access Layer

Silverlight and some WPF applications do not use offline storage for persisting data Instead, they use a relational database to store the object data in a structured format Rather than employing serialization, applications that use a relational database for storage will have a dedicated Data Access Layer (DAL) that serves to insulate the model from additional responsibility while allowing clients to load and save the

model state

There are four typical operations that the DAL should provide for each object in the model: create, read, update, and delete (often given the unfortunate acronym CRUD) These operations correspond to inserting new records in the database, retrieving records from the database, editing existing records, and removing records entirely from the database, respectively Clients can combine these simple operations

to fulfill all of the requirements necessary to interact with application data

DALs can be implemented in many different ways, from simplistic wrappers around SQL statements

to complex modules that require maintenance in their own right Explaining how to fit the latter into an MVVM application is the focus of this chapter

Object-Relational Dichotomy

At a coarse-grained level, the NET Framework uses classes that interact with each other in order to solve

a particular problem These classes are a mixture of data, which comprise the state of an object, and

method implementations, which transform the data in some meaningful way Relational database

management systems (RDBMS), on the other hand, consist solely of data that has no associated

behavior—absent stored procedures, of course An RDBMS solves a very specific problem that does not concern objects That is, it does not concern them until one tries to convert from one to the other (from objects to database tables or vice versa)

Trang 2

DATABASE KEYS

Database tables should always have some sort of key that uniquely identifies each record and

distinguishes it from other records in the same table There are also different kinds of keys Natural keys

are composed of data that is a real-world property of the entity being mapped The natural key for a person could be his name, but this precludes multiple people being stored who share the same name Natural

keys can be hard to determine without resorting to a composite key, which is a key made up of more than one property that, when combined, uniquely identifies an entity For a person, his name and date of birth

could be combined to form a natural composite key, but even this is dissatisfying because it prevents

the—rare but plausible—case of two people sharing both name and birth date For this reason, rather

than struggling to force a natural key on an entity, a surrogate key is often used This is a value that does

not relate to the entity directly and is contrived to satisfy the requirement for uniqueness Typically, the

surrogate key is an integer that auto-increments with each additional record added to the table, or a

Universally Unique Identifier (UUID), which is a 32-byte (128–bit) value that is virtually guaranteed to be

unique (there are roughly 3.4  1038 possible values) Object-Relational Mapping (ORM) libraries, as

discussed in this chapter, typically recommend surrogate keys for simplicity

In an RDBMS, data is stored in tables, which are two-dimensional data structures where each

column applies to an atomic datum and each row is a full record of a single entry in the table Table 8–1 shows a trivial database design that contains products, customers, and orders aggregated into one

monolithic table

Table 8–1 A Monolithic—Unnormalized—Database Table

Product Name Product Price Customer Name Customer Address Order Number Order Date

XBox 360 100.00 Gary Hall Sommershire, England XX217P11D 08/02/2010

HP Probook 500.00 Gary Hall Sommershire, England XX217P11D 08/02/2010

40.00 A Reader Theirton, Utherplace IJ876Q98X 10/02/2010

This table poses a number of problems that render it so difficult and potentially problematic to use that it is almost useless The most egregious problem is that there is so much repeated data If the name

of this book or its price were to change, the database would require changes in two different places

Similarly, if I changed my name or address, the database would have to be changed wherever this data occurred The repetition is unnecessary and causes a maintenance headache, at best Also, how is the correct “A Reader” found without using both their name and address? As it stands, there is no single distinguishing column that allows differentiation between the two customers, so their whole data record must be used to ensure uniqueness

Normalization is the process of refactoring a database so that it conforms to a structure more

suitable for querying It frees the database of anomalies that negatively affect the integrity of the data

Trang 3

There are many different levels of normalization, called normal forms (or NF), which are assigned an

ascending index to indicate the rigor of the normalization Although normalization can progress to 6th

Normal Form (6NF), 3rd Normal Form is usually considered sufficient because, by this point, the tables are quite likely to be free of anomalies When the database table from Table 8–1 is normalized, the result

is what is shown in Table 8–2

Table 8–2 Table 8–1 Normalized to 3NF

WPFMVVM Pro WPF and Silverlight MVVM 40.00

XX217P11D 08/02/2010 1

IJ094A73N 11/10/2010 2

IJ876Q98X 10/02/2010 3

There are now three tables: Products, Customers, and Orders The products and customers have

been assigned identity columns that uniquely differentiate each record as being distinct However, there

is a table missing that links each order to the products that the customer purchased For this, an

OrderLine table is required, as shown in Table 8–3

Table 8–3 The OrderLine table

XX217P11D HPP

Trang 4

This table allows an order to contain multiple products while simultaneously allowing each product

to be used in different orders It is an example of a many-to-many relationship and contrasts to the to-many relationship between orders and customers (an order belongs to a single customer, but acustomer can create multiple orders)

one-Now, imagine that there are corresponding Product, Order, and Customer classes, each of whichcontains the same data as is here in the database tables However, there are also operations on theclasses that allow collaborations between the object instances Listing 8–1 is an example implementation

of such classes

Listing 8–1 A Sample Implementation of the Product, Order, and Customer Classes

public class Product

Trang 5

Notice that there is no OrderLine class; it is simply implied in the relationship between Order and

Product This is a key signifier for how different the two paradigms are and the difficulty that is inherent

in reconciling the object-relational dichotomy

Trang 6

Mapping Object Relationships

There are many different relationship types that can exist between objects Objects can be compositions

of other objects, whereby the container and the contents are both intrinsically linked If the containing class is destroyed, then all of the contained objects are also destroyed The UML diagram in Figure 8–1 shows the Customer class is composed of Orders

Figure 8–1 The Customer class has a composition relationship with the Order class

If a specific Customer instance is destroyed, all of the Order instances that it contains should also be destroyed The relationship between Order and Product is one of aggregation, which does not link the lifetimes of the two objects so strictly Instead, the two can be created and destroyed independently, as shown in Figure 8–2

Figure 8–2 The Order class has an aggregation relationship with the Product class

This makes sense because each Product is not wedded to a specific Order If the relationship was composition, deleting an Order would consequently delete all of the Products in that Order, so no-one could subsequently purchase any more of that Product!

In the RDBMS, both of these relationships would be one-to-many, with the primary key of the Customer table referenced as a foreign key within the Order table The difference would be that the composition relationship would require the ON CASCADE DELETE option when creating the Order table The contained objects in composition and aggregation need not be collections Suppose the Customer class was refactored so that the customer’s address was placed into its own Address class The Address would only ever be referenced by the one Customer object, and the Customer object would only have one address This, then, is a one-to-one composition relationship, as shown in Figure 8–3

Figure 8–3 The Customer class has a composition relationship with the Address class, but it is one-to-one

There are two ways to handle this relationship in the RDBMS The first option is to ignore that it is one-to-one and decide which side seems most reasonable for multiplicity In this instance, it makes more sense for customers to be allowed to store multiple addresses—in case the shipping address differs from the billing address, for example—rather than pander to the small demographic of customers who

Trang 7

live at the same address From this point, the tables can have a foreign key relationship to satisfy the new multiplicity: Address would be handed the Customer ID field as a foreign key The other option would be

to flatten the relationship out on the RDBMS side because it serves no purpose, leaving all of the address data in the Customer table The objects may be retrieved with a composition relationship, but the

database itself ignores that and stores the data in a single table

The final object relationship occurs when two objects have a many-to-many relationship, as shown

in Figure 8–4

Figure 8–4 The Product and Order classes have a many-to-many relationship

Orders can contain many Products, and Products can belong to many Orders In fact, the multiplicity

1 * indicates that an Order must have at least one Product, whereas 0 * implies that Products can exist wholly independent of Orders In this scenario, an association table is required, which links the Order

and Product tables together This is necessary, because a many-to-many relationship cannot exist in an RDBMS and must be factored out into two one-to-many relationships instead The OrderLine table from Table 8–3 is exactly the association table that is required in this case

Mapping Class Hierarchies

Although it is advisable to look first at the possibility of composing objects through a “has-a”

relationship, object-oriented programming provides the facility for deriving one class from another to form an “is-a” relationship With a sufficiently complex domain model, class hierarchies can form that succinctly solve the problem at hand Mapping this to a relational model is certainly achievable, but

there are three options to choose from that each present themselves as appropriate under varying

circumstances

To make these examples clearer, I am going to alter the design of the e-commerce system to include

a class hierarchy Customer seems like a good option with two subclasses inheriting from a base class (see Figure 8–5)

Figure 8–5 The Customer class refactored to distinguish between different types of User

Trang 8

The common data (the Customer’s Name, EmailAddress, and Password) have been extracted and

placed into a superclass called User, which is marked as abstract and cannot be instantiated in its own right The Customer class now only contains the Orders list and inherits the other data from the User A second subclass has been created to represent administrators for the application Administrators also have a Name, EmailAddress, and Password, but they have a PermissionsToken, which would determine

what level of administrative privileges the administrator possesses, and a list of AssignedDefects, which are bugs that have been handed to them for investigation and fixing Now that the inheritance hierarchy

is in place, the mapping options can be examined

Table-Per-Class-Hierarchy

The easiest option, from an implementation standpoint, is to flatten the whole hierarchy down and

create a single table that incorporates all of the data (see Table 8–4) While the User data will

undoubtedly be shared and not repeated, each record will contain redundant data depending on

whether it applies to a Customer or an Administrator This can affect data integrity, too, because the

AssignedDefects and Orders fields are collections that hold a one-to-many relationship with their

respective object types The tables that contain the data for these fields are not prevented from

referencing a row in the User table of either Customer or Administrator type This could leave Customers with AssignedDefects and Administrators with Orders, which is clearly erroneous

Table 8–4 The User Class Hierarchy Implemented as a Single Table

1 Gary Hall gary.hall@apress.com 112ADP33X NULL 76

2 Joe Bloggs Joe.Bloggs@hotmail.co.uk 938BCX82L 45 NULL

3 John Doe John_Doe@hotmail.com 364PZI32H 33 NULL

Table-Per-Concrete-Class

Each concrete class—that is, each non-abstract class—has its own corresponding table The database does not recognize that there is shared data between the different subclasses In Table 8–5, the User

abstract class is ignored, and the two tables replicate the Name, EmailAddress, and Password fields, which

is somewhat redundant However, the Order and Defect tables will correctly reference the separated Customer and Administrator, respectively, with no possibility of a mix-up As long as a user cannot be both a Customer and an Administrator, the redundancy is not too much of a problem

Trang 9

Table 8–5 The User Class Hierarchy Implemented with a Table for Each Subclass

1 Joe Bloggs Joe.Bloggs@hotmail.co.uk 938BCX82L 45

2 John Doe John_Doe@hotmail.com 364PZI32H 33

1 Gary Hall gary.hall@apress.com 112ADP33X 76

Table-Per-Class

The final alternative is to include the abstract base class and allow it a table of its own The subclass

tables then hold a foreign key reference to the appropriate User, so the shared data is given one single

schema in the database (see Table 8–6) While the implementation of this method is a little more

complicated, the database is properly normalized The application could be extended to allow a User to

be simultaneously a Customer and an Administrator without any alterations to the database structure

Table 8–6 The User Class Hierarchy Implemented with a Table for Each Concrete Class

1 Gary Hall gary.hall@apress.com 112ADP33X

2 Joe Bloggs Joe.Bloggs@hotmail.co.uk 938BCX82L

AdministratorID UserID PermissionsToken

Trang 10

DAL Implementations

So far, there is a domain model of a simple e-commerce system and a corresponding database structure What is left is to bridge the gap with the code that will perform the CRUD operations required by the clients of the model—most likely to be the ViewModel The objective of the DAL code is to protect the object schema from changes to the database schema This requires a type of mediator that will isolate the object model from the domain model while allowing clients to transfer data to and from each schema

The DataMapper pattern [PoEAA ,Fowler] is employed throughout the DAL to keep model classes and the database independent of each other and, more important, independent of the DataMapper itself (see Figure 8–6) Only the clients of the DataMapper are aware of its existence and use it to interact with the RDBMS

Figure 8–6 The DataMapper pattern: A UserMapper acts as the mediator between the RDBMS and User

class

A big decision must be made whether to implement the DAL manually, which allows a greater degree of control over the code, or whether to integrate a third-party library, which will generate the mapping layer automatically Although the latter will undoubtedly be quicker to implement, assimilating

a library into a project remains a non-trivial task that requires significant developer resource There are enough Object Relational Mapping (ORM) libraries available—at no cost—that using an existing

solution should be the default position However, guidance for manually implementing the DAL is provided to give background on the internal workings of an ORM Then, the current most popular third-party ORM libraries are briefly compared and contrasted

Manual Implementation

Before undertaking the monumental task of manually implementing a mapping layer, consider whether the resources required are worth the gains in control Reinvention of the wheel should be avoided if possible, especially when there are so many compelling ORM libraries available The DAL fits into the category of application infrastructure—any effort expended building infrastructure is effort diverted from adding true value to a project Caveats aside, it is still beneficial to understand what goes into a mapping layer because there is a lot of noise generated by third-party components that must remain domain agnostic so that it remains all things to all people, which is not an easy task

Listing 8–2 shows the interface of the UserMapper class that will be used by the ViewModel to interact with the underlying data storage mechanism The implementation will follow: method-by-method For now, notice that there are four methods for finding Users, two of which return a unique User when given their ID and EmailAddress fields, respectively, two of which return lists of Users The FindByName method accepts a partial name and, because User Names are not unique, multiple User instances may be returned The FindAll method, on the other hand, returns every user in the database without discrimination

Trang 11

There is also a single method each for the remaining create, update, and delete aspects of the CRUD

acronym, each accepting an existing User instance to act upon

Listing 8–2 The Interface for the UserMapper Class

public class UserMapper

{

public User FindByID(int id)

public User FindByEmailAddress(string emailAddress)

public IEnumerable<User> FindByName(string name)

public IEnumerable<User> FindAll()

public void Update(User user)

public int Insert(User user)

public void Delete(User user)

}

To implement this class succinctly, some helper methods will be required to avoid needless

repetition of database access code (see Listing 8–3) The NET Framework provides the ADO.NET

services for accessing databases without tying code to a specific vendor The class will require a database Connection, which (for now) will be opened and closed inside each method, making each action atomic

A Command will be executed on the Connection, instructing the data store to return the requested data,

insert new records, and update or remove existing records The DataReader class will be examined to

retrieve the correct data into newly instantiated User objects in each of the Find* methods

Listing 8–3 Database Interaction Helper Methods

public UserMapper(string connectionString)

{

_connectionString = connectionString;

}

private string _connectionString;

private IDbConnection _databaseConnection;

private bool ConnectToDatabase(string connectionString)

{

bool success = false;

_databaseConnection = new SqlConnection(connectionString);

Ngày đăng: 03/10/2013, 01:20

Xem thêm

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN