自定義Unity物件生命週期管理整合ADO.NET Entity Framework

NO IMAGE

在Unity中,從Unity 取得的例項為 Transient。如果你希望使用多執行緒方式,就需要在組成時使用lifecycle引數,這時候取出的元件就不再是同一個了。在Unity IOC中,它支援我們對於元件的例項進行控制,也就是說我們可以透明的管理一個元件擁有多少個例項。Unity IOC容器提供瞭如下幾種生命處理方式:
# Singleton:一個元件只有一個例項被建立,所有請求的客戶使用程式得到的都是同一個例項。
# Transient:這種處理方式與我們平時使用new的效果是一樣的,對於每次的請求得到的都是一個新的例項。
# Custom:自定義的生命處理方式。

我要增加一個Request的,一個Request請求一個例項,然後在Request結束的時候,回收資源。 增加一個Resquest級別的LifetimeManager,HttpContext.Items中資料是Request期間共享資料用的,所以HttpContext.Items中放一個字典,用型別為key,型別的例項為value。如果當前Context.Items中有型別的例項,就直接返回例項。 ObjectContext本身是有快取的,整個Request內都是一個ObjectContext,ObjectContext一級快取能力進一步利用。

用在Unity中,如何獲取物件的例項及如何銷燬物件都是由LifetimeManager完成的,其定義如下
public abstract class LifetimeManager : ILifetimePolicy, IBuilderPolicy
{
    protected LifetimeManager();

    public abstract object GetValue();
    public abstract void RemoveValue();
    public abstract void SetValue(object newValue);
}

其中GetValue方法獲取物件例項,RemoveValue方法銷燬物件,SetValue方法為對外引用的儲存提供新的例項

有了這3個方法,就可以通過自定義LifetimeManager來實現從HttpContext中取值。

下面我們來實現Unity整合ADO.NET Entity Framework的工作:

1、利用Unity的依賴注入,ObjectContext會給我們生成3個建構函式,類似於下面的程式碼:

// Original file name:
// Generation date: 2008/8/24 10:05:33
namespace RequestLifeTimeManagerTest
{
    using Microsoft.Practices.Unity;

    /// <summary>
    /// There are no comments for AdventureWorksLTEntities in the schema.
    /// </summary>
    public partial class AdventureWorksLTEntities : global::System.Data.Objects.ObjectContext
    {
        /// <summary>
        /// Initializes a new AdventureWorksLTEntities object using the connection string found in the ‘AdventureWorksLTEntities’ section of the application configuration file.
        /// </summary>
        public AdventureWorksLTEntities() :
                base(“name=AdventureWorksLTEntities”, “AdventureWorksLTEntities”)
        {
            this.OnContextCreated();
        }
        /// <summary>
        /// Initialize a new AdventureWorksLTEntities object.
        /// </summary>
        public AdventureWorksLTEntities(string connectionString) :
                base(connectionString, “AdventureWorksLTEntities”)
        {
            this.OnContextCreated();
        }
        /// <summary>
        /// Initialize a new AdventureWorksLTEntities object.
        /// </summary>
        public AdventureWorksLTEntities(global::System.Data.EntityClient.EntityConnection connection) :
                base(connection, “AdventureWorksLTEntities”)
        {
            this.OnContextCreated();
        }
        partial void OnContextCreated();

……

}

建構函式注入包含了二種情況,一種是類僅有一個建構函式時,Unity 可以進行自動注入;另一種情況是,類包含多個建構函式時,必須使用 Attribute 或者配置檔案指定注入時使用的建構函式。

ObjectContext有多個建構函式,而且ObjectContext的建構函式程式碼是Visual Studio 程式碼生成的,最好的選擇是使用配置檔案或者使用配置API指定注入時使用的建構函式。下面是使用配置API:

namespace RequestLifeTimeManagerTest
{
    public class EFContainerExtension : UnityContainerExtension  
    {
        protected override void Initialize()
        {
           this.Container.RegisterType<AdventureWorksLTEntities, AdventureWorksLTEntities>(new RequestControlledLifetimeManager(typeof(AdventureWorksLTEntities)))
                .Configure<InjectedMembers>()
                    .ConfigureInjectionFor<AdventureWorksLTEntities>(new InjectionConstructor());

        }
    }
}

我們定義了一個Unity擴充套件,在擴充套件類EFContainerExtension 我們選擇了第一個建構函式以及ObjectContext使用RequestControlledLifetimeManager實現ObjectContext的生命週期管理。

2、實現RequestControlledLifetimeManager,完成對整個Request內都是一個ObjectContext的物件的生命週期管理:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Practices.Unity;

namespace RequestLifeTimeManagerTest
{
    public class RequestControlledLifetimeManager : LifetimeManager
    {
        private Type objectType;

        /// <summary>  
        ///   
        /// </summary>  
        /// <param name=”t”></param>  
        public RequestControlledLifetimeManager(Type t)
        {
            this.objectType = t;
        }

        private IDictionary<Type, object> GetObjectTable()
        {
            IDictionary<Type, object> objects = HttpContext.Current.Items[RequestLifeTimeManagerTest.UnityHttpModule.UNITYOBJECTS]
                as IDictionary<Type, object>;
            if (objects == null)
            {
                lock (this)
                {
                    if (HttpContext.Current.Items[RequestLifeTimeManagerTest.UnityHttpModule.UNITYOBJECTS] == null)
                    {
                        objects = new Dictionary<Type, object>();
                        HttpContext.Current.Items[RequestLifeTimeManagerTest.UnityHttpModule.UNITYOBJECTS] = objects;
                    }
                    else
                    {
                        return HttpContext.Current.Items[RequestLifeTimeManagerTest.UnityHttpModule.UNITYOBJECTS]
                            as IDictionary<Type, object>;
                    }
                }
            }
            return objects;
        }

        public override object GetValue()
        {
            IDictionary<Type, object> objects = this.GetObjectTable();
            object obj = null;
            if (objects.TryGetValue(this.objectType, out obj))
            {
                return obj;
            }
            return null;
        }

        public override void RemoveValue()
        {
            IDictionary<Type, object> objects = this.GetObjectTable();
            object obj = null;
            if (objects.TryGetValue(this.objectType, out obj))
            {
                ((IDisposable)obj).Dispose();
                objects.Remove(this.objectType);
            }
        }

        public override void SetValue(object newValue)
        {
            IDictionary<Type, object> objects = this.GetObjectTable();
            objects.Add(this.objectType, newValue);
        }
    }
}

寫一個HttpMoudle,在Request結束的時候回收資源。

 

using System;
using System.Web;
using System.Collections.Generic;

namespace RequestLifeTimeManagerTest
{
    public class UnityHttpModule : IHttpModule
    {
        internal const string UNITYOBJECTS = “UNITYOBJECTS”;
        #region IHttpModule Members

        public void Dispose()
        {
            //clean-up code here.
        }

        public void Init(HttpApplication context)
        {
            context.EndRequest = new EventHandler(context_EndRequest);

        }

        #endregion

        private void context_EndRequest(object sender, EventArgs e)
        {
            IDictionary<Type, object> objects = HttpContext.Current.Items[UNITYOBJECTS]
                as IDictionary<Type, object>;
            if (objects != null)
            {
                foreach (Type key in objects.Keys)
                {
                    if (objects[key] is IDisposable)
                    {
                        ((IDisposable)objects[key]).Dispose();
                    }
                }
                HttpContext.Current.Items.Remove(UNITYOBJECTS);
            }
        }

    }
}

3、web.config中的配置檔案內容如下,注意看紅色部分:

<?xml version=”1.0″?>
<configuration>
    <configSections>
        <sectionGroup name=”system.web.extensions” type=”System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″>
            <sectionGroup name=”scripting” type=”System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″>
                <section name=”scriptResourceHandler” type=”System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication” />
                <sectionGroup name=”webServices” type=”System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″>
                    <section name=”jsonSerialization” type=”System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”Everywhere” />
                    <section name=”profileService” type=”System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication” />
                    <section name=”authenticationService” type=”System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication” />
                    <section name=”roleService” type=”System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication” />
                </sectionGroup>
            </sectionGroup>
        </sectionGroup>
        <section name=”unity” type=”Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration” />
    </configSections>
    <unity>
        <typeAliases>
      <typeAlias alias=”string” type=”System.String, mscorlib” />
            <typeAlias alias=”singleton” type=”Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity” />
            <typeAlias alias=”transient” type=”Microsoft.Practices.Unity.TransientLifetimeManager, Microsoft.Practices.Unity” />
    </typeAliases>
        <containers>  
            <container>
        <types>
          <type type=”RequestLifeTimeManagerTest.Gateways.IProductGateway,RequestLifeTimeManagerTest” mapTo=”RequestLifeTimeManagerTest.Gateways.ProductGateway, RequestLifeTimeManagerTest”>
          </type>
        </types>
        <extensions>
          <add type=”RequestLifeTimeManagerTest.EFContainerExtension, RequestLifeTimeManagerTest” />
        </extensions>
            </container>
        </containers>
    </unity>

    <appSettings />
    <connectionStrings><add name=”AdventureWorksLTEntities” connectionString=”metadata=res://*/AdventureWorksModel.csdl|res://*/AdventureWorksModel.ssdl|res://*/AdventureWorksModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=GEFF-PC;Initial Catalog=AdventureWorksLT;Integrated Security=True;MultipleActiveResultSets=True&quot;” providerName=”System.Data.EntityClient” /></connectionStrings>
    <system.web>
        <!–
            Set compilation debug=”true” to insert debugging
            symbols into the compiled page. Because this
            affects performance, set this value to true only
            during development.
        –>
        <compilation debug=”true”>
            <assemblies>
                <add assembly=”System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″ />
                <add assembly=”System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″ />
                <add assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
                <add assembly=”System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″ />
            <add assembly=”System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″ /></assemblies>
        </compilation>
        <!–
            The <authentication> section enables configuration
            of the security authentication mode used by
            ASP.NET to identify an incoming user.
        –>
        <authentication mode=”Windows” />
        <!–
            The <customErrors> section enables configuration
            of what to do if/when an unhandled error occurs
            during the execution of a request. Specifically,
            it enables developers to configure html error pages
            to be displayed in place of a error stack trace.

        <customErrors mode=”RemoteOnly” defaultRedirect=”GenericErrorPage.htm”>
            <error statusCode=”403″ redirect=”NoAccess.htm” />
            <error statusCode=”404″ redirect=”FileNotFound.htm” />
        </customErrors>
        –>
        <pages>
            <controls>
                <add tagPrefix=”asp” namespace=”System.Web.UI” assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
                <add tagPrefix=”asp” namespace=”System.Web.UI.WebControls” assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
            </controls>
        </pages>
        <httpHandlers>
            <!–<remove verb=”*” path=”*.aspx”/>
            <add verb=”*” path=”*.aspx” type=”RequestLifeTimeManagerTest.UnityHttpHandlerFactory, RequestLifeTimeManagerTest”/>–>
            <remove verb=”*” path=”*.asmx” />
            <add verb=”*” path=”*.asmx” validate=”false” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
            <add verb=”*” path=”*_AppService.axd” validate=”false” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
            <add verb=”GET,HEAD” path=”ScriptResource.axd” type=”System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ validate=”false” />
        </httpHandlers>
        <httpModules>
            <add name=”ScriptModule” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />

       <add name=”UnityModule” type=”RequestLifeTimeManagerTest.UnityHttpModule,RequestLifeTimeManagerTest”/>           
        </httpModules>
    </system.web>
    <system.codedom>
        <compilers>
            <compiler language=”c#;cs;csharp” extension=”.cs” warningLevel=”4″ type=”Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″>
                <providerOption name=”CompilerVersion” value=”v3.5″ />
                <providerOption name=”WarnAsError” value=”false” />
            </compiler>
        </compilers>
    </system.codedom>
    <!–
        The system.webServer section is required for running ASP.NET AJAX under Internet
        Information Services 7.0.  It is not necessary for previous version of IIS.
    –>
    <system.webServer>
        <validation validateIntegratedModeConfiguration=”false” />
        <modules>
            <remove name=”ScriptModule” />
            <add name=”ScriptModule” preCondition=”managedHandler” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
        </modules>
        <handlers>
            <remove name=”WebServiceHandlerFactory-Integrated” />
            <remove name=”ScriptHandlerFactory” />
            <remove name=”ScriptHandlerFactoryAppServices” />
            <remove name=”ScriptResource” />
            <add name=”ScriptHandlerFactory” verb=”*” path=”*.asmx” preCondition=”integratedMode” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
            <add name=”ScriptHandlerFactoryAppServices” verb=”*” path=”*_AppService.axd” preCondition=”integratedMode” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
            <add name=”ScriptResource” preCondition=”integratedMode” verb=”GET,HEAD” path=”ScriptResource.axd” type=”System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ />
        </handlers>
    </system.webServer>
    <runtime>
        <assemblyBinding xmlns=”urn:schemas-microsoft-com:asm.v1″>
            <dependentAssembly>
                <assemblyIdentity name=”System.Web.Extensions” publicKeyToken=”31bf3856ad364e35″ />
                <bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″ />
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name=”System.Web.Extensions.Design” publicKeyToken=”31bf3856ad364e35″ />
                <bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″ />
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>