Comparing without attributes

Apr 8, 2014 at 2:25 PM
Hi!

I found your app very useful, thank you!

However, could you go a bit into detail on how I could compare items from a list by defining the attributes in a config file? Provide a simple example maybe?

Thank you!
Coordinator
Apr 8, 2014 at 11:36 PM
Hi,

ok. While creating an example for you I checked that there was still a bug in when using external config. I created a new release and can provide you the following example (is also included in source code under Documentation):
            /* Get the default context for ObjectComparer */
            Context ctx = Context.Default;

            /* Load the object comparer configuation that
             * defines how member infos (properties and fields)
             * are retrieved (in this case via the description in
             * the configuration file: ReflectDescription instead of
             * using default ReflectCompareAttribute)
             * */
            var xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CompareConfig.xml");
            using (var fs = new FileStream(xmlPath, FileMode.Open))
                ctx.Configuration = Configuration.FromXml(fs);

            /* Create sample data */
            var myOrder = SampleData.CreateSampleOrder1();

            /* Create snapshot ss1; Use ctx */
            var ss1 = new ObjectSnapshot(myOrder, ctx);

            /* Modify the sample data */
            SampleData.ModifyOrder(myOrder);

            /* Create snapshot ss2; Use ctx */
            var ss2 = new ObjectSnapshot(myOrder, ctx);

            /* Create compare item */
            var ci = new ObjectCompareItem();
            ci.Create(ss1, ss2);
            
            /* Get change set from compare item */
            var changes = new ChangeSet();
            changes.Create(ci);

            /* Print changes in xml format to console */
            Console.WriteLine(changes.ToXml());
I hope that helps. Please give me Feedback.

Thanks!
Marked as answer by testy on 4/8/2014 at 4:37 PM
Apr 9, 2014 at 7:26 AM
Thank you! Works like a charm! :D

Does it also support IDictionary?
Coordinator
Apr 9, 2014 at 8:56 AM
Good question!
I tried it and thats the solution:

Code:
           Context ctx = Context.Default;
            var xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.xml");
            using (var fs = new FileStream(xmlPath, FileMode.Open))
                ctx.Configuration = Configuration.FromXml(fs);


            IDictionary<int, string> dict = new Dictionary<int, string>();
            dict.Add( 1, "a" );
            dict.Add( 2, "b" );
            dict.Add( 3, "c" );

            var ss1 = new EnumerableSnapshot( dict, ctx );

            dict[ 3 ] = "c-ChangedValue";
            dict.Remove( 2 );
            dict.Add( 4, "d" );

            /* Create snapshot ss2; Use ctx */
            var ss2 = new EnumerableSnapshot( dict, ctx );

            /* Create compare item */
            var ci = new EnumerableCompareItem();
            ci.Create(ss1, ss2);
            
            /* Get change set from compare item */
            var changes = new ChangeSet();
            changes.Create(ci);

            /* Print changes in xml format to console */
            Console.WriteLine(changes.ToXml());
Configuration file:
<?xml version="1.0"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <GetMemberBindingFlags>Instance Static Public NonPublic</GetMemberBindingFlags>
  <MetadataRetrievalOptions>ReflectDescriptions</MetadataRetrievalOptions>
  <ClassDescriptions>
    <ClassDescription FullName="System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]">
      <DisplayName>MyDictionary</DisplayName>
      <IdPropertyName></IdPropertyName>
      <Members>
        <Member FullName="Count">
          <DisplayName>NumberOfElements</DisplayName>
        </Member>
      </Members>
    </ClassDescription>
    <ClassDescription FullName="System.Collections.Generic.KeyValuePair`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]">
      <DisplayName>MyDictEntry</DisplayName>
      <IdPropertyName>Key</IdPropertyName>
      <Members>
        <Member FullName="Key">
          <DisplayName>Identity</DisplayName>
        </Member>
        <Member FullName="Value">
          <DisplayName>MyValue</DisplayName>
        </Member>
      </Members>
    </ClassDescription>
  </ClassDescriptions>
</Configuration>
Marked as answer by testy on 4/9/2014 at 1:56 AM
Coordinator
Apr 9, 2014 at 11:57 AM
I just tried it with "IDictionary" only and there are some issues. But I'll take that as new Issue, because I think the Problems occur also when using ICollection.
Meanwhile, refer to the previous posted solution.

Thanks!
Apr 9, 2014 at 2:01 PM
Thanks once more.

I tested the solution you provided. But when I tried using a complex type I defined as value, I keep getting a TypeLoadException:
"Could not load type with name 'System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[Person]]"
, where Person is my custom type.

In the config file I've declared the dictionary like this:
<ClassDescription FullName="System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[Person]]">
      <DisplayName>MyDictionary</DisplayName>
      <IdPropertyName></IdPropertyName>
      <Members>
        <Member FullName="Count">
          <DisplayName>NumberOfElements</DisplayName>
        </Member>
      </Members>
    </ClassDescription>
    
    <ClassDescription FullName="System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[Person]]">
      <DisplayName>MyDictEntry</DisplayName>
      <IdPropertyName>Key</IdPropertyName>
      <Members>
        <Member FullName="Key">
          <DisplayName>Identity</DisplayName>
        </Member>
        <Member FullName="Value">
          <DisplayName>MyValue</DisplayName>
        </Member>
      </Members>
    </ClassDescription>
I've also tried using the full type name in the config file for the Person type, but with the same result.

Am I missing something?
Coordinator
Apr 9, 2014 at 10:18 PM
Edited Apr 9, 2014 at 10:22 PM
Ok. In that case you always have to give the full type names.

For the Dictionary:
System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[YouNameSpace.Person, YourAssemblyFullName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
For the KeyValuePair:
System.Collections.Generic.KeyValuePair`2[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[YouNameSpace.Person, YourAssemblyFullName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
The Problem is, that if another Version is used either of the .Net CLR or "YourAssembly" the configuration has to be adapted. That is really not nice! Thus this is an architectural issue I cannot solve this in a short time. I've to guess a bit more.

To track the progress I've created that issue:
https://objectcomparer.codeplex.com/workitem/1695
Apr 10, 2014 at 12:21 PM
Tried with the type full name, assembly name, etc. and it worked.
Thanks!