AdFactum ObjectMapper .NET Blog

Official blog of the AdFactum ObjectMapper .NET

Archive for the 'Tutorial' Category

Tutorial 4: Using the ObjectMapper in a multi threaded environment and cascaded transactions

Posted by Gerhard Stephan on 2nd April 2007

The following article is obsolete!
The OBM (ObjectMapper .NET Manager Class)
is now a part of the AdFactum ObjectMapper .NET 1.90.1917 itself.

This tutorial shows how to use the AdFactum ObjectMapper .NET in an enterprise environment. Therefore we assume that we have several threads (e.g. IIS sessions) that will access the business services.

We will implement all required features within the OBM (ObjectMapper .NET Manager Class). That’s a static helper class which you can simply copy to your project and change to your needs. (Please copy the source from your Tutorial Solution that you’ll find within your AdFactum ObjectMapper .NET download)

The first issue is, that we have to manage different threads. Because the ObjectMapper .NET is not multi threading capable by default, every single thread own it’s ObjectMapper .NET instance. Therefore we are using a feature of the ObjectMapper .NET that allows you to export/import the transcation context. Now we are using the following method to cache the transaction context objects for every single thread. 

/// <summary>
/// Used to access the transaction context
/// </summary>
private static ITransactionContext TransactionContext
{
    get
    {
        permissionSetLock.AcquireReaderLock(Timeout.Infinite);
        
        try
        {
            /*
             * If we don’t have a collection return NULL
             */
            if (transactionContext == null)
                 return null;
            
            int threadId = Thread.CurrentThread.ManagedThreadId;
            
            if (transactionContext.ContainsKey(threadId))
                 return transactionContext[threadId];
            else
                 return null;
        }
        finally
        {
            permissionSetLock.ReleaseReaderLock();
        }
    }
    
    set
    {
        permissionSetLock.AcquireWriterLock(Timeout.Infinite);
        
        try
        {
            /*
             * Check if the transaction Context is initialized
             */
            if (transactionContext == null)
                 transactionContext = new Dictionary<int, ITransactionContext>();
            
            /*
             * Set the value
             */
            transactionContext[Thread.CurrentThread.ManagedThreadId] = value;
        }
        
        finally
        {
            permissionSetLock.ReleaseWriterLock();
        }
    }
}

In order to create an instance of the ObjectMapper .NET for a particular thread we have to use another helper method that checks whether the transaction context has been stored in cache or not.  

/// <summary>
/// Creates the mapper using the standard factory and persister of the tutorial.
/// </summary>
/// <returns></returns>
public static ObjectMapper CreateMapper()
{
    ObjectMapper mapper;
    
    /*
     * Check if a valid transaction Context does exist, if not – create a new mapper object
     */
    if ((TransactionContext == null) || (TransactionContext.IsValid == false))
         mapper = new ObjectMapper(DefaultFactory, DefaultPersister, Transactions.Manual);
    else
         mapper = new ObjectMapper(DefaultFactory, TransactionContext);
    
    /*
     * Store the transaction Context
     */
    TransactionContext = mapper.TransactionContext;
    
    return mapper;
}

If you want to access the ObjectMapper .NET you have to use the CreateMapper method of the OBM helper class. The method retrieves an instance of the ObjectMapper .NET using the current thread and returns it to the calling method where it can be used.

using (ObjectMapper mapper = OBM.CreateMapper())
{
    /*
     * Open a transaction
     */
    bool nested = OBM.BeginTransaction(mapper);
    
    /*
     * Store the data
     */
    User HansDampf = new User();
    HansDampf.Name = "Hans Dampf";
    HansDampf.Logon = "Dampf";
    HansDampf.Md5PasswordKey = Md5CryptHelper.ComputeHash("turboDiesel");
    StoreUser (HansDampf);
    
    User DanielThunderbird = new User();
    DanielThunderbird.Name = "Daniel Thunderbird";
    DanielThunderbird.Logon = "Thunder";
    DanielThunderbird.Md5PasswordKey = Md5CryptHelper.ComputeHash("pokemon");
    StoreUser (DanielThunderbird);
    
    /*
     * Commit the transaction
     */
    OBM.Commit(mapper,nested);
}

As you can see, every method is using a new ObjectMapper .NET instance created by the CreateMapper method of the OBM Helper class. That’s the recommended way of using the ObjectMapper .NET in a multi threaded environment. Unlike other O/R Mapping tools, you don’t have to bother about the initializing ramp up time of the ObjectMapper .NET – it’s negligible.

So what are cascaded transactions?

Imagine you own several business methods which are calling each other in a cascading way. And every method own a separated ObjectMapper .NET instance. How do you manage transactions that are spanned over several business methods? To solve this problem we implemented helper methods for BeginTransaction, Flush and Commit. If you look at the implementation, you’ll see that the BeginTranscation will only return TRUE, if no superior transcation has been opened before.

/// <summary>
/// This method is used for opening an transaction.
/// If a parent transaction is open, the return value is set to true.
/// </summary>
/// <param name="mapper">Database Mapper</param>
/// <returns>
/// Returns True, if it’s a nested transaction
/// </returns>
public static bool BeginTransaction(ObjectMapper mapper)
{
    bool nestedTransaction = mapper.IsTransactionOpen;
    if (!nestedTransaction)
        mapper.BeginTransaction();
    
    return nestedTransaction;
}

/// <summary>
/// Flushes the content to database. This method does not close the transaction
/// </summary>
/// <param name="mapper">The mapper.</param>
public static void Flush(ObjectMapper mapper)
{
    try
    {
        mapper.Flush();
    }
    catch (Exception)
    {
        mapper.Rollback();
        throw;
    }
}

/// <summary>
/// This method executes an guarded commit. That means errors will be cached and resolved.
/// </summary>
/// <param name="mapper">Database Mapper</param>
/// <param name="nestedTransaction">True, if it’s a nested transaction.</param>
public static void Commit(ObjectMapper mapper, bool nestedTransaction)
{
    /*
     * If a surrounding transaction is already open – don’t commit the transaction
     */
    if (nestedTransaction)
        return;
    
    try
    {
        /*
         * Commit work
         */
        mapper.Commit();
    }
    catch (Exception)
    {
        mapper.Rollback();
        throw;
    }
}

The "Commit" method only executes the commit if it’s not a nested transaction. That means that only the first Commit method within the call stack will fire the SQLs to database. Due to that fact you’ll need to call the "flush" in nested business methods if the calling method needs to load data that has been stored by nested methods.

The Tutorial 4 has been included within the ObjectMapper .NET project. So download the ObjectMapper .NET and open the tutorial. If you have any questions, feel free to post them.

Cheers
– Gerhard 

kick it on DotNetKicks.com

Posted in Tutorial | 1 Comment »

Tutorial 3: Tracing SQL commands using Log4Net

Posted by Gerhard Stephan on 28th June 2006

For many reasons it’s quite useful to output the SQL commands into a log file. Especially for that case the ObjectMapper .NET offers the interface ISqlTracer which can be implemented by your application in order to log the sql output.

For logging purpose Log4Net is most time the first choice. Would’nt it be nice to marriage both technologies, the ObjectMapper .NET and Log4Net? That’s in fact possible and this tutorial will show how to implement it.

First thing we have to do is implementing the ISqlTracer interface. Have a look at the implementation below. I cuttet some basic stuff in order to save a little desktop space.

      /// <summary>

      /// This class traces the SQL Commands using log4net

      /// </summary>

      public class SqlTracer : ISqlTracer

      {

            private static readonly ILog log = LogManager.GetLogger(typeof (SqlTracer));

 

           

           

           

 

            /// <summary>

            /// The persister uses this method to publish the sql command

            /// </summary>

            /// <param name="original">The original string without the parameter extension.</param>

            /// <param name="extended">The extended string including the parameter content</param>

            /// <param name="affactedRows">The rows which are touched by the sql statement.</param>

            /// <param name="duration">Duration of the sql statement</param>

            public void SqlCommand(string original, string extended, int affactedRows, TimeSpan duration)

            {

                  if (extended.StartsWith("SELECT"))

                        log.Debug(extended);

                  else

                        log.Info(extended);

            }

 

            /// <summary>

            /// The persister uses this method to publish info messages, like warnings and so on.

            /// </summary>

            /// <param name="message">Message text</param>

            /// <param name="source">Source</param>

            public void ErrorMessage(string message, string source)

            {

                  log.Error(string.Concat(message, "\nSource: ", source));

            }

 

            /// <summary>

            /// Must return true, if an error logging shall be enabled.

            /// </summary>

            /// <value>

            ///   <c>true</c> if traceing of errors shall be enabled; otherwise, <c>false</c>.

            /// </value>

            public bool TraceErrorEnabled

            {

                  get { return log.IsErrorEnabled; }

            }

 

            /// <summary>

            /// Must return true, if the SQL tracing shall be enabled

            /// </summary>

            /// <value>

            ///   <c>true</c> if tracing of sql shall be enabled; otherwise, <c>false</c>.

            /// </value>

            public bool TraceSqlEnabled

            {

                  get { return log.IsInfoEnabled; }

            }

      }

 

Second thing we need is the Log4Net config file that configures the Log4Net Settings.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

 

      <configSections>

            <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />

      </configSections>

 

      <log4net>

     

            <appender name="SqlFileAppender" type="log4net.Appender.FileAppender">

                  <file value="sql_logging.txt" />

                  <appendToFile value="true" />

                  <layout type="log4net.Layout.PatternLayout">

                        <conversionPattern value="%date [%thread] %-5level: %message%newline" />

                  </layout>

            </appender>

           

            <root>

                  <level value="WARN" />

            </root>

           

            <logger name="Utils.SqlTracer">

                  <level value="DEBUG" />

                  <appender-ref ref="SqlFileAppender" />

            </logger>

 

      </log4net>

 

</configuration>

 

To complete the Log4Net framework must be initialized once when starting the application. That’s only 2 lines of code.

            string log4NetConfig = Path.GetDirectoryName("..\\..\\")+"\\logging.config";

            log4net.Config.XmlConfigurator.Configure(new FileInfo(log4NetConfig));

 

And sure: Our new SqlTracer must be initialized too when creating the Persister.

            protected AccessPersister GetAccessPersister ()

            {

                  return new AccessPersister("accessDb.mdb", new SqlTracer());

            }

That’s all. After starting the application and running the NUnit Test Cases a sql_logging.txt file must appear in your output directory (bin\debug).

You can download the example application using the following link:

Tracing SQL commands using Log4Net

Posted in Tutorial | No Comments »

Tutorial 2: Setting up the database, store and query some data

Posted by Gerhard Stephan on 31st May 2006

This tutorial shows how to execute an SQL file in order to create the tables. Furthermore it shows a simple example how the query API works and how to store data. But first things first.

In Tutorial 2 I decided to use NUnit instead of a console application. The advantage of NUnit is, that we can build different test cases in order to show different aspects of the ObjectMapper .NET. So If you load the Tutorial 2 you have to setup NUnit correctly in order to run the examples.

Project Configuration

Before we can insert data, it’s necessary to create a new database. In the tutorial we are going to create a new Microsoft Access Database by copying an empty database and than executing the DDL file.

            public void CreateAccessDatabase()

            {

                  /*

                   * Copy the template database as the new database

                   */

                  File.Copy(@"..\..\emptyAccessDb.mdb", "accessDb.mdb", true);

 

                  /*

                   * Create the schema

                   */

                  new ExportSchema().ExportAccessDDL();

 

                  /*

                   * Open the access db and execute the created script file

                   */

                  AccessPersister persister = GetAccessPersister();

                  SqlFile file = new SqlFile("Marketplace.access.sql");

                  file.ExecuteScript(persister);

                  file.Dispose();

                  persister.Dispose();

            }

 Now we created a working database – with all relations a database must have.

Tutorial02 - E/R Model

In our example we want to add a new user to the database. Looking at the source code of the User entity you see that the "Logon" property is defined as unique. So we have to care for duplicate values when storing new users.

            /// <summary>

            /// Gets or sets the logon.

            /// </summary>

            /// <value>The logon.</value>

            [PropertyLength(32)]

            [Required]

            [Unique]

            public string Logon

            {

                  get { return logon; }

                  set { logon = value; }

            }

In order to prevent a sql exception because of duplicated logon values, we have to check if a logon already exists and then to cancel the save operation. Furthermore this has only to be checked if we want to add new users to database. Existing Users don’t need to be checked, because they are only getting updated.

As you can see within the following code, every entity has a field named IsNew which is set by the ObjectMapper .NET. This field defines if a entity has already been stored to database. Our first query condition is a check to the Logon property of the User entity. So we are going to count all users with the specified logon column. If the count is greater than 0, there has already been stored a user entity to database.  

            /// <summary>

            /// Creates the user.

            /// </summary>

            /// <param name="mapper">The mapper.</param>

            /// <param name="user">The user.</param>

            /// <returns>True, if the user could be stored</returns>

            private bool StoreUser (ObjectMapper mapper, User user)

            {

                  /*

                   * If the user is new, check if the logon name does already exists

                   * If the logon name does exist, return false

                   */

                  if (user.IsNew)

                  {

                        ICondition logonCondition = new AndCondition(

                             typeof(User),    

                             "Logon",

                              QueryOperator.Like_NoCaseSensitive,

                             user.Logon);

                       

                        int count = mapper.Count(typeof(User), logonCondition);

                        if (count > 0)

                             return false;

                  }

 

                  /*

                   * if the logon name is not used or it is an existing user, than store the user

                   */

                  mapper.Save(user);

 

                  return true;

            }

The outcoming SQL looks like :

SELECT COUNT([USERS].[ID]) FROM [USERS] WHERE UCASE(TRIM([USERS].[LOGON])) like UCASE(TRIM(?))
 
INSERT INTO [USERS] ([ID],[NAME], [MD5PASSWORDKEY], [LOGON]) VALUES (?,?,?,?)
 

This statements have been created for Microsoft Access. If you are using an other database persister like Oracle, this SQL statements will change.

The whole tutorial can be download from here:
Tutorial 2: Setting up the database, store and query some data

Posted in Tutorial | No Comments »

Tutorial 1: How to create a DDL file out of your business entities

Posted by Gerhard Stephan on 15th May 2006

The first ObjectMapper .NET tutorial shows how to create a valid DDL file out of your business entities.

First we will need some business entities. In terminology of the ObjectMapper .NET, Business Entities are called "ValueObjects". That is because they will only contain the raw values.

Marketplace class diagram

In the first draw we will have an user class, his marketplace items and the bids. Our szenario: A user wants to sell marketplace items to an other user which can place bids on the items.

To export the entities we have to add the entity types to an ArrayList(). 

///<summary>
/// Gets the business entity types.
///</summary>
///<value>The business entity types.</value>
private ArrayList BusinessEntityTypes
{
      get
      {
            ArrayList result = new ArrayList();
            result.Add(typeof(User));
            result.Add(typeof(MarketplaceItem));
            result.Add(typeof(Bid));
                 
            return result;
      }
}
Than the DDL file can be exported by the ObjectMapper .NET and the WriteSchema method.
 
///<summary>
/// Exports the access DDL.
///</summary>
private void ExportAccessDDL()
{
      IPersister accessPersister = new AccessPersister();
      ObjectMapper mapper = CreateMapper(accessPersister);
      mapper.WriteSchema("Marketplace.access.sql", BusinessEntityTypes);
      mapper.Dispose();
 
      ShowFile("Marketplace.access.sql");
}
 
But that’s not only possible for a Microsoft Access database. It’s also possible for Oracle, Microsoft SQL Server and XML Schema files.
The demo can be downloaded using the following link:

Posted in Tutorial | 1 Comment »