Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Managing LINQ Data Contexts with the Unity Framework

3.44/5 (6 votes)
24 Jul 2008CPOL4 min read 1   148  
A unique way of managing the LINQ Data Context object between business logic classes with Dependency Injection.

Introduction

This article looks at a unique way of managing the LINQ Data Context object between business logic classes. Business logic objects need not be aware of the data context itself, they should just do only what they're meant to do even though they may depend on the data context. I thought this could be a cool way to use Dependency Injection with Microsoft's Unity Framework.

Background

There are tons of posts out there about managing the LINQ Data Context object. Some say that a new one should be instantiated each time a developer wants to do anything with the data, some say it should exist globally, and others have better ways of managing it. After looking at all of the different ways to manage the context object, I found that the only reason why you would want to persist the object is if you are doing a lot of data manipulation (inserts, updates, deletes) at one time and only want to make one call to the database to execute all of the changes. This could be done all at once with a simple method and one Data Context object, but this doesn't separate concerns into business objects. Instead, it would be nicer to have the business logic for a member, a sale, a product, etc... to be in their own business logic classes. The problem is maintaining the Data Context object between these business logic classes without having to worry about the business logic classes themselves caring which context object to use. Dependency Injection to the rescue!

Using the Code

The solution attached is based on NUnit, so you'll need to use NUnit to see the outcome. The breakdown of the code is as follows:

  • LinqUnity.Business...
    • The generated objects created by LINQ to SQL.
  • LinqUnity.Logic...
    • My custom business logic objects containing the logic for database access. These classes have a dependency on the Factory object which contains the data context object. All of these objects implement the IFactoryData interface which defines a property for the Factory
  • LinqUnity.Business.Factory
    • The object to use for resolving all business logic classes. This object's constructor creates a new Unity container which gets built based on the configuration in the app.config. It then registers an instance of itself into the container so that all dependant objects in container receeive the itself as the Factory instance.
  • LinqUnity.Test
    • The NUnit test class containing the tests and examples for data manipulation.

The database structure is quite simple. and is made up of the following tables:

  • Members
  • Articles
  • Comments

I'm not sure what this application could be used for, but the idea is that Articles can have Comments and Members can create these Comments.

The Factory object is what all IFactoryData objects are dependant on. The Factory object contains a read only property that returns the LINQ data context object and a Resolve method to return any of the IFactoryData objects out. The reason that each IFactoryData object is dependant on the Factory and not just the LINQ data context object is because if one of the IFactoryData objects needed to resolve one of the other IFactoryData objects, it could using it's own Factory object.

An example of how this works is in the TestObjects() test method which tests to ensure that each object resolved from the factory is always the same object referencing the same data context object.

C#
private Factory m_factory = new Factory();

private MemberData m_memberData;
private ArticleData m_articleData;
private CommentData m_commentData;

// This create our private objects before running tests
[SetUp]
public void Init()
{
    m_memberData = m_factory.Resolve<memberdata>();           
    m_articleData = m_factory.Resolve<articledata>();
    m_commentData = m_factory.Resolve<commentdata>();
}

[Test]
public void TestObjects()
{
    //prove that the data factory is always the same
    Assert.AreEqual(m_memberData.DataFactory, m_factory);
    Assert.AreEqual(m_articleData.DataFactory, m_factory);
    Assert.AreEqual(m_commentData.DataFactory, m_factory);

    //resolve objects out of the factory
    MemberData memberData = m_factory.Resolve<memberdata>();
    ArticleData articleData = m_factory.Resolve<articledata>();
    CommentData commentData = m_factory.Resolve<commentdata>();
    
    //prove that the above objects are the same as our private members
    Assert.AreEqual(memberData, m_memberData);
    Assert.AreEqual(articleData, m_articleData);
    Assert.AreEqual(commentData, m_commentData);

    //prove that the context object is always the same
    Assert.AreEqual(memberData.DataFactory.DBContext, m_factory.DBContext);
    Assert.AreEqual(articleData.DataFactory.DBContext, m_factory.DBContext);
    Assert.AreEqual(commentData.DataFactory.DBContext, m_factory.DBContext);
}

To put this into practice, this method will create an article, create a member, and create 20 comments for the new member/article, and do it all in one database connection.

C#
[Test]
public void CreateMemberArticleComments()
{
    string unique = Guid.NewGuid().ToString();

    //create a new member and article with some random data
    Member newMember = m_memberData.CreateNew(unique, 
      string.Format("{0}@{0}.com", unique), "00000000");
    Article newArticle = m_articleData.CreateNew(unique, 
      "Some description...", string.Format("Some body text {0}", unique));
   
    //we're creating the new comment with the reference 
    //to our new member and new article objects. LINQ will
    //take care of wiring up the primary keys in the backend on submit
    List<comment> comments = new List<comment>();
    for (int i = 0; i < 20; i++)
        comments.Add(m_commentData.CreateNew(string.Format("{0} - This is new comment", 
                     i.ToString()), newMember, newArticle));

    //SAVE ALL THE CHANGES TO THE DATABASE
    m_factory.DBContext.SubmitChanges();

    //ensure that everything has worked...

    //ensure that we have a new member ID
    Assert.Greater(newMember.memberID, 0);
    //ensure that we have a new article ID
    Assert.Greater(newArticle.articleID, 0);

    //ensure that all of the new comments has the correct member and article ids
    foreach (Comment comment in comments)
    {
        Assert.Greater(comment.commentID, 0);
        Assert.AreEqual(comment.articleID, newArticle.articleID);
        Assert.AreEqual(comment.memberID, newMember.memberID);
    }
    
}

This all works because each IFactoryData has a dependency on the Factory object, which in turn contains the LINQ Data Context object. When the factory is created, it creates a new Unity container and registers itelf inside of it.

An example of one of the dependant logic objects:

C#
public class MemberData : IFactoryData, IMemberData
    {

        #region IFactoryData Members

        /// <summary>
        /// Flag this property as dependant so the Unity Framework passes this object
        /// the current Factory/Context
        /// </summary>

        [Dependency]
        public Factory DataFactory
        {
            get
            {
                return m_factory;
            }
            set
            {
                m_factory = value;
            }
        }

        #endregion

        private Factory m_factory;

        #region IMemberData Members

        public List<Member> SelectAll()
        {
            return (from m in m_factory.DBContext.Members select m).ToList();
        }

        public Member SelectByID(int ID)
        {
            return (from m in m_factory.DBContext.Members where m.memberID ==
                ID select m).Single();
        }

        public Member CreateNew(string name, string email, string phone)
        {
            Member member = new Member();
            member.name = name;
            member.email = email;
            member.phone = phone;
            member.created = DateTime.Now;
            m_factory.DBContext.Members.InsertOnSubmit(member);
            return member;
        }

        #endregion
    }

The Factory is fairly simple. It creates a new Unity container, configures the container based on the configuration file and inserts itself into the container. This ensures that all of the IFactoryData. objects exist in the container and all of them have the current Factory instance as their Factory object.

C#
public class Factory : IFactory
    {

        /// <summary>
        /// This constructor adds itself to the Unity Container
        /// </summary>
        public Factory()
        {            
            container = new UnityContainer();

            //Get the "DataLayer" container definition from the config file and
            //configure our container with it
            UnityConfigurationSection section = (
                UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            section.Containers["DataLayer"].Configure(container);

            //Register this object instance in the container
            container.RegisterInstance<Factory>(this,
                new ContainerControlledLifetimeManager());
        }

        private IUnityContainer container;

       /// <summary>
       /// Returns the IDataContext object requested from the container
       /// </summary>
       /// <typeparam name="T"></typeparam>
       /// <returns></returns>

        public T Resolve<T>() where T : IFactoryData
        {
            T objData = container.Resolve<T>();
            return objData;
        }

        /// <summary>
        /// Return the current DataContext object for the factory
        /// </summary>
        public TestDataContext DBContext
        {
            get { return container.Resolve<TestDataContext>(); }
        }

    }

The configuration section is also quite simple. The only "trick" was to ensure that it creates the LINQ data context object using the default parameterless constructor.

XML
<unity>

    <!-- Define all of the types that we want to put into our container -->
    <typeAliases>
      <typeAlias alias="singleton"
          type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,
          Microsoft.Practices.Unity" />      
      <typeAlias alias="IMemberData" type="LinqUnity.Logic.IMemberData, LinqUnity" />
      <typeAlias alias="IArticleData" type="LinqUnity.Logic.IArticleData, LinqUnity" />
      <typeAlias alias="ICommentData" type="LinqUnity.Logic.ICommentData, LinqUnity" />
    </typeAliases>

    <containers>

    
      <!-- Define a container that contains all of the data logic objects -->
      <container name="DataLayer">
        <types>
          <!-- The LINQ data context object -->
          <type type="LinqUnity.Business.TestDataContext, LinqUnity">
            <lifetime type="singleton" />
            <typeConfig
               extensionType=
               "Microsoft.Practices.Unity.Configuration.TypeInjectionElement,
               Microsoft.Practices.Unity.Configuration">
              <constructor/>
              <!-- Ensure it is created with the default empty parameter constructor -->

            </typeConfig>
          </type>
          <!-- Our custom logic classes: -->
          <type type="IMemberData" mapTo="LinqUnity.Logic.MemberData, LinqUnity">
            <lifetime type="singleton" />
          </type>
          <type type="IArticleData" mapTo="LinqUnity.Logic.ArticleData, LinqUnity">
            <lifetime type="singleton" />
          </type>

          <type type="ICommentData" mapTo="LinqUnity.Logic.CommentData, LinqUnity">
            <lifetime type="singleton" />
          </type>
        </types>
      </container>
      
    </containers>
    
</unity>

Points of Interest

I thought this was a cool way to manage the LINQ data context object since it makes it easy to create business logic classes that contain the logic to only do what they're meant to do but while maintaining the same context. A developer using this model could have as many concurrent factories instances as they want that would each have their own LINQ context object.

If anyone wants to read more about the Unity application block, here are the links:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)