Object to Object Mapper, Part 2

My last post described an odd issue that I ran into with the app I was working on. Basically my first pass at the solution was a object to object mapper/factory pattern/whatever. It's not the most elegant but like I said it was my first pass. But after that I figured why not create a more robust object to object mapper that included that sort of functionality and since I like to reinvent the wheel, I thought what the hell. It turns out for basic functionality, it's quite simple. Anyway, I started with centralizing everything (similar to HaterAide) with a central manager:

    public class Manager:Singleton<Manager>
    {
        protected Manager()
            : base()
        {
            Mappings = new Dictionary<Type, IMapping>();
        }

        public void Setup(List<Assembly> Assemblies)
        {
            foreach (Assembly Assembly in Assemblies)
            {
                Setup(Assembly);
            }
        }

        public void Setup(Assembly Assembly)
        {
            List<Type> Types = Utilities.Reflection.GetTypes(Assembly, "IMapping");
            foreach (Type Type in Types)
            {
                IMapping Mapping=(IMapping)Assembly.CreateInstance(Type.FullName);
                Mappings.Add(Mapping.SourceType, Mapping);
            }
        }

        public void Sync(object Source, object Destination)
        {
            Mappings[Source.GetType()].Sync(Source, Destination);
        }

        private Dictionary<Type, IMapping> Mappings { get; set; }
    }

The singleton class is a helper from my utility library and while I know that singletons can cause issues, in this case it makes sense (mostly anyway as I want all of the various mappings in one location). The manager class has a couple setup functions (single assembly or a list of them) and simply creates an instance of anything that has the IMapping interface, which looks like this:

    public interface IMapping
    {
        Type SourceType { get; }
        Type DestinationType { get; }
        void Sync(object Source,object Destination);
    }

The IMapping interface is rather basic and only exposes a couple of items (the type of the source item, the type of the destination item, and a sync function which only goes from source to destination). Going back to the manager, the only other function of note is Sync which simply calls the IMapping object's Sync function. Now for the more interesting item, the actual mapping class:

    public class Mapping<Source, Destination>:IMapping
    {
        public Mapping()
        {
            Setup();
        }

        public void Sync(object SourceObject, object DestinationObject)
        {
            foreach(string Key in Mappings.Keys)
            {
                string[]Splitter={"."};
                string[]SourceProperties=Key.Split(Splitter,StringSplitOptions.None);
                object TempSourceProperty = SourceObject;
                Type PropertyType=SourceType;
                for (int x = 0; x < SourceProperties.Length; ++x)
                {
                    TempSourceProperty = PropertyType.GetProperty(SourceProperties[x]).GetValue(TempSourceProperty, null);
                    PropertyType = TempSourceProperty.GetType();
                }
                string[] DestinationProperties = Mappings[Key].Split(Splitter, StringSplitOptions.None);
                object TempDestinationProperty = DestinationObject;
                Type DestinationPropertyType = DestinationType;
                for (int x = 0; x < DestinationProperties.Length-1; ++x)
                {
                    TempDestinationProperty = DestinationPropertyType.GetProperty(DestinationProperties[x]).GetValue(TempDestinationProperty, null);
                    PropertyType = TempSourceProperty.GetType();
                }
                DestinationPropertyType.GetProperty(DestinationProperties[DestinationProperties.Length-1]).SetValue(TempDestinationProperty,TempSourceProperty,null);
            }
        }

        protected void Map(Expression<Func<Source, object>> SourceExpression,
            Expression<Func<Destination, object>> DestinationExpression)
        {
            Setup();
            string SourceName = GetName<Source>(SourceExpression);
            string DestinationName = GetName<Destination>(DestinationExpression);
            if (Mappings.ContainsValue(DestinationName))
            {
                foreach (string Key in Mappings.Keys)
                {
                    if (Mappings[Key] == DestinationName)
                    {
                        Mappings.Remove(Key);
                        break;
                    }
                }
            }
            if (Mappings.ContainsKey(SourceName))
            {
                Mappings[SourceName] = DestinationName;
            }
            else
            {
                Mappings.Add(SourceName, DestinationName);
            }
        }

        protected void Ignore(Expression<Func<Source, object>> SourceExpression)
        {
            Setup();
            string SourceName = GetName<Source>(SourceExpression);
            if (Mappings.ContainsKey(SourceName))
            {
                Mappings.Remove(SourceName);
            }
        }

        private void Setup()
        {
            if (Mappings == null)
            {
                Mappings = new Dictionary<string, string>();
                PropertyInfo[] Properties = typeof(Source).GetProperties();
                Type DestinationType = typeof(Destination);
                for (int x = 0; x < Properties.Length; ++x)
                {
                    if (DestinationType.GetProperty(Properties[x].Name) != null)
                    {
                        Mappings.Add(Properties[x].Name, Properties[x].Name);
                    }
                }
            }
        }

        private string GetName<T>(Expression<Func<T, object>> expression)
        {
            string Name = "";
            if (expression.Body.NodeType == ExpressionType.Convert)
            {
                Name = expression.Body.ToString().Replace("Convert(", "").Replace(")", "");
                Name = Name.Remove(0, Name.IndexOf(".") + 1);
            }
            else
            {
                Name = expression.Body.ToString();
                Name = Name.Remove(0, Name.IndexOf(".") + 1);
            }
            return Name;
        }

        public Type SourceType { get { return typeof(Source); } }
        public Type DestinationType { get { return typeof(Destination); } }

        protected Dictionary<string, string> Mappings { get; set; }
    }

There is quite a bit here but it's actually quite simple. When initialized, it calls the Setup function. This function sets up the default mappings. For example if you have two items that both have the Text field, it will set those up automatically. However in the constructor of the class that inherits from this class we can define the mapping to a greater degree. The first function to help with this is the Map function. This takes two Linq expressions. The advantage of doing this is that we can actually specify properties of properties. So for instance if we want the Length property of a string to be copied to an int property on the destination side, all we need to do is send in x=>x.Text.Length. The Map function will save the Text.Length portion of that so we can get to that value later on. The second function in there is Ignore. There might be times when you don't want those two Text fields to match up, so simply call ignore and it removes it from its list of mappings.

Past that there is only one more function of importance, Sync. Sync copies values from the source to the destination object. Since it already knows their types and what maps to what, it just goes down the list of mappings and copies them over. The two loops ensure that we get down to the individual properties if we're looking at nested properties. But that's all there is to it. Although it is a bit too basic at this step as it doesn't take into account type differences, string formatting, etc. But I'll talk about that next time. Anyway, take a look, leave feedback, and happy coding.

Oh and on an unrelated note, I find it sad that everyone I've talked to today has brought up the iPad in a positive light. First off I hate talking "technology" with people. Which is in quotes because they only bring up that stuff because they don't have much else in common with me as video games, cartoons, sci-fi stuff, bad B movies, XKCD, etc., which according to an article I read about why women don't go into tech related fields are "masculine" things (which I've never heard in conjunction with any of those items before reading that), are not common things that are enjoyed in my place of work. Not to mention their idea of tech talk is "it's cute/sexy"... Secondly I hate it being brought up because I think it's a dud of a product. No multitasking and lack of flash/silverlight killed it for me. It means I can't stream Hulu or Netflix or AdultSwim or anywhere really, there was minimal talk about publisher partners (so iBook is a wait and see thing), I can't play games on Newgrounds or Kongregate (which have some games that are more addictive and better than anything I've seen on even the DS or PSP, let alone the iPhone). Plus the size is too big to really carry around easily. Plus you have to pay the monthly fee for the 3G (although no contract) and it's only AT&T which has terrible coverage in my area. Oh and since there is no multitasking so I can't even listen to Pandora while reading a book on it. Not to mention bringing the dock VGA connector with you just to do a presentation with it seems annoying to me (I always forget cords, etc.). So basically what I'm saying is I'm completely confused as to what niche it's suppose to fill in my life as it doesn't seem to do anything that I would want it to do.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 1/29/2010 at 5:07 PM
Tags: , , ,
Categories: C#
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

Object to Object Mapper, Part 1

Ok, so the title is really a lie but I'll get into that later. Anyway, I mentioned a while back in the LMS project that due to an issue that popped up I had a side project that had reared its head. In the LMS I have multiple DLLs floating around. Each one of them is self contained. For example if I drop a Netflix service into the directory, it should pick it up and go. That means finding the service, loading it up, etc. Well in order to do this, it means that these services may need to use the database to save information (specifically config info). This is pretty simple as all it took was a slight adjustment in my ORM to accept multiple assemblies. The issue came about in letting the user modify the information. In the Netflix example, one of the things that you need is an app ID. The user, in this example, would need to enter his own ID. So we have a Netflix object with an AppID field that gets saved to the database. However the interface has no clue about this object (no idea what it is, what the fields are, etc.).

Now in a normal system this would be easy as we just have the Netflix service creator come up with a page/form to modify the info. In the LMS, this would be an issue as it's suppose to be designed such that the interface doesn't matter. It may be a web page or a form (or verbal or whatever). So we end up with an interesting issue where we need to copy data to and from the interface and object without knowing what we are copying. Well my first instinct was to look online to see if anyone had something similar when I came across AutoMapper. I like AutoMapper as it does an efficient job of copying data from one object to another. However in my case I don't know what the source is when I know what the destination should be and I when I know what the destination should be I don't know what the source is... On top of that the interface may actually be multiple objects of different types. Maybe I want to display strings inside a textbox and booleans as a checkbox. In AutoMapper it seems like the idea is to copy one object (including child objects) to a distinct destination object. In my case I want to copy a single object and potentially cut it up into dozens of smaller objects without knowing in advance what those objects are or really how I'm going to cut it up (or join it back together again for that matter). So unless there is a feature that I'm missing, AutoMapper was out. So I decided to do what I normally do, start from scratch.

Below I'm showing you the first attempt at the issue:

    internal class DestinationInfo
    {
        public string Name { get; set; }
        public Type Type { get; set; }
    }

    public class Mapping<Source>
    {
        private Type SourceType { get; set; }
        private Dictionary<string, DestinationInfo> MappingDictionary = new Dictionary<string, DestinationInfo>();

        public Mapping()
        {
            SourceType = typeof(Source);
        }

        public void Map<Destination>(Expression<Func<Source, object>> SourceExpression,
            Expression<Func<Destination, object>> DestinationExpression)
        {
            string SourceName = GetName<Source>(SourceExpression);
            string DestinationName = GetName<Destination>(DestinationExpression);
            Type DestinationType = typeof(Destination);
            DestinationInfo DestinationInfo = new DestinationInfo();
            DestinationInfo.Name = DestinationName;
            DestinationInfo.Type = DestinationType;
            MappingDictionary.Add(SourceName, DestinationInfo);
        }

        public void Map(Expression<Func<Source, object>> SourceExpression,TypeMapping Mapping)
        {
            string SourceName = GetName<Source>(SourceExpression);
            Type SourceType = typeof(Source);
            Type SourcePropertyType = SourceType.GetProperty(SourceName).PropertyType;
            Type DestinationType=null;
            string DestinationName="";
            if (Mapping.Properties.ContainsKey(SourcePropertyType))
            {
                DestinationType = Mapping.Properties[SourcePropertyType].Type;
                DestinationName = Mapping.Properties[SourcePropertyType].Name;
            }
            DestinationInfo DestinationInfo = new DestinationInfo();
            DestinationInfo.Name = DestinationName;
            DestinationInfo.Type = DestinationType;
            MappingDictionary.Add(SourceName, DestinationInfo);
        }

        public object CreateObject(string SourcePropertyName,object SourceObject)
        {
            object ReturnObject = null;
            if (MappingDictionary.ContainsKey(SourcePropertyName))
            {
                Type TypeUsing=MappingDictionary[SourcePropertyName].Type;
                Assembly TempAssembly = TypeUsing.Assembly;
                ReturnObject = TempAssembly.CreateInstance(TypeUsing.FullName);
                object SourcePropertyValue=SourceType.GetProperty(SourcePropertyName).GetValue(SourceObject,null);
                Type DestinationPropertyValueType = TypeUsing.GetProperty(MappingDictionary[SourcePropertyName].Name).PropertyType;
                if (DestinationPropertyValueType == typeof(string) && SourcePropertyValue != null)
                {
                    SourcePropertyValue = SourcePropertyValue.ToString();
                }
                TypeUsing.GetProperty(MappingDictionary[SourcePropertyName].Name).SetValue(ReturnObject, SourcePropertyValue, null);
            }
            return ReturnObject;
        }

        public object CreateObject(Expression<Func<Source, object>> SourceExpression, object SourceObject)
        {
            string SourcePropertyName = GetName<Source>(SourceExpression);
            return CreateObject(SourcePropertyName, SourceObject);
        }

        public List<object> CreateObject(object SourceObject)
        {
            List<object> ReturnList = new List<object>();
            foreach (string Key in MappingDictionary.Keys)
            {
                object TempObj = CreateObject(Key, SourceObject);
                if (TempObj != null)
                {
                    ReturnList.Add(TempObj);
                }
            }
            return ReturnList;
        }

        public void Sync(string SourcePropertyName,object SourceObject, object DestinationObject)
        {
            string DestinationName=MappingDictionary[SourcePropertyName].Name;
            Type DestinationType = MappingDictionary[SourcePropertyName].Type;
            Type DestinationPropertyType = DestinationType.GetProperty(DestinationName).PropertyType;
            Type SourcePropertyType=SourceType.GetProperty(SourcePropertyName).PropertyType;
            object DestinationPropertyValue=DestinationType.GetProperty(DestinationName).GetValue(DestinationObject,null);
            if (DestinationPropertyType == typeof(string) && SourcePropertyType != typeof(string))
            {
                DestinationPropertyValue = Global.Parse((string)DestinationPropertyValue, SourcePropertyType);
            }
            SourceType.GetProperty(SourcePropertyName).SetValue(SourceObject, DestinationPropertyValue, null);
        }

        public void Sync(Expression<Func<Source, object>> SourceExpression, object SourceObject, object DestinationObject)
        {
            string SourcePropertyName = GetName<Source>(SourceExpression);
            Sync(SourcePropertyName, SourceObject, DestinationObject);
        }

        public void Sync(object SourceObject, List<object> DestinationObject)
        {
            int x=0;
            foreach (string Key in MappingDictionary.Keys)
            {
                PropertyInfo Property = SourceType.GetProperty(Key);
                Sync(Property.Name, SourceObject, DestinationObject[x]);
                ++x;
            }
        }

        private string GetName<T>(Expression<Func<T, object>> expression)
        {
            string Name = "";
            if (expression.Body.NodeType == ExpressionType.Convert)
            {
                Name = expression.Body.ToString().Replace("Convert(", "").Replace(")", "");
                string[] Splitter = { "." };
                string[] SplitName = Name.Split(Splitter, StringSplitOptions.None);
                Name = SplitName[SplitName.Length - 1];
            }
            else
            {
                Name = expression.Body.ToString();
                string[] Splitter = { "." };
                string[] SplitName = Name.Split(Splitter, StringSplitOptions.None);
                Name = SplitName[SplitName.Length - 1];
            }
            return Name;
        }
    }

I liked the idea of the mapping object from my ORM (which I took from Fluent NHibernate). It's simple and allows for the seperation of code in nice ways. In this case, this is a mapping object for the source object. The class has a couple of Map functions. Each of them take in an Expression object for the source and one takes in an Expression object for the destination while the other takes in a TypeMapping object. In the first one, the idea is that you know when you're mapping what you want it to map to (TextBox, etc.) and the properties that you want to map (Netflix's AppID to the TextBox's Text property for example). However in my case, I don't know what they are at the same time so the second version of Map comes into play. In that case we send in the TypeMapping object. The TypeMapping object can be found below:

    internal class DestinationPropertyInfo
    {
        public string Name { get; set; }
        public Type Type { get; set; }
    }

    public class TypeMapping
    {
        public TypeMapping()
        {
        }

        public void Map<Source, Destination>(string DestinationPropertyName)
        {
            DestinationPropertyInfo Temp = new DestinationPropertyInfo();
            Temp.Name = DestinationPropertyName;
            Temp.Type = typeof(Destination);
            Properties.Add(typeof(Source), Temp);
        }

        public void Map<Source, Destination>(Expression<Func<Destination, object>> DestinationExpression)
        {
            Map<Source, Destination>(GetName<Destination>(DestinationExpression));
        }

        private string GetName<T>(Expression<Func<T, object>> expression)
        {
            string Name = "";
            if (expression.Body.NodeType == ExpressionType.Convert)
            {
                Name = expression.Body.ToString().Replace("Convert(", "").Replace(")", "");
                string[] Splitter = { "." };
                string[] SplitName = Name.Split(Splitter, StringSplitOptions.None);
                Name = SplitName[SplitName.Length - 1];
            }
            else
            {
                Name = expression.Body.ToString();
                string[] Splitter = { "." };
                string[] SplitName = Name.Split(Splitter, StringSplitOptions.None);
                Name = SplitName[SplitName.Length - 1];
            }
            return Name;
        }

        internal Dictionary<Type, DestinationPropertyInfo> Properties = new Dictionary<Type, DestinationPropertyInfo>();
    }

The TypeMapping object is very similar to the Mapping object as it takes its cue from my ORM's ClassMapping object. This object takes a type and maps that to a destination type/property. So for instance you can tell it to map strings to a TextBox's Text property. It then keeps this list for use in the Mapping object. Doing it this way allows me to keep the information in two seperate places. The TypeMapping class would be defined in the interface and the Mapping class would be in the seperate Netflix service DLL. But we could tell it whatever we wanted without having to worry about what the type is.

Getting back to the Mapping class, the next set of functions are CreateObject. With the mapping functions we know that if property A is type X then it maps to type Y's property B (in otherwords we know what the hell we're doing at this point). The issue that I ran into is the fact that the interface doesn't know how many properties there are, what they are, etc. In otherwords we need the Mapping object to give us the items, which is where the CreateObject functions come into play. In two of the instances we're dealing with individual properties (given a source object and the property you want it gives you the output object). But to be honest, we really want all of the properties spit out back to us in a list (since the interface doesn't know what they are anyway), which is where the third function comes into play. All it does is uses the mapping to create a new object and populate the property that it's told to with the value of the property.

The last set of functions are the Sync functions. Basically these do the reverse of the CreateObject functions. They take the individual objects that were created and set the properties of the original object with the appropriate values. Most of it isn't that difficult but it does call a function that I haven't shown yet:

    public static class Global
    {
        public static object Parse(string Input, Type OutputType)
        {
            if (string.IsNullOrEmpty(Input))
                return null;
            Type[] MethodInputType=new Type[1];
            MethodInputType[0]=typeof(string);
            MethodInfo ParseMethod = OutputType.GetMethod("Parse", MethodInputType);
            object Item=OutputType.Assembly.CreateInstance(OutputType.FullName);
            object[] Values=new object[1];
            Values[0]=Input;
            return ParseMethod.Invoke(Item, Values);
        }
    }

All that function does is finds the Parse function of the object type and calls it. So of course this limits it to things like integers, floats, etc. basically things with a Parse function. But that's all there is to it. Two classes, one that the DLL sets up and one that the interface sets up, and we're good to go. Well almost. Like I said, this was my first pass at it but there are a couple flaws (no sub classes, lack of type conversion, doesn't even bother with lists of objects, etc.). And this isn't really an object to object mapper. Perhaps an object to object ( and another object, and another object) mapper (would that be an OTOAAOAAOM?)? Object to List of Objects Mapper (OTLOOM)? A config interface generator? An object generator? I have no freakin' clue what to call this and if someone else has an idea I'd love to hear it. Anyway, look it over, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 1/27/2010 at 1:29 PM
Tags: , , ,
Categories: C#
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

Using a ListBox Using SelectionMode Multiple with a GridView

You've probably run into this before. You have a ListBox (Or CheckBoxList or whatever) that lists a number of options and you can select multiple items. You then want it to be used to update the content of a GridView. Most likely you tried setting up the select statement as something like this: "SELECT * FROM Table WHERE Value=@Value" or even "SELECT * FROM Table WHERE Value in (@Value)". With @Value pointing to the SelectedValue parameter of the ListBox. If you tried that you'll notice that it didn't work. Well I mean it will update the GridView but it will only show that you selected one item. So how do we fix this?

One of the things that I like about the way the classes like GridView, etc. were set up is that you can inherit from them. In our case we want to create an item that uses the ListBox object:

    public class OurListBox : ListBox
    {
        public OurListBox()
            : base()
        {
        }

        public string SelectedValueList
        {
            get
            {
                bool None = true;
                string Seperator = "";
                StringBuilder Builder = new StringBuilder();
                foreach (ListItem Item in Items)
                {
                    if (Item.Selected)
                    {
                        None = false;
                        Builder.Append(Seperator).Append(Item.Value);
                        Seperator = ",";
                    }
                }
                if (None)
                {
                    Builder.Append("0");
                }
                return Builder.ToString();
            }
        }
    }

Ok, so now we have something that gives us a comma delimited string of the selected values. That's great but it doesn't help us much as sending that to @Value is just going to come back with an error or not much at all... So what we need is an SQL function that can parse that string. Luckily I found one that I could simply copy from here. So we create the function, switch our ListBox to the OurListBox class that we just created, and switch our select statement to this: "SELECT * FROM Table where Value in (SELECT Value FROM dbo.Split(@Value, ','))". And our controlparameter pointing towards the SelectedValueList property... And suddenly we have a working solution. Oh and just so you have something to look at:

     <asp:SqlDataSource ID="SqlDataSource2" runat="server"
        ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
        SelectCommand="SELECT * FROM Table WHERE Value in (SELECT Value FROM dbo.Split(@Value, ','))">
        <SelectParameters>
            <asp:ControlParameter ControlID="ListBox1" DefaultValue="0" Name="Value" PropertyName="SelectedValueList" />
        </SelectParameters>
    </asp:SqlDataSource>
    <cc1:OurListBox ID="ListBox1" runat="server" SelectionMode="Multiple"></cc1:OurListBox>

That's it. So hopefully this will help you out. Try it out, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 1/22/2010 at 1:10 PM
Tags: , ,
Categories: ASP.Net | C#
Post Information: Permalink | Comments (5) | Post RSSRSS comment feed