Unity with WCF services

Apr 2, 2008 at 11:38 AM
Hi!

I plan to use Unity in combination with WCF services. Are there any guidance how to do this?
Currently I've the following question:

*) Where should I instantiate and configure my UnityContainer (inside my WCF service), so that the UnityContainer can be used between several WCF operations?

Thanks

Klaus Aschenbrenner
http://www.csharp.at
http://www.csharp.at/blog
Apr 2, 2008 at 2:17 PM
Edited Apr 2, 2008 at 2:26 PM
Hey,

if you want to use Unity container with WCF you may want to see previous discussion of this at Object creation and WCF. Please also note that (depends on what you want to do with Unity/WCF) WCF itself has own behavior and extension model. Any code explorations would be great to be shared here as I may one day need to take a look of this more in deep.

Regards,
Alexander
Apr 3, 2008 at 1:08 AM
Edited Apr 3, 2008 at 3:47 AM
I think your question is slightly different that the other referenced post.

I would create a singleton object that is your container, something like:
    /// <summary>
    /// 
    /// </summary>
    public static class Container
    {
        #region Fields
        private static readonly object syncRoot = new object();
        private static volatile IUnityContainer instance;
        #endregion
 
        /// <summary>
        /// Gets the current <see cref="UnityContainer"/> instance.
        /// </summary>
        /// <value>The current.</value>
        /// <remarks>
        /// Implements the singleton pattern as described in 
        /// http://msdn2.microsoft.com/en-us/library/ms998558.aspx
        /// </remarks>
        public static IUnityContainer Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                        {
                            instance = new UnityContainer();
                            UnityConfigurationSection section 
                                = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
                            if (section != null)
                                section.Containers.Default.GetConfigCommand().Configure(instance);
                        }
                    }
                }
                return instance;
            }
        }
    }

Phil


  • Where should I instantiate and configure my UnityContainer (inside my WCF service), so that the UnityContainer can be used between several WCF operations?

Apr 3, 2008 at 4:26 AM
Edited Apr 3, 2008 at 4:52 AM

The Prism project uses a Bootstrapper class, see http://www.codeplex.com/prism/SourceControl/FileView.aspx?itemId=121405&changeSetId=6267.

In the article, Hosting and Consuming WCF Services
http://msdn2.microsoft.com/en-us/library/bb332338.aspx, the section Accessing ServiceHost in IIS talks of how to use a CustomServiceHostFactory to control startup and shutdown code.


  • Where should I instantiate and configure my UnityContainer (inside my WCF service), so that the UnityContainer can be used between several WCF operations?

Apr 3, 2008 at 12:18 PM
I don’t think anyone has done this yet, so feel free to try. To give you a way forward I would use the same approach that is discussed in MSDN magazine article: PIAB and WCF -Integrating the Policy Injection Application Block with WCF Services. http://msdn2.microsoft.com/en-us/magazine/cc136759.aspx

You need to extend your WCF IEndpointBehavior and IContractBehavior in order to use unity in WCF. Please note, Unity is just an extension to PIAB or vice verse depends on the viewer, so don’t get confused of the article’s PIAB focus.


Regards,
Alexander
Apr 3, 2008 at 2:51 PM
Klaus, my posts assume you want to build dependant objects only within your service. Alexander's post link to samples that allow you to use Unity to create your serivce contract type. I think this second option would a cleaner implementation, however it may be a little more work. Good Luck, let us (and all other discussion forum users) know how it goes.
Apr 3, 2008 at 4:15 PM
I followed Oren Dennison's Post to hookup Unity w/ WCF. It's working for me. Sorry for lack of commenting:

Post
ur:lhttp://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html

using System;
using System.Collections.Generic;
using System.ServiceModel.Configuration;
using System.Text;

namespace AMG.WCF.Unity
{
public class UnityBehaviorExtensionElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new UnityServiceBehavior();
}

public override Type BehaviorType
{
get { return typeof(UnityServiceBehavior); }
}
}
}

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;

namespace AMG.WCF.Unity
{
public class UnityServiceBehavior : IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;

if (cd != null)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider =
new UnityInstanceProvider(serviceDescription.ServiceType);
}
}
}
}

public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }

public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
}
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Text;
using AMG.Unity;
using Microsoft.Practices.Unity;

namespace AMG.WCF.Unity
{
public class UnityInstanceProvider : IInstanceProvider
{
#region Private Fields

private readonly Type _serviceType;

private static IUnityContainer _container;
private static readonly object _lockObject = new object();

#endregion

#region Properties

protected static IUnityContainer Container
{
get
{
if (_container == null)
{
lock (_lockObject)
{
_container = CreateContainer();
}
}

return _container;
}
}

#endregion

#region Constructors

public UnityInstanceProvider(Type serviceType)
{
_serviceType = serviceType;
}

#endregion

#region IInstanceProvider Members

public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}

public object GetInstance(InstanceContext instanceContext, Message message)
{
return Container.Resolve(_serviceType);
}

public void ReleaseInstance(InstanceContext instanceContext, object instance) { }

#endregion

#region Private Methods

private static IUnityContainer CreateContainer()
{
IUnityContainer container = new UnityContainer();

IUnityConfigurator configurator = new WebConfigUnityConfigurator();

configurator.Configure(container);

return container;
}

#endregion
}
}

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.Unity;

namespace AMG.Unity
{
public interface IUnityConfigurator
{
/// <summary>
/// Configures a unity container
/// </summary>
void Configure(IUnityContainer container);
}
}

using System;
using System.Configuration;
using System.IO;
using System.Web.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

namespace AMG.Unity
{
public class WebConfigUnityConfigurator : IUnityConfigurator
{
#region Private Fields

private readonly string _baseDirectory;

#endregion

#region Constructors

/// <summary>
/// Initializes a new instance of <see cref="WebConfigUnityConfigurator"/>.
/// </summary>
public WebConfigUnityConfigurator()
: this(String.Empty)
{
}

/// <summary>
/// Initializes a new instance of <see cref="WebConfigUnityConfigurator"/>.
/// </summary>
/// <param name="baseDirectory">The directory from which to start searching for the
/// configuration files.</param>
public WebConfigUnityConfigurator(string baseDirectory)
{
_baseDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, baseDirectory);
}

#endregion

#region IUnityConfigurator Members

/// <summary>
/// Configures a unity container
/// </summary>
public void Configure(IUnityContainer container)
{
Configure(container, _baseDirectory);
}

#endregion

#region Private Methods

private static void Configure(IUnityContainer container, string rootDirectory)
{
foreach (string fileName in Directory.GetFiles(rootDirectory, "Web.Config", SearchOption.TopDirectoryOnly))
{
Configuration configuration = GetConfiguration(Path.Combine(rootDirectory, fileName));

ConfigurationSection section = configuration.GetSection("unity");

UnityConfigurationSection localSection = section as UnityConfigurationSection;

if (localSection != null)
{
foreach (UnityContainerElement element in localSection.Containers)
{
element.Configure(container);
}
}
}
foreach (string childDirectory in Directory.GetDirectories(rootDirectory))
{
Configure(container, childDirectory);
}
}

private static Configuration GetConfiguration(string configFilePath)
{
Configuration configuration;

System.Web.HttpContext context = System.Web.HttpContext.Current;

if (context == null)
{
configuration = GetConfigurationForCustomFile(configFilePath);
}
else
{
configuration = WebConfigurationManager.OpenWebConfiguration(context.Request.ApplicationPath + "/" + configFilePath.Replace(context.Request.PhysicalApplicationPath, ""));
}
return configuration;
}

private static Configuration GetConfigurationForCustomFile(string fileName)
{
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = fileName;
File.SetAttributes(fileMap.ExeConfigFilename, FileAttributes.Normal);
return ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
}

#endregion
}
}

<!--WCF Configuration-->
<extensions>
<behaviorExtensions>
<!-- unityBehavior extension
for dependency injection with Unity using
custom WCF InstanceProvider
-->
<add name="unityBehavior"
type="AMG.WCF.Unity.UnityBehaviorExtensionElement, AMG.WCF.Unity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceMetadata httpGetEnabled="true" />
<unityBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
Apr 3, 2008 at 4:16 PM
Edited Apr 3, 2008 at 4:17 PM
Wrapping your code in double braces {{ }} will retain the formatting

using System;
using System.Collections.Generic;
using System.ServiceModel.Configuration;
using System.Text;
 
namespace AMG.WCF.Unity
{
    public class UnityBehaviorExtensionElement : BehaviorExtensionElement
    {
        protected override object CreateBehavior()
        {
            return new UnityServiceBehavior();
        }
 
        public override Type BehaviorType
        {
            get { return typeof(UnityServiceBehavior); }
        }
    }
}
 
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
 
namespace AMG.WCF.Unity
{
    public class UnityServiceBehavior : IServiceBehavior
    {
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher cd = cdb as ChannelDispatcher;
 
                if (cd != null)
                {
                    foreach (EndpointDispatcher ed in cd.Endpoints)
                    {
                        ed.DispatchRuntime.InstanceProvider =
                            new UnityInstanceProvider(serviceDescription.ServiceType);
                    }
                }
            }
        }
 
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
                Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
 
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
    }
}
 
 
using System;
using System.Collections.Generic;
using System.Globalization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Text;
using AMG.Unity;
using Microsoft.Practices.Unity;
 
namespace AMG.WCF.Unity
{
    public class UnityInstanceProvider : IInstanceProvider
    {
        #region Private Fields
 
        private readonly Type _serviceType;
 
        private static IUnityContainer _container; 
        private static readonly object _lockObject = new object();
 
        #endregion
 
        #region Properties
 
        protected static IUnityContainer Container
        {
            get
            {
                if (_container == null)
                {
                    lock (_lockObject)
                    {
                        _container = CreateContainer();
                    }
                }
 
                return _container;
            }
        }
 
        #endregion
 
        #region Constructors
 
        public UnityInstanceProvider(Type serviceType)
        {
            _serviceType = serviceType;
        } 
 
        #endregion
 
        #region IInstanceProvider Members
 
        public object GetInstance(InstanceContext instanceContext)
        {
            return GetInstance(instanceContext, null);
        }
 
        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return Container.Resolve(_serviceType);
        }
 
        public void ReleaseInstance(InstanceContext instanceContext, object instance) { }
 
        #endregion
 
        #region Private Methods
 
        private static IUnityContainer CreateContainer()
        {
            IUnityContainer container = new UnityContainer();
 
            IUnityConfigurator configurator = new WebConfigUnityConfigurator();
 
            configurator.Configure(container);
 
            return container;
        } 
 
        #endregion
    }
}
 
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.Unity;
 
namespace AMG.Unity
{
    public interface IUnityConfigurator
    {
        /// <summary>
        /// Configures a unity container
        /// </summary>
        void Configure(IUnityContainer container);
    }
}
 
using System;
using System.Configuration;
using System.IO;
using System.Web.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
 
namespace AMG.Unity
{
    public class WebConfigUnityConfigurator : IUnityConfigurator
    {
        #region Private Fields
 
        private readonly string _baseDirectory; 
 
        #endregion
 
        #region Constructors
 
        /// <summary>
        /// Initializes a new instance of <see cref="WebConfigUnityConfigurator"/>.
        /// </summary>
        public WebConfigUnityConfigurator()
            : this(String.Empty)
        {
        }
 
        /// <summary>
        /// Initializes a new instance of <see cref="WebConfigUnityConfigurator"/>.
        /// </summary>
        /// <param name="baseDirectory">The directory from which to start searching for the 
        /// configuration files.</param>
        public WebConfigUnityConfigurator(string baseDirectory)
        {
            _baseDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, baseDirectory);
        } 
 
        #endregion
 
        #region IUnityConfigurator Members
 
        /// <summary>
        /// Configures a unity container
        /// </summary>
        public void Configure(IUnityContainer container)
        {
            Configure(container, _baseDirectory);
        }
 
        #endregion
 
        #region Private Methods
 
        private static void Configure(IUnityContainer container, string rootDirectory)
        {
            foreach (string fileName in Directory.GetFiles(rootDirectory, "Web.Config", SearchOption.TopDirectoryOnly))
            {
                Configuration configuration = GetConfiguration(Path.Combine(rootDirectory, fileName));
 
                ConfigurationSection section = configuration.GetSection("unity");
 
                UnityConfigurationSection localSection = section as UnityConfigurationSection;
 
                if (localSection != null)
                {
                    foreach (UnityContainerElement element in localSection.Containers)
                    {
                        element.Configure(container);
                    }
                }
            }
            foreach (string childDirectory in Directory.GetDirectories(rootDirectory))
            {
                Configure(container, childDirectory);
            }
        }
 
        private static Configuration GetConfiguration(string configFilePath)
        {
            Configuration configuration;
 
            System.Web.HttpContext context = System.Web.HttpContext.Current;
 
            if (context == null)
            {
                configuration = GetConfigurationForCustomFile(configFilePath);
            }
            else
            {
                configuration = WebConfigurationManager.OpenWebConfiguration(context.Request.ApplicationPath + "/" + configFilePath.Replace(context.Request.PhysicalApplicationPath, ""));
            }
            return configuration;
        }
 
        private static Configuration GetConfigurationForCustomFile(string fileName)
        {
            ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
            fileMap.ExeConfigFilename = fileName;
            File.SetAttributes(fileMap.ExeConfigFilename, FileAttributes.Normal);
            return ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
        } 
 
        #endregion
    }
}
 
<!--WCF Configuration-->
<extensions>
  <behaviorExtensions>
    <!-- unityBehavior extension
             for dependency injection with Unity using
             custom WCF InstanceProvider
        -->
    <add name="unityBehavior"
         type="AMG.WCF.Unity.UnityBehaviorExtensionElement, AMG.WCF.Unity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </behaviorExtensions>
</extensions>
<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBehavior">
      <serviceDebug includeExceptionDetailInFaults="false" />
      <serviceMetadata httpGetEnabled="true" />
      <unityBehavior />
    </behavior>
  </serviceBehaviors>
</behaviors>

Apr 7, 2008 at 4:02 PM
If i remember quite well, a lot of people and myself included asked Ayende to implement an external integration from Windsor to WCF. The WCF Facility was born. Lately i've looked at what it became thanks to the amazing job Craig Neuwirt did with Ayende's initial implementation. You will find the details of this beast there:
http://groups.google.com/group/castle-project-devel/browse_thread/thread/4435ad57f89e61a5?hl=en

I'm currently looking at the implementation in order to see if i could port it to Unity or at least be inspired by the ideas beneath it. The only limitation i see right now is due to the missing interception mechanism in Unity. I hope the p&p team will take the opportunity they have to rethink the PIAB + Unity Interactions or merge :p

Apr 8, 2008 at 12:28 AM
Edited Apr 8, 2008 at 6:13 AM
Prototyped the below solution for an issue similar to Klaus’ which should guarantee that the unity container is created only once per AppDomain. It should also be possible to host the WCF application using Unity in IIS, WAS, Windows Service, BizTalk 2006 etc.

Cheers,
Tim Bajz.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
 
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.StaticFactory;
using Microsoft.Practices.Unity.Configuration;
 
namespace Sample.Enterprise.Helpers
{
    public class UnityHelper
    {
        /// <summary>
        /// 
        /// </summary>
        public static UnityContainer Container
        {
            get { return GetContainer(); }
        }
 
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private static UnityContainer GetContainer()
        {
            AppDomain appDomain = AppDomain.CurrentDomain;
            UnityContainer container = (UnityContainer)appDomain.GetData("UnityContainer");
 
            if (container == null)
            {
                Singleton singleton = Singleton.Instance;
                container = (UnityContainer)appDomain.GetData("UnityContainer");
            }
 
            return container;
        }
 
        private sealed class Singleton
        {
            /// <summary>
            /// 
            /// </summary>
            private Singleton()
            {
                AppDomain appDomain = AppDomain.CurrentDomain;
                UnityContainer container = new UnityContainer();
 
                UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
                section.Containers.Default.Configure(container);
              
                appDomain.SetData("UnityContainer", container);
            }
 
            /// <summary>
            /// 
            /// </summary>
            public static Singleton Instance
            {
                get
                {
                    return InnerSingleton.instance;
                }
            }
 
            /// <summary>
            /// 
            /// </summary>
            private class InnerSingleton
            {
                // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
                static InnerSingleton()
                { }
 
                internal static readonly Singleton instance = new Singleton();
            }
        }
    }
}
Apr 14, 2008 at 9:46 AM
Hello, Tim.
Could you comment a bit why have you used two nested classed (Singleton and InnerSingleton) ?
Apr 16, 2008 at 12:39 PM
This link should clarify: http://www.yoda.arachsys.com/csharp/singleton.html


EvilShrike wrote:
Hello, Tim.
Could you comment a bit why have you used two nested classed (Singleton and InnerSingleton) ?

Apr 17, 2008 at 1:10 PM
The link posted should hopefully clarify the various pros and cons.
Hope that helps…

{quote}
windywinter wrote:
It's getting slightly off topic, but I couldn't resist to ask:
Your Singleton class is already nested class, usage of this class happens only from static Container property, so are you sure that such double/triple checking is necessary?


TimBajz wrote:
This link should clarify: http://www.yoda.arachsys.com/csharp/singleton.html


Apr 18, 2008 at 7:00 AM
Hi Wojciech,

1. Original code runs inside BizTalk 2006 which has special lifetime requirements
2. Do you believe a race condition can be produced?

Tim.

{quote}
windywinter wrote:
Could you answer on following questions?
1. Why you need something-like cross-app-domain singleton?
2. You are using thread-safe version of singleton, but are you sure that UnityHelper.GetContainer is thread safe it self?

Regards,
Wojciech Gebczyk


TimBajz wrote:
The link posted should hopefully clarify the various pros and cons.
Hope that helps…


windywinter wrote:
It's getting slightly off topic, but I couldn't resist to ask:
Your Singleton class is already nested class, usage of this class happens only from static Container property, so are you sure that such double/triple checking is necessary?


TimBajz wrote:
This link should clarify: http://www.yoda.arachsys.com/csharp/singleton.html




Apr 18, 2008 at 12:14 PM
In the BizTalk runtime objects can be dehydrated and hydrated. The following site has useful information: http://www.traceofthought.net which might help (log4net blog posts in particular).

What is your simpler preference for allowing the Unity container to be accessible?

Tim.

{quote}
windywinter wrote:
1. As I'm not aware of those special cross-domain access of BizTalk Server - could you point me some documentation that requires such behavior?
I would be very thankful for any documentation that explains such needs. Unfortunately article that you have pointed me previously does not mention BizTalk "quirks".
Till now you have mentioned 2 reasons of using your snipped code:
A. "guarantee that the unity container is created only once per AppDomain"
B. "host the WCF application using Unity in IIS, WAS, Windows Service, BizTalk 2006 etc. "
If the only requirement would be A then much, much, much more simpler code will work fine without any problems. That's why I'm asking about any clues why you need Get/Set Data on AppDomain to achieve your goals?
I will read with pleasure why anyone of those cited by you hosting environments requires special treating.

2. Right now I don't see any such problems. My mistake - yesterday I thought that there is one, but I was wrong.


Wojciech Gebczyk


TimBajz wrote:
Hi Wojciech,

1. Original code runs inside BizTalk 2006 which has special lifetime requirements
2. Do you believe a race condition can be produced?

Tim.


Apr 21, 2008 at 9:33 AM
Wojciech,

My example does not create one Unity container across AppDomains.
Hope that helps.

Tim.

{quote}
windywinter wrote:
Tim,

1. I'm looking in Google to find more info about (de)hydrating orchestration, because till now it seem that it describes process of (de)serialization of some running process state.
So your possible intention would be to save some time on dehyrdating and after deserialization container shouldn't be reinitialized, but:
- UnityContainer is not binary serializable - example: "new BinaryFormatter().Serialize(new MemoryStream(), new UnityContainer())"
- Use AppDomain's data "hashtable" to prevent serialization of static fields - but according to one blog text static fields are not serialzable in BizTalk during hydrating
So still I don't have any idea why Get/Set Data is used in your example.

2. Look at below code sample. In my opinion each domain will have separate singleton instances. (Run code with defined CROSS_DOMAIN and without this conditional compilation label, to see differences)

#define CROSS_DOMAIN
 
using System;
 
namespace ConsoleApplication5 {
    static class Program {
        static void Main(string[] args) {
#if CROSS_DOMAIN
            var o = new object[32];
            var i = 0;
 
            var domain = AppDomain.CreateDomain("x");
            o[i++] = RootClass.StringA;
            o[i++] = RootClass.InstanceA;
            domain.DoCallBack(delegate { Program.Test(); });
#endif
            Test();
 
            Console.ReadKey();
        }
        static void Test() {
            var o = new object[32];
            var i = 0;
 
            o[i++] = RootClass.StringA;
            o[i++] = RootClass.InstanceA;
            o[i++] = RootClass.StringB;
            o[i++] = RootClass.InstanceB;
        }
    }
 
    sealed class ClassA {
        public ClassA() { Console.WriteLine("{0}: ClassA.ctor", AppDomain.CurrentDomain.Id); }
    }
    sealed class ClassB {
        public ClassB() { Console.WriteLine("{0}: ClassB.ctor", AppDomain.CurrentDomain.Id); }
    }
    static class RootClass {
        public static ClassA InstanceA { get { return InnerA.Instance; } }
        public static ClassB InstanceB { get { return InnerB.Instance; } }
        public static readonly string StringA = "aaa";
        public static readonly string StringB = "bbb";
 
        static class InnerA {
            static InnerA() {}
            public static readonly ClassA Instance = new ClassA();
        }
        static class InnerB {
            static InnerB() {}
            public static readonly ClassB Instance = new ClassB();
        }
    }
}

3. Honestly I don't have much experience (In fact I've done very small task/work in prev ver of BizTalk) so probably I'm missing something.
4. And last question is for necessity of rather atrificial Singleton type with nested InnerSingeton types - in my opinion Singleton type can be removed and InnterSingleton moved to UnityHelper type.

What do you think?

Wojciech Gebczyk


TimBajz wrote:
In the BizTalk runtime objects can be dehydrated and hydrated. The following site has useful information: http://www.traceofthought.net which might help (log4net blog posts in particular).

What is your simpler preference for allowing the Unity container to be accessible?

Tim.


Apr 22, 2008 at 5:52 AM
Hi Wojciech,

The use of GetData and SetData are specifically to also allow Unity to be hosted inside BizTalk 2006. The links I provided explain the reasoning. As for the use of the inner singleton it is optional however I believe the use of singletons is preferable over locking solutions mentioned elsewhere.

Tim.


windywinter wrote:
Tim,

Then could you tell me on following questions:
1. Why you have used Get/Set Data on AppDomain to achieve your solution instead on much more simpler code?
2. Why you propose to use nested type with nested type to implement singleton instead of one nested type and still it be lazy loading thread safe solution?

Probably from your perspective you have answered on my questions pointing to "http://www.yoda.arachsys.com/csharp/singleton.html" article and BizTalk requirements.
Could you make clear statement(s) that describe reasons of your solution. Or simply quote from cited article about singleton and BizTalk requirement those parts that directly unveils reasons of your code composition.

Generally I don't understand your proposal as in my opinion is is heavily overcomplicated. I'm sure that it is working, but this is not necessary simplest (or one of the simplest) ways to achieve this same effect with much less(simplier) code.

But maybe I'm wrong...

Wojciech Gebczyk


TimBajz wrote:
Wojciech,

My example does not create one Unity container across AppDomains.
Hope that helps.

Tim.


Apr 22, 2008 at 12:48 PM
Wojciech,

Are you in a position to test your much simpler non-mashup use of the Unity container within a BizTalk 2006 orchestration? Through your persistence and interest we’ll either prove Get/Set on AppDomains actually does something useful or you’ll enlighten me on how it can be done in a much simpler way and for it all to still work with the hosts mentioned :-)

Tim.