Jan 28 2010

Injecting AppSetting values via Unity

Category: .NetRory Primrose @ 08:14

I've been working with Unity a bit in an enterprise level system and I have been trying to separate objects of different concerns as much as possible. One requirement hit me today where I have a dependency that is resolved from a Unity container. I created a cache dependency layer to go around it, but needed to provide a configuration value to the cache wrapper.

To demonstrate this scenario, consider the following example:

using System;

namespace Neovolve.UnityTesting
{
    public interface IDoSomething
    {
        String Execute();
    }

    public class SomethingDone : IDoSomething
    {
        public String Execute()
        {
            return"Some random value";
        }
    }

    public class CachedSomethingDone : IDoSomething
    {
        private readonly IDoSomething _dependency;

        public CachedSomethingDone(IDoSomething dependency, Int64 maxAgeInMilliseconds)
        {
            _dependency = dependency;
            MaxAgeInMilliseconds = maxAgeInMilliseconds;
        }

        public String Execute()
        {
            // Check cache for value
            // If cache has value and is not too old then return the value
            // If not, get value from dependency, cache it for the next call and return the value

            return _dependency.Execute();
        }

        public long MaxAgeInMilliseconds
        {
            get;
            private set;
        }
    }
}

Unfortunately Unity does not provide a way to resolve a configuration value and inject it into the instance being resolved. This means that the configuration value must be injected as a literal value defined in the unity configuration. While this works, most people think in terms of the appSettings element when configuring the options for their application. This would require them to also review the often complex unity configuration to adjust a required value. I wanted a way to essentially redirect the appSetting value into a unity injection value.

I spent a bit of time cruising around the Enterprise Library source with Reflector to see what could be done about this. Unity natively understands injection configuration for dependencies, literal values and array types. Unity happens to make a call down to InjectionParameterValueHelper.DeserializeUnrecognizedElement() which has a switch over the element name (being value, dependency or array). If the type is not understood then it makes a call out to DeserializePolymorphicElement().

The DeserializePolymorphicElement method is the key. It uses an elementType attribute that gets resolved as InjectionParameterValueElement. This is the point at which Unity can be extended to provide a custom injection implementation. I created an AppSettingsParameterInjectionElement class that is mostly a copy of InstanceValueElement which is the unity definition for injection configuration of literal values. The class looks like the following:

using System;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

namespace Neovolve.UnityTesting
{
    public class AppSettingsParameterInjectionElement : InjectionParameterValueElement
    {
        public Object CreateInstance()
        {
            String configurationValue = ConfigurationManager.AppSettings[AppSettingKey];
            Type typeToCreate = TypeToCreate;

            if (typeToCreate == typeof(String))
            {
                return configurationValue;
            }

            TypeConverter converter = GetTypeConverter(typeToCreate, TypeConverterName, TypeResolver);

            try
            {
                return converter.ConvertFromString(configurationValue);
            }
            catch (NotSupportedException ex)
            {
                const String MessageFormat =
                    "The AppSetting with key '{0}' and value '{1}' cannot be converted to type '{2}'";
                String settingValue = configurationValue ?? "(null)";

                String failureMessage = String.Format(
                    CultureInfo.InvariantCulture, MessageFormat, AppSettingKey, settingValue, typeToCreate.FullName);
                
                throw new ConfigurationErrorsException(failureMessage, ex);
            }
        }

        public override InjectionParameterValue CreateParameterValue(Type targetType)
        {
            Type type;

            if (String.IsNullOrEmpty(TypeName))
            {
                type = targetType;
            }
            else
            {
                type = TypeResolver.ResolveType(TypeName);
            }

            return new InjectionParameter(type, CreateInstance());
        }

        private static TypeConverter GetTypeConverter(
            Type typeToCreate, String typeConverterName, UnityTypeResolver typeResolver)
        {
            if (!String.IsNullOrEmpty(typeConverterName))
            {
                return (TypeConverter)Activator.CreateInstance(typeResolver.ResolveType(typeConverterName));
            }

            return TypeDescriptor.GetConverter(typeToCreate);
        }

        [ConfigurationProperty("appSettingKey", IsRequired = true)]
        public String AppSettingKey
        {
            get
            {
                return (String)base["appSettingKey"];
            }

            set
            {
                base["appSettingKey"] = value;
            }
        }

        [ConfigurationProperty("typeConverter", IsRequired = false, DefaultValue = null)]
        public String TypeConverterName
        {
            get
            {
                return (String)base["typeConverter"];
            }

            set
            {
                base["typeConverter"] = value;
            }
        }

        [ConfigurationProperty("type", DefaultValue = null)]
        public String TypeName
        {
            get
            {
                return (String)base["type"];
            }

            set
            {
                base["type"] = value;
            }
        }

        public Type TypeToCreate
        {
            get
            {
                return TypeResolver.ResolveWithDefault(TypeName, typeof(String));
            }
        }
    }
}

The configuration for this looks like the following:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <section name="unity"
             type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
  </configSections>

  <appSettings>
    <add key="MyTestSetting"
         value="234234" />
  </appSettings>

  <unity>
    <containers>
      <container>
        <types>

          <type type="Neovolve.UnityTesting.IDoSomething, Neovolve.UnityTesting"
                mapTo="Neovolve.UnityTesting.CachedSomethingDone, Neovolve.UnityTesting">
            <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement,
                                     Microsoft.Practices.Unity.Configuration">
              <constructor>
                <param name="dependency"
                       parameterType="Neovolve.UnityTesting.IDoSomething, Neovolve.UnityTesting">
                  <dependency name="CacheSomething" />
                </param>
                <param name="maxAgeInMilliseconds"
                       parameterType="System.Int64, mscorlib, Version=2.0.0.0">
                  <appSetting elementType="Neovolve.UnityTesting.AppSettingsParameterInjectionElement, Neovolve.UnityTesting"
                              appSettingKey="MyTestSetting"
                              type="System.Int64, mscorlib, Version=2.0.0.0"/>
                </param>
              </constructor>
            </typeConfig>
          </type>

          <type type="Neovolve.UnityTesting.IDoSomething, Neovolve.UnityTesting"
                mapTo="Neovolve.UnityTesting.SomethingDone, Neovolve.UnityTesting"
                name="CacheSomething">
          </type>

        </types>
      </container>
    </containers>
  </unity>
</configuration>

The test application to tie all this together is the following:

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

namespace Neovolve.UnityTesting
{
    class Program
    {
        static void Main(string[] args)
        {
            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            UnityContainer container = new UnityContainer();

            section.Containers.Default.Configure(container);

            CachedSomethingDone something = (CachedSomethingDone)container.Resolve();

            Console.WriteLine("Dependency configured with max age: " + something.MaxAgeInMilliseconds);
            Console.ReadKey();
        }
    }
}

In this process I found that it would be nice if the InstanceValueElement class was designed to allow for easier extension. The two likely options here are to allow the CreateInstance method or the Value property to be overridden. This would mean that the above class would only have to resolve the application setting rather than essentially duplicate the logic in InstanceValueElement.

Neovolve.UnityTesting.zip (8.63 kb)

Tags:

Jan 15 2010

Filtering items in the MSF 2.0

Category: .NetRory Primrose @ 11:56

I’ve been playing with the Microsoft Sync Framework over the last couple of years. For too long I have been dabbling in a services based project that synchronises data between a set of clients. The system has been through several designs starting with hand-crafted change tracking which was really tricky. The next version used Microsoft Sync Framework on the client manage this process. This didn’t have the best result as there was no central replica for the data held by the service. The third design used a proxy provider so that central metadata information was held by the service. The fourth and latest design completely pushes Microsoft Sync Framework into the service.

The latest design allows clients to simply work with services and not have to have any understanding of Microsoft Sync Framework. There are a few hurdles with this design however. The provider implementation on the server needs to implement a preview sync so it can tell the client what changes needs to happen without doing them at that time. When a change does happens, the client will only action a single change at a time each of which must operate within a sync session in the service. This means that the sync provider also needs to work with a filtered sync session.

I created a POC project to prove that I could actually achieve these features with Microsoft Sync Framework before I invested any more time in the latest design. The POC aims to sync a data item that looks like the following.

using System;

namespace CachedSyncPOC
{
    public class ItemData
    {
        public ItemData()
        {
            Id = Guid.NewGuid().ToString();
            Data = Guid.NewGuid().ToString();
        }

        public String Id
        {
            get;
            set;
        }

        public String Data
        {
            get;
            set;
        }
    }
}

The code aims to store both the Id and the Data of each item in the metadata store of each replica.

Preview mode

This was actually easy to implement and is done in two parts.

The first part is that the provider notifies any interested parties of changes found using a custom event raised in GetChangeBatch.

public override ChangeBatch GetChangeBatch(
    UInt32 batchSize, SyncKnowledge destinationKnowledge, out Object changeDataRetriever)
{
    ChangeBatch batch = Metadata.GetChangeBatch(batchSize, destinationKnowledge); 

    IList changes = new List(batch.Count());
    ItemDataRetriever retriever = new ItemDataRetriever(Metadata);

    foreach (ItemChange change in batch)
    {
        changes.Add(retriever.LoadFromSyncId(change.ItemId));
    }

    OnChangesFound(
        new ChangesFoundEventArgs
        {
            Changes = changes,
            ReplicaId = ReplicaId.GetGuidId()
        });

    changeDataRetriever = retriever;

    return batch;
}

The second part is that the ProcessChangeBatch simply ignores any changes when in preview mode.

public override void ProcessChangeBatch(
    ConflictResolutionPolicy resolutionPolicy, 
    ChangeBatch sourceChanges, 
    Object changeDataRetriever, 
    SyncCallbacks syncCallbacks, 
    SyncSessionStatistics sessionStatistics)
{
    if (IsPreview)
    {
        return;
    }

    // Use a NotifyingChangeApplier object to process the changes. 
    // This object is passed as the INotifyingChangeApplierTarget
    // object that will be called to apply changes to the item store.
    NotifyingChangeApplier changeApplier = new NotifyingChangeApplier(IdFormats);
    INotifyingChangeApplierTarget2 applier = new ItemDataChangeApplier(this, Metadata, Filter);

    changeApplier.ApplyChanges(
        resolutionPolicy, 
        Configuration.CollisionConflictResolutionPolicy, 
        sourceChanges, 
        (IChangeDataRetriever)changeDataRetriever, 
        Metadata.GetKnowledge(), 
        Metadata.GetForgottenKnowledge(), 
        applier, 
        null, 
        SessionContext, 
        syncCallbacks);
}

This example is a little simplistic in that the event does not indicate what action is going to be taken for each item but that should be easily implemented down the track.

Filtering

The Microsoft Sync Framework team has provided many examples on how to use the framework. My initial reaction to the custom filtering sample was one of complete dread. My filtering requirements are simple and the sample code provided is very complex. I posted a question on the Microsoft Sync Framework forum to seek some advice. While the advice was good, it unfortunately pushed me back into the provided filter sample code.

As I started to look through the code, I realised that there were a couple of different types of filtering being demonstrated. I came across a MSDN document about Microsoft Sync Framework filtering (here) and found that what I needed wasn’t actually that complex. I need to filter an item in a session rather than filter a change unit or implement full custom filtering. It is the latter two that are demonstrated in the sync filtering sample code.

You need to update the provider implementation and provide a filter type in order to filter an item in a sync session. The filter type in my example works with the Id property of the filter item and provides logic for comparing filters between providers.

using System;
using Microsoft.Synchronization;

namespace CachedSyncPOC
{
    public class ItemDataFilter : ISyncFilter
    {
        public ItemDataFilter(String id)
        {
            if (String.IsNullOrEmpty(id))
            {
                const String IdParameterName = "id";

                throw new ArgumentNullException(IdParameterName);
            }

            Id = id;
        }

        public Boolean IsIdentical(ISyncFilter otherFilter)
        {
            ItemDataFilter itemFilter = otherFilter as ItemDataFilter;

            return itemFilter != null && Id.Equals(itemFilter.Id);
        }

        public Byte[] Serialize()
        {
            throw new NotImplementedException();
        }

        public String Id
        {
            get;
            set;
        }
    }
}

The provider needs to support a couple of filter interfaces. I need to implement both interfaces as I intend on using the same provider as both source and destination provider.

internal class CustomProvider : KnowledgeSyncProvider, ISupportFilteredSync, IRequestFilteredSync, IDisposable
{
    public void SpecifyFilter(FilterRequestCallback filterRequest)
    {
        if (Filter != null)
        {
            if (!filterRequest(Filter, FilteringType.CurrentItemsOnly))
            {
                throw new Exception("Filter not accepted at source");
            }
        }
    }

    public Boolean TryAddFilter(Object filter, FilteringType filteringType)
    {
        ISyncFilter syncFilter = filter as ISyncFilter;

        if (syncFilter == null)
        {
            return false;
        }

        return true;
    }

    // Rest of class removed for brevity

}

The next change is the GetChangeBatch method needs to deal with the filter. The ChangeBatch returned to the other provider should only contain changes related to the filter. This was the bit I was dreading in the filter process, but the Microsoft Sync Framework makes this really easy for item filtering. The GetFilteredChangeBatch method takes a delegate that determines whether items should be in the filtered change batch or not.

public override ChangeBatch GetChangeBatch(
    UInt32 batchSize, SyncKnowledge destinationKnowledge, out Object changeDataRetriever)
{
    ChangeBatch batch;

    if (Filter != null)
    {
        FilterInfo filterInfo = new ItemListFilterInfo(IdFormats);

        batch = Metadata.GetFilteredChangeBatch(batchSize, destinationKnowledge, filterInfo, ItemFilterCallback);
    }
    else
    {
        batch = Metadata.GetChangeBatch(batchSize, destinationKnowledge);
    }

    IList changes = new List(batch.Count());
    ItemDataRetriever retriever = new ItemDataRetriever(Metadata);

    foreach (ItemChange change in batch)
    {
        changes.Add(retriever.LoadFromSyncId(change.ItemId));
    }

    OnChangesFound(
        new ChangesFoundEventArgs
            {
                Changes = changes, 
                ReplicaId = ReplicaId.GetGuidId()
            });

    changeDataRetriever = retriever;

    return batch;
}

private Boolean ItemFilterCallback(ItemMetadata itemmetadata)
{
    // TODO: Cache this lookup in GetChangeBatch as we don't want to unnecessarily call this for each item checked
    ItemMetadata metadata = Metadata.FindItemMetadataByUniqueIndexedField("Id", Filter.Id);

    if (metadata == null)
    {
        return false;
    }

    return itemmetadata.GlobalId == metadata.GlobalId;
}

That's all there is to it. Not too hard after all.

My POC project is attached to this post for reference.

CachedSyncPOC.zip (21.08 kb)

Tags: