Introduction
The biggest aim when I developed that architecture was to be able to simplify the whole mapping process between the database and the application, but without performance losses. As we all know, the four things that can be done with a set of data are:
- The selecting of filtered data – many rows by filtering criteria
- The selecting of one row by id (unique identifier of a table), the saving of the data, this can be divided into two:
- The inserting the data in the table (for example if the id is less or equal to zero)
- The updating of the data in the table (if the id of the row is greater than zero)
- The deleting data by id
No matter how difficult the business logic is in the application, these four actions are the things that can be done and must be done with a table or a view. The next figure illustrates the N-tier architecture of the system.
Let’s start with the BusinessEntities
project. It is the most separated of them all but the simplest to be understood.
BusinessEntities Project
Figure 1: Class Diagram of the project BusinessEntities
The project BusinessEntities
is a class library project. It’s a part of the middle layer in the N-tier architecture. It consists of container classes, which will be filled with data from the database. These kinds of classes are used only for programming representations of the data returned from the database. Usually a class like this represents a table or view in the common scenario a query which gives a list of data from the database. The name of the class corresponds to the name of the table; the name of the properties of the class corresponds to the names of the columns. These corresponding items are set by attributes. It’s done in EntityAttributes.cs file. The class MapField
inherits the base .NET class Attribute. This attribute is used to map the property of the class to the column of the database. For example:
[MapField("Title")]
public string Title
{
get { return _title; }
set { _title = value; }
}
The property called ‘Title
’ of the class corresponds to the column called ‘Title
’ returned from the database according to MapField
attribute. The class ‘SpNames
’ also inherits from the base .NET class attribute. It is used to set the names of the stored procedures for the main four actions – ‘select’, ‘select one’, ‘insert or update’, and ‘delete’. For example:
[SPNames("pContactList", "pContactSelect", "pContactSave", "pContactDelete")]
public class ContactEntity : BaseEntity, IContactEntity
{
The constructor of the class ‘SpNames
’ is overridden and if there is a need, it can be initialized with one or more of the names of the stored procedures. The way these custom attributes are used will be explained in details later. As you can see, there is a ‘BaseEntity
’ class. It is used to store the two main properties in all entity classes – ‘ID
’ and ‘ReturnMessage
’. Every table has an id; the return message in the entity class is used to store the return message from the stored procedures. The interface ‘IBaseEntity
’ is the base interface for all entities. It makes the properties ‘ID
’ and ‘ReturnMessage
’ to be required. It’s good if all entity classes have a corresponding interface in which all the properties can be described, but this is not compulsory.
Project DBAccess
This makes the connection with the database. It’s also a class library project. Its class diagram can be seen in Fig. 2:
Figure 2: Class Diagram of the project DBAccess
The file named Reflection.cs consists of one class called Mapper
. This class, as you can guess, contains methods for mapping the entity classes and their properties with table names and columns. This is made by reading the attributes by reflection mechanism in the .NET Framework. The method ‘GetMapFields
’ is one of the methods implemented in this class. By its explanation, you will be able to understand the whole reflection mechanism.
private static void GetMapFields
(Type type, DbDataReader reader, ref Dictionary<int> fields)
{
PropertyInfo[] pInfo = type.GetProperties();
for (int i = 0; i < reader.FieldCount; i++)
{
foreach (PropertyInfo pi in pInfo)
{
object[] attributes =
pi.GetCustomAttributes(typeof(MapField), false);
if (attributes.Length > 0)
{
MapField field = attributes[0] as MapField;
if (field != null && field.DbField ==
reader.GetName(i) && !fields.ContainsKey(i))
{
fields.Add(i, pi);
break;
}
}
}
}
if (type.BaseType != null && type.BaseType.IsClass)
Mapper.GetMapFields(type.BaseType, reader, ref fields);
}
The method ‘GetProperties()
’ returns all properties of the type. Because the class Type
is the base class of all types, here can be passed all the entity classes from the ‘BusinessEntities
’ project. After the properties are returned, each property must collect its attribute defined by the developer and then they are checked if they are matched with the returned data from the ‘DbDataReader
’ object. After that, the matches are saved in the Dictionary
collection. By this code:
if (type.BaseType != null && type.BaseType.IsClass)
Mapper.GetMapFields(type.BaseType, reader, ref fields);
Properties are taken as well as the properties of the base types, which are added to the Dictionary
collection when there is a match. There are different implementations of the method ‘GetMapFields()
’. In some are passed the type and the output collection, others the type and others the type and the object DbDataReader
. The class GetEntitiesPaging
will be explained in my next posting when I will say more for returning paged data. The file Filter.cs consists of ‘FilterInfo
’ class, which is used for passing the parameters, when the data from the stored procedure is taken by using filtering parameters. (The filtering parameters are the parameters which limit the result or which sort the result by some criteria). The class DBAccess
contains methods for creation of commands, for creation of parameters to that command and for returning of an object with type ‘DbDataReader
’ and ‘DataTable
’. This is the base class of the class DataTransaction
. In the class ‘DataTransaction
’ are made the opening and closing of the connection to the database and there are also additional methods for working with the transactions. The class ‘Paging
’ stores the properties, which are necessary when there is paging of the results. The class ‘DataSource
’ inherits the interfaces ICollection
and IEnumberable
. It contains the property ‘Paging
’ which has type ‘Paging
’ (the previous explained class), which is used for the paging, and also the properties for the total number of records, the list of records and the messages from the database. The class ‘Enumerator
’ inherits the interface ‘IEnumerator
’, there is also a property which returns the current element and methods for navigation into the collection. Another very important class is the class ‘Utils
’. It contains methods which support the work of the application with the database. ‘GetDbType
’: This is a method by which input type checks its name and returns the representation of the type in the database. ‘GetPropertyValue
’ is a method by which input type and the name of the property returns the value of the property.
public static DbType GetDbType(Type pType)
{
DbType ret;
switch (pType.ToString())
{
case "System.Int16":
ret = DbType.Int16;
break;
case "System.Int32":
ret = DbType.Int32;
break;
case "System.Int64":
ret = DbType.Int64;
break;
The method ‘GetProcedureName
’ by input type and the type of the procedure (enum
type) return the name of the procedure. This also is made by reflection mechanism. There is one nested enumerated type called ‘ProcedureType
’ for all types of the stored procedures and the class EntityException
. This class has as a base class - the ‘class Exception’ and it is used to throw exception when there is no defined stored procedure by the attribute of the class. The class ‘DBAccessBase
’ is in the file EntityMethods.cs. It is a major class for the whole class library application. There are lots of methods for data processing, which are divided into regions.
- The ‘
GetEntities
’ region consists of static
methods, which returns objects with type DataSource
. Generics are used for passing the type of the objects. All methods are with one and the same name ‘GetEntities
’ and are overriden several times depending on their input arguments. - The ‘
GetEntity
’ region consists of methods which return only one object with type defined by the generalization. This returned type must be inherited by ‘IBaseEntity
interface’. This means that this will be a type of the project BusinessEntities
. - The ‘
SaveEntity
’ region is for saving of the types from the project ‘BusinessEntities
’. It’s passed object from the same type, which is not defined before – the generic mechanism is used again. - The ‘
DeleteEntity
/DeleteEntities
’ region contains methods for deleting of an element or many elements according to the filter. - The Filters region contains only one
static
methods for adding of such filters. - In the Other region is added code which breaks the architecture of the system, but it’s very useful in some moments. It can accept the name of the stored procedure and then execute it. This option is used when there is a need for other procedures different from the four main attributes of the entity classes. This makes the architecture not so consistent but it’s better than to create a new entity class which must derive the one which has the same type like the result from the method of the class.
Project Business
This project also like the two which I explained before is a class library project. This is a place for realization of the business rules. It consists of only one class ‘BLBase
’. This class has methods, which called methods directly from the class library project ‘DBAccess
’.
Figure 3: Class Diagram of the project Business made with the class designer of the Microsoft Visual Studio 2008
As you can see from the class diagram, there are methods which are very important for working with database. The method ‘DeleteEntity
’ deletes one element according to the id of the element. The method ‘DeleteEntities
’ deletes many elements according to filter criteria. (array of objects with type FilterInfo
). The method ‘ExecuteProcedure
’ executes stored procedure. The name of the stored procedure is passed as a parameter. It’s not taken like in the other methods by reflection. The method ‘GetEntities
’ returns object with type DataSource
. There are several implementations of that method according to the input parameters. The method GetEntitiesPaging
also returns DataSource
object, but with records which are determined by the input parameter of type Paging. By this parameter are passed the start record, the last record, and the count of the records. With that input data, the stored procedure will be able to return only the necessary records. The method ‘GetEntity
’ returns only one element from a particular type according to an id. The method ‘SaveEntity
’ saves the changes from a particular object or creates a new one according to the id property of the passed object. It returns the created object or the updated one.
Project BusinessActions
This is another layer. It’s not required but it helps because it separates the methods for returning data from the business logic of an aspx page or from the other presentation files of other projects.
Figure 4: Project BusinessActions
Project ConsoleTestApplication
Figure 5: Project ConsoleTestApplication
This is a simple console application which is used just to call methods from ‘BusinessActions
Project’.
Conclusion
In my opinion, it’s very good layered architecture which is very easy to use. The only loss of performance is when the creation of the object is done. This is because reflection mechanism needs time to get properties of the class, the stored procedure name and to match them. I have made some test regarding its performance and it turns out that this only becomes a problem when a lot of objects are returned from the database, but this is not likely to happen in real life case. If you have any questions, please feel free to ask. I am open to criticism. In the next publication, I will show you a stored procedure for sorting and filtering the data. The C# code for getting data from such a procedure is written in the code, so don't worry if you don't understand it. (Example: GetEntitiesPaging
procedure in the Reflection.cs file)