Data Access Adapter Use

Posts   
 
    
Maxus
User
Posts: 76
Joined: 04-Aug-2006
# Posted on: 21-Aug-2008 11:41:15   

Hi LLBLGEN People,

I've been working in my business layer, stared reviewing the way I've been using adapter class, to be honest I'm not happy with my use.

Currently I have three types of classes:

  1. DAO class lives in the LLBLGEN DataAccess generated project:


    /// <summary>
    /// User Dao, Manages fetching all user related information.
    /// </summary>
    public class CommentDao
    {
        protected DataAccessAdapter _Adapter;

        /// <summary>
        /// Create a new Base Dao.
        /// </summary>
        /// <param name="adapter"></param>
        public CommentDao(DataAccessAdapter adapter)
        {
            if (adapter != null)
            {
                this._Adapter = adapter;
            }
            else
            {
                throw new Exception("Adapter is null.");
            }
        }

        //----------------------------------
        // Actions
        //----------------------------------

        //----------------------------------
        // Views
        //----------------------------------

        #region Fetch Comments ...
        /// <summary>
        /// Fetch a list of comments.
        /// </summary>
        /// <param name="itemId">The primary key Id of the items comments your retriveing.</param>
        /// <param name="commentTypeGroup">The comment group to fetch</param>
        public DataTable FetchComments(int itemId, CommentGroup commentTypeGroup)
        {
             // remove for simplicty.
             // Use Adapter Here.
        }
        #endregion


  1. I also have in the same project the DAO manager:


    /// <summary>
    /// Handles creating DAO objects and inserting the data Adapter into them.
    /// </summary>
    public class DaoManager: IDisposable
    {
        /// <summary>
        /// Adapter currently being used by this dao.
        /// </summary>
        protected DataAccessAdapter _Adapter;

        /// <summary>
        /// Create a new DaoManager.
        /// </summary>
        public DaoManager()
        {
            this._Adapter = new DataAccessAdapter();
        }

        /// <summary>
        /// Create a comment dao.
        /// </summary>
        /// <returns></returns>
        public CommentDao CreateCommentDao()
        {
            return new CommentDao(this._Adapter);
        }

        #region Save Transation ...
        /// <summary>
        /// Save Transaction.
        /// </summary>
        /// <param name="savePointName"></param>
        public void SaveTransaction(string savePointName)
        {
            // Save Transaction.
            this._Adapter.SaveTransaction(savePointName);
        }
        #endregion

        #region Start Transaction ...

        /// <summary>
        /// Start Transaction.
        /// </summary>
        /// <param name="isolationLevel"></param>
        /// <param name="name"></param>
        public void StartTransaction(IsolationLevel isolationLevel, string name)
        {
            // Start Transaction.
            this._Adapter.StartTransaction(isolationLevel, name);
        }

        #endregion

        #region Rollback Transaction ...

        /// <summary>
        /// Roll back Transaction.
        /// </summary>
        public void RollbackTransaction()
        {
            // Rollback Transaction.
            this._Adapter.Rollback();
        }

        #endregion

        #region Commit Transaction ...

        /// <summary>
        /// Commit Transaction.
        /// </summary>
        public void CommitTransaction()
        {
            // Commit Transaction.
            this._Adapter.Commit();
        }

        #endregion

        #region Close ...

        /// <summary>
        /// Close the underlying data adapter.
        /// </summary>
        public void Close()
        {
            // Close the connection.
            // Trace.Write(string.Format("Close Connection: {0}", Environment.NewLine));

            // Close.
            this._Adapter.CloseConnection();
        }

        #endregion


        #region IDisposable Members

        /// <summary>
        /// Dispose of used resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Dispose and close connections.
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this._Adapter != null)
                {
                    // Dispose.
                    this._Adapter.Dispose();
                    this._Adapter = null;
                }
            }
        }


        #endregion

}


  1. Then in my BL layer I have a manager class which groups together a bunch of operations that belong together eg:

    /// <summary>
    /// Comment Manager.
    /// </summary>
    public static class CommentManager
   {
        //----------------------------------
        // Methods
        //----------------------------------

        #region Fetch Comment Type List ...
        /// <summary>
        /// Fetch a list of comment types.
        /// </summary>
        /// <param name="commentTypeGroup">The comment group to fetch</param>
        public static DataTable FetchCommentTypeList(CommentGroup commentTypeGroup)
        {
            using (DaoManager DaoManager = new DaoManager())
            {
                // Create Comment Type Dao.
                CommentTypeDao Dao = DaoManager.CreateCommentTypeDao();

                // Fetch Result.
                return Dao.FetchCommentTypeList(commentTypeGroup);
            }
        }
        #endregion
   }


This means I can create a DAO manager the grab daos as I need them and they all use the same underlying adapter.

I was wondering if there was a simpler way of doing this so my DAOs could be static something neater like this:



            using (DataAccessAdapter D = new DataAccessAdapter())
            {
                // Fetch Result.
                return CommentDao.FetchCommentTypeList(commentTypeGroup);
            }


reason being is I often end up with a mess like this with having to create lots of DAOs simple_smile :


        #region Example ...
        /// <summary>
        /// Example
        /// </summary>
        public static DataTable TestMethod()
        {
            using (DaoManager DaoManager = new DaoManager())
            {
                // Create Comment Type Dao.
                CommentTypeDao CDao = DaoManager.CreateCommentTypeDao();
                DiffrentDao DDao = DaoManager.DiffrentDao();
                AnotherDao ADao = DaoManager.AnotherDao();

                // Do stuff with all the Daos
            }
        }
        #endregion

Sorry for the long post, any ideas how to make this better? any major flaws I should be aware of?

Thanks M

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 21-Aug-2008 14:24:50   

I have been using a static gateway which contains exactly 1 adapter per thread. there is a very simple implementation of this in Rhino-Tools http://rhino-tools.svn.sourceforge.net/viewvc/rhino-tools/trunk/rhino-commons/Rhino.Commons.Clr/LocalDataImpl/LocalData.cs?view=markup this implementation is a hashtable for anything. you could make your's specific to Adapters if you wanted.

Ok so lets say we are using a threaded container I would then create my gateway


public static UnitOfWork
{
    private const string KEY = "adapter";

    public void Start()
    {
           IDataAccessAdapter adapter = LocalData[KEY] as IDataAccessAdapter;
           if (adapter == null)
           {
                 adapter = new DataAccessAdapter();
                 adapter.BeginTransaction();
           }
    }

    public static IDataAccessAdapter Current
   {
         get 
         {
                 if(LocalData[KEY] == null)
                       throw new Exception("You must start the UnitOfWork");
                 return (IDataAccessAdatper)LocalData[KEY];
         }
   }

    public static void Commit
   {
         if(Current.IsInTransaction)
               Current.CommitTransaction();
   }


    public static void Rollback
   {
         if(Current.IsInTransaction)
               Current.RollbackTransaction();
   }

   public static void Dispose
   {
         Commit()
         Current.Dispose();
   }
}

If your working in a web app the best place to start/dispose the UnitOfWork is at Begin/End requests respectively. this can be hooked in on the Global.asax. if you working on a service or windows app the "best" place is relative to how long you want the adapter open.

Then from within your DAO objects you reference the UnitOfWork.Current. to preform any actions you need.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 21-Aug-2008 15:42:52   

Don't define the DataAccessAdapter inside your different Dao classes, but rather in a static global context, from which it can be accessed from static methods inside each Dao class.

Maxus
User
Posts: 76
Joined: 04-Aug-2006
# Posted on: 22-Aug-2008 10:15:01   

Hi jmeckley,

Really nice work! thanks a lot for that suggestion, I owe you a beer if you are in my neighborhood (Perth).

I was aware of rhino commons having worked a bit with them doing nHibernate development but had missed that bit or its new simple_smile

I reworked your example of unit of work to suit my current needs a bit better, I take it your starting your unit of work in a begin request end request sort of situation? Are you using WCF? Do you think there is much of a performace gain to be had by opening and closing your adapter per request rather than per manager method like I am?

it neatens up my code a lot, and provides way to attach other things to my thread. I have post my version to help anyone else having similar troubles and for a bit of peer review.



        /// <summary>
        /// Fetch System User Entity.
        /// </summary>
        /// <returns></returns>
        public static UserEntity FetchUser(int id)
        {
            using(UnitOfWork unit = new UnitOfWork())
            {
                return UserDao.FetchUser(1);
            }
        }




   /// <summary>
    /// A unit of work for managing the data adapter for access to the underlying data source.
    /// </summary>
    public class UnitOfWork : IUnitOfWork, IDisposable 
    {
        //----------------------------------
        // Fields
        //----------------------------------

        #region Fields ...
        private const string _ThreadStorageKey = "adapter";
        #endregion

        //----------------------------------
        // Constructors
        //----------------------------------

        #region Constructors ...

        /// <summary>
        /// cTor
        /// </summary>
        public UnitOfWork()
        {
            if (CurrentAdapter == null) 
            {
                CurrentAdapter = new CustomDataAccessAdapter(); 
            }
        }

        /// <summary>
        /// cTor
        /// </summary>
        public UnitOfWork(UserEntity committingUser)
        {
            if (CurrentAdapter == null) 
            {
                CurrentAdapter = new CustomDataAccessAdapter(committingUser.LogOnName); 
            }
        }

        #endregion

        //----------------------------------
        // Properties
        //----------------------------------

        #region Properties ...

        /// <summary>
        /// Get the current adapter.
        /// </summary>
        private static IDataAccessAdapter CurrentAdapter
        {
            get { return (IDataAccessAdapter)LocalDataStore.Data[_ThreadStorageKey]; }
            set { LocalDataStore.Data[_ThreadStorageKey] = value; }
        }

        #endregion

        //----------------------------------
        // Events
        //----------------------------------

        #region Events ...
        #endregion

        //----------------------------------
        // Methods
        //----------------------------------

        #region Methods ...

        /// <summary>
        /// Start a transaction.
        /// </summary>
        public void StartTransaction()
        {
            if (CurrentAdapter.IsTransactionInProgress) { CurrentAdapter.Commit(); }
        }

        /// <summary>
        /// Commit the adapter.
        /// </summary>
        public void CommitTransaction()
        {
            if (CurrentAdapter.IsTransactionInProgress) { CurrentAdapter.Commit(); }
        }

        /// <summary>
        /// Roll back transaction.
        /// </summary>
        public void RollbackTransaction()
        {
            if (CurrentAdapter.IsTransactionInProgress) { CurrentAdapter.Rollback(); }
        }

        #endregion

        //----------------------------------
        // Interface Implementations
        //----------------------------------

        #region IDisposable Members ...

        /// <summary>
        /// Dispose of used resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Dispose and close connections.
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Check the adapter is not null.
                if (CurrentAdapter != null)
                {
                    // Close any outstanding transactions.
                    if (CurrentAdapter.IsTransactionInProgress) { CommitTransaction(); }

                    // Dispose of the adpater.
                    CurrentAdapter.Dispose();

                    // set its reference to null.
                    CurrentAdapter = null;
                }
            }
        }

        #endregion

        //----------------------------------
        // Nested Types
        //----------------------------------

        #region Nested Types ...
        #endregion
        
    }


Cheers! M

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 22-Aug-2008 14:13:13   

I owe you a beer if you are in my neighborhood

Central, PA?

I was aware of rhino commons having worked a bit with them doing nHibernate development but had missed that bit or its new.

It's part of Rhino.Comon.Clr it's core to just about everything Rhino does. But it's not directly part of the public API, so it's not that obvious unless you read the source.

I take it your starting your unit of work in a begin request end request sort of situation?

Yes

Are you using WCF?

No, I'm currently exploring different options for html output. Html is the one area I'm still maunually testing. I'm considering Rhino.Igloo right now. MonoRail is tempting, but a drastic change from webforms. You also need to tinker with IIS to get the requests processed. I'm not ready to tackle that. MS MVC also looks promissing, but I'm not running .net 3.5

Do you think there is much of a performace gain to be had by opening and closing your adapter per request rather than per manager method like I am?

I do this for maintainability, not preformance. By hooking the UOW to request handlers I have exactly 1 place in my code that I start/stop/commit/rollback my database calls. Within my domain I know I have an open connection available. I also believe it its not the responsibility of my domain services to start/stop a UOW. they are only suppose to consume it. As for preformance. There is no noticable gain or loss from the end users prespective. Until preformance becomes a problem, I'm not worried about it.

Maxus
User
Posts: 76
Joined: 04-Aug-2006
# Posted on: 27-Aug-2008 09:40:54   

Hi Jason,

Central, PA?

Sadly I'm in Perth, Australia (think the beer wouldn't be worth drinking after a trip that long) simple_smile

It's part of Rhino.Comon.Clr it's core to just about everything Rhino does. But it's not directly part of the public API, so it's not that obvious unless you read the source.

Yep, thats most likly why I missed it, a great set of tools there. I did a bit of research after learning about using the [threadstatic] attribute, FYI here are some more examples of attaching information to the call context:

http://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext.aspx

Another nice implementation example:

http://startbigthinksmall.wordpress.com/2008/04/24/nice-free-and-reusable-net-ambient-context-pattern-implementation/

No, I'm currently exploring different options for html output. Html is the one area I'm still maunually testing. I'm considering Rhino.Igloo right now. MonoRail is tempting, but a drastic change from webforms. You also need to tinker with IIS to get the requests processed. I'm not ready to tackle that. MS MVC also looks promissing, but I'm not running .net 3.5

The MVC stuff does look good, Although I'm a little far in to start changing things like that on my current project. A bit annoyed it didn't come with SP1 like was originally suggested.

I do this for maintainability, not preformance. By hooking the UOW to request handlers I have exactly 1 place in my code that I start/stop/commit/rollback my database calls. Within my domain I know I have an open connection available. I also believe it its not the responsibility of my domain services to start/stop a UOW. they are only suppose to consume it. As for preformance. There is no noticable gain or loss from the end users prespective. Until preformance becomes a problem, I'm not worried about it.

I can really see the benefits of working this way, keeps the code a lot cleaner. I'm considering moving to it after our first release.

Thanks, M

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 27-Aug-2008 14:42:19   

Central PA = Pennsylvania, USA