AdFactum ObjectMapper .NET Blog

Official blog of the AdFactum ObjectMapper .NET

Archive for April, 2007

AdFactum ObjectMapper .NET Forum launched

Posted by Gerhard Stephan on 24th April 2007

Today the AdFactum ObjectMapper .NET Forum has been launched. The forum shall be an open discussion board for all issues regarding the AdFactum ObjectMapper .NET.

Please don’t hesitate to register yourself within the forum. The more people are getting involved in the forum, the better the AdFactum ObjectMapper .NET becomes.

So visit the forum at: http://forum.objectmapper.net

Cheers
– Gerhard

Posted in Miscellaneous | No Comments »

How to store binary data (blob)

Posted by Gerhard Stephan on 23rd April 2007

The ObjectMapper .NET knows 3 different ways to store binary data (blobs) in database. Two ways are designed to store binary blobs and one to store character blobs.

The straightest way to store binary data is to create a byte array property. This byte array property could look like:

        /// <summary>

        /// Gets or sets the BLOB.

        /// </summary>

        /// <value>The BLOB.</value>

        [PropertyName("Stream")]

        public byte[] Blob

        {

            get { return blob; }

            set { blob = value; }

        }

 

As a second solution the AdFactum ObjectMapper .NET offers you the possibility to use Streams. 

        /// <summary>

        /// Gets or sets the BLOB.

        /// </summary>

        /// <value>The BLOB.</value>

        [PropertyName("Stream")]

        public Stream Stream

        {

            get { return stream; }

            set { stream = value; }

        }

 

When reading from database the ObjectMapper .NET creates a Memory Stream for the blob content.

A third solution is to use character blobs. In oracle known as CBLOB or known as a MEMO field if you’re using Microsoft Access. Such character blobs are defined as regular strings with an unlimited property length.

        /// <summary>

        /// Gets or sets the content.

        /// </summary>

        /// <value>The content.</value>

        [PropertyLength(int.MaxValue)]

        public string Content

        {

            get { return content; }

            set { content = value; }

        }

 

So, that’s all you have to know about blobs if you’re using the AdFactum ObjectMapper .NET.
Hope you enjoy the simplicity.

Cheers
– Gerhard 

kick it on DotNetKicks.com

Posted in HowTo | No Comments »

3rd Place for the ObjectMapper .NET

Posted by Gerhard Stephan on 19th April 2007

Readers Choice 2007The readers of the dot.net magazine are the conceivably best jury for the evaluation of development products. The magazine nominated in twelve categories over 100 products!

That’s why I’m really proud to say that the ObjectMapper .NET has been voted for the 3rd Place within the category O/R Mapping Tools.

 

Bester O/R Mapper
1. Platz: DataObjects.NET (25,6 %)
2. Platz: NHibernate (14 %)
3. Platz: ObjectMapper .NET (13,7 %)

 

The ObjectMapper .NET project started last year, so I think it’s a very good 3rd place for such a short time. But I won’t rest on that position. The ObjectMapper .NET project will grow and I’m looking forward to the next readers survey in that the ObjectMapper .NET hopefully achieve the 1st place.

Cheers
Gerhard 

Web Link: Reader’s Choice 2007

Posted in Miscellaneous | 1 Comment »

Attribute: [ForeignKey]

Posted by Gerhard Stephan on 19th April 2007

Beside the automatic generation of foreign keys, it’s sometimes necessary to create foreign keys that are driven by business logic. Those foreign keys can’t be handled automatically, but they have to be set manually with the ForeignKey attribute.

Create a single foreign key relation:

Creating a single foreign key relation is pretty easy. This can be done by attributing the property with the ForeignKey attribute:

        [ForeignKey(typeof(RangeText), "Key")]

        public new string FilterKey

        {

            get { return base.FilterKey; }

            set { base.FilterKey = value; }

        }

 

The resulting DDL script would look like:

alter table RANGE

  add constraint RANGE_FK01 foreign key (FILTERKEY)

  references RANGETEXT (KEY);

Create compound foreign keys using key groups:

In order to create compound foreign keys you have to bundle the affected properties in foreign key groups. A foreign key group owns a number that identifies the group. The number itself isn’t significant, it’s only important that the properties you want to group have the same key group number assigned.

        [ForeignKey(UNIQUE_GROUP, typeof(ManufactoryText), "Key")]

        public new string FilterKey

        {

            get { return base.FilterKey; }

            set { base.FilterKey = value; }

        }

 

        [ForeignKey(UNIQUE_GROUP, typeof(ManufactoryText), "Locale")]

        public new string Locale

        {

            get { return base.Locale; }

            set { base.Locale = value; }

        }

 

The resulting DDL script would look like:

 

alter table MANUFACTORY

  add constraint MANUFACTORY_FK01 foreign key (FILTERKEY, LOCALE)

  references MANUFACTORYTEXT (KEY, LOCALE);

Create compound foreign keys using key groups with sorted keys:

Additional to the foreign key group feature you can sort the keys within the foreign key group. Have a look at the example above. Most times it is necessary to store the properties in a defined sorting. That’s important if you have an compound index on the table you want to use with the compound foreign key.

In that case, the second number, after the foreign key group defines the sorting of the keys. Imagine you want to change the ordering of the keys in our example above.

        [ForeignKey(UNIQUE_GROUP, 2, typeof(ManufactoryText), "Key")]

        public new string FilterKey

        {

            get { return base.FilterKey; }

            set { base.FilterKey = value; }

        }

 

        [ForeignKey(UNIQUE_GROUP, 1, typeof(ManufactoryText), "Locale")]

        public new string Locale

        {

            get { return base.Locale; }

            set { base.Locale = value; }

        }

 

The resulting DDL Script would look like: 

alter table MANUFACTORY

  add constraint MANUFACTORY_FK01 foreign key (LOCALE, FILTERKEY)

  references MANUFACTORYTEXT (LOCALE, KEY);

 

As you can see the sorting of the columns within the foreign key group changed.

Summary:

The foreign key attribute offers you a lot of possibilities to cover more than 90% of all use cases for that you might need foreign key constraints. The last 10% should be better left to a seasoned database admin, because they are such special that they can’t be handled by a generic OR Mapping tool.

Cheers
Gerhard

Posted in Attributes | No Comments »

New Release – ObjectMapper .NET 1.60.1517.1

Posted by Gerhard Stephan on 17th April 2007

Today I’m proud to present the new major release of the AdFactum ObjectMapper .NET.  This release includes two new features and one major attribute change.

As a new feature I extended the join condition in order to enable joining objects with objects, types with objects and types with types. Thus reduces the amount of conditions that are necessary to limit the database query.

Imagine we have a structure like that:

            Employee emp = new Employee("All", "Mine");

            FullFeaturedCompany c1 = new FullFeaturedCompany("Heavy stuff");

            c1.Owner = emp;

            FullFeaturedCompany c2 = new FullFeaturedCompany("Light stuff");

            c2.Owner = emp;

 

            Employee emp2 = new Employee("Thats", "not mine");

            FullFeaturedCompany c3 = new FullFeaturedCompany("Other stuff");

            c3.Owner = emp2;

 

Now you have several possibilities to join these Company -> Employee structure.

 

            /*

             * Join company c1 object with owner object

             */

            FullFeaturedCompany loaded;

            loaded = (FullFeaturedCompany) Mapper.FlatLoad(typeof (FullFeaturedCompany),

                new Join(c1, "Owner", emp));

            Assert.AreEqual(c1.Id, loaded.Id, "Could not load company");

 

            /*

             * Join typeof(Company) with owner object

             */

            IList selection;

            selection = Mapper.FlatSelect(typeof (FullFeaturedCompany),

                new Join(typeof (FullFeaturedCompany), "Owner", emp));

            Assert.AreEqual(2, selection.Count, "Could not find the 2 expected companies.");

 

            /*

             * Join typeof(Company) with typeof(Employee)

             */

            selection = Mapper.FlatSelect(typeof(FullFeaturedCompany),

                new Join(typeof(FullFeaturedCompany), "Owner", typeof(Employee)));

            Assert.AreEqual(3, selection.Count, "Could not find the 3 expected companies.");

 

 

As a second new feature I extended the InCondition. In prior versions the InCondition could only use SubSelects or Unions to build IN Condition SQL statements. Now it’s possible to give value arrays or value lists to the ObjectMapper .NET in order to support queries like "field IN (value1, value2, … valueN)".

 

            /*

             * Insert some test contacts

             */

            BeginTransaction();

            Mapper.Save(new Contact("Fritz", "Bauer"));

            Mapper.Save(new Contact("Annemarie", "Aal"));

            Mapper.Save(new Contact("Hans", "Habicht"));

            Mapper.Save(new Contact("Olaf", "Schubert"));

            Commit();

 

            /*

             * Search "Annemarie" and "Olaf" using the InCondition

             */

            InCondition condition = new InCondition(typeof(Contact), "FirstName", "Annemarie", "Olaf");

            IList result = Mapper.Select(typeof (Contact), condition);

 

.. or

            InCondition condition = new InCondition(typeof(Contact), "FirstName", "Annemarie", "Olaf", "Fritz");

 

You can also put a IList object into the InCondition. This enables you to use a generic collection e.g. List<string> in order to provide the values for the InCondition.

 

The biggest changes have been made for the ForeignKey Attribute. In prior versions the foreign key attribute has been a class attribute. Because this wasn’t really generic, I changed the ForeignKey attribute handling so that it can now be used like the Unique attribute. Compound Foreign Keys can now set with Key Groups. So have a look at the following example.

 

        [PropertyLength(32)]

        [ForeignKey(1, typeof(Country), "Key")]

        public string CountryKey

        {

            get { return countryKey; }

            set { countryKey = value; }

        }

 

        [PropertyLength(5)]

        [ForeignKey(1, typeof(Country), "Locale")]

        public new string Locale

        {

            get { return base.Locale; }

            set { base.Locale = value; }

        }

 

This example creates a compound foreign key to the Country object. 

 

That’s all for now. So hope you enjoy the new version of the ObjectMapper .NET.

Cheers

 

– Gerhard

 

 http://www.objectmapper.net/download/ObjectMapper_1.60.1517.1.exe

kick it on DotNetKicks.com

Posted in Releases | No Comments »

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 »