Click here to Skip to main content
Click here to Skip to main content
Go to top

Managing LINQ Data Contexts with the Unity Framework

, 24 Jul 2008
Rate this:
Please Sign up or sign in to vote.
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.

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.

[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:

public class MemberData : IFactoryData, IMemberData
    {

        #region IFactoryData Members

        /// <span class="code-SummaryComment"><summary></span>
        /// Flag this property as dependant so the Unity Framework passes this object
        /// the current Factory/Context
        /// <span class="code-SummaryComment"></summary></span>

        [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.

public class Factory : IFactory
    {

        /// <span class="code-SummaryComment"><summary></span>
        /// This constructor adds itself to the Unity Container
        /// <span class="code-SummaryComment"></summary></span>
        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;

       /// <span class="code-SummaryComment"><summary></span>
       /// Returns the IDataContext object requested from the container
       /// <span class="code-SummaryComment"></summary></span>
       /// <span class="code-SummaryComment"><typeparam name="T"></typeparam></span>
       /// <span class="code-SummaryComment"><returns></returns></span>

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

        /// <span class="code-SummaryComment"><summary></span>
        /// Return the current DataContext object for the factory
        /// <span class="code-SummaryComment"></summary></span>
        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.

<unity>

    <!--<span class="code-comment"> Define all of the types that we want to put into our container --></span>
    <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>

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

            </typeConfig>
          </type>
          <!--<span class="code-comment"> Our custom logic classes: --></span>
          <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)

Share

About the Author

Shannon Deminick
Web Developer The Farm Digital
Australia Australia
Shannon Deminick is the Technical Director of The Farm Digital, a Sydney based digital services agency.

Comments and Discussions

 
QuestionWould not be the same as a return to factory singleton datacontext? PinmemberMember 37709592-May-09 17:51 
AnswerRe: Would not be the same as a return to factory singleton datacontext? PinmemberShannon Deminick3-May-09 13:04 
QuestionHow to instantiate the DataContext PinmemberMember 38258777-Aug-08 10:51 
AnswerRe: How to instantiate the DataContext PinmemberShannon Deminick10-Aug-08 20:41 
GeneralRe: How to instantiate the DataContext PinmemberRanjan.D18-Jun-11 22:08 
GeneralA bit more info on Unity PinmemberLazar Mihai ( Sharp Override )15-Jul-08 19:13 
GeneralRe: A bit more info on Unity PinmemberShannonD15-Jul-08 19:19 
GeneralRe: A bit more info on Unity PinmemberLazar Mihai ( Sharp Override )15-Jul-08 20:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 24 Jul 2008
Article Copyright 2008 by Shannon Deminick
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid