Thursday, 29 April 2010

www.ddkonline.com featured in SAP official documentation!

I was referred to the following article by the SAP Project tech lead in my current project. Funny part is that this blog is actually one of the references detailed at the end of the article! It was interesting to see the look on his face!

Ahhhh, it's nice to be appreciated sometimes - even though I'm still feel like a bit of a Microsoft foreigner in the SAP world -

http://www.sdn.sap.com/irj/scn/index?rid=/library/uuid/40dbb54f-5c28-2d10-ad94-f812a548dc07




DDK

Monday, 26 April 2010

.NET Deep Copy/Deep Clone between 2 different object types in C# – Some of the Different/Alternative Approaches

I’ve had to use this “Translation” pattern (and similar code) several times over the last few years – so I’ve decided to commit it to a blog entry for future reference.

If you have 2 objects (of different types e.g. say 2 different generated WCF client proxies pointing to SAP), with similar generated properties (e.g. both types have the same “Employee” structure with UserId, UserName, Display Name, then you could spend a lot of time trying to write code manually. You could end up writing mapping code like this:

employeeDto.EmployeeId = currentEmployee.EmployeeId
employeeDto.UserId = currentEmployee.UserId
employeeDto.Department = currentEmployee.Department
etc.

If you have a simple, flat object that has just value types (e.g. int, strings) with no complex objects (e.g. arrays, generic lists or reference objects) within, then you can simply use a “Shallow Copy” (also known as a “Memberwise Clone”) of the properties between the 2 objects.

For example:

/// 
/// Shallow Property Example
/// 
/// 
/// 
public static void CopyPropertyValues(object source, object destination)
{
    var destProperties = destination.GetType().GetProperties();
    foreach (var sourceProperty in source.GetType().GetProperties())
    {
        foreach (var destProperty in destProperties)
        {
            if (destProperty.Name == sourceProperty.Name &&
        destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
            {
                destProperty.SetValue(destination, sourceProperty.GetValue(
                    source, new object[] { }), new object[] { });

                break;
            }
        }
    }
}


If (as in most situations, your objects have child collections) e.g like a Purchase Order would have Purchase order items, then the Shallow copy approach will not work. If you have reference objects, it will actually just copy the references themselves – so the original object and the cloned object effectively have the same references and become the same object. As the references are the same, changes to the "cloned" object will be reflected in the original object - which is typically NOT what you want!

To solve this problem, you need to perform a Deep copy of your objects that will in fact traverse your object graph (ie full structure/hierarchy of your objects) to copy all properties and “subproperties” between the 2 different objects (of different types).

Deep Copy – for Same Object Type
There are approaches involving serialization and the IClonable interface (see http://en.csharp-online.net/ICloneable) for details – but they only work within the bounds of a single object type (ie they can’t map/translate the properties between 2 different types) – plus it only works with Serializable types:

(Sample taken from here http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx)

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;     
      
/// 
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// 
public static class ObjectCopier
{
 /// 
 /// Perform a deep Copy of the object.
 /// 
 /// The type of object being copied.
 /// 
The object instance to copy./// The copied object.
 public static T Clone(T source)
 {
  if (!typeof(T).IsSerializable)
  {
    throw new ArgumentException("The type must be serializable.", "source");
  }
  
  // Don't serialize a null object, simply return the default for that object
  if (Object.ReferenceEquals(source, null))
  {
    return default(T);
  }

  IFormatter formatter = new BinaryFormatter();
  Stream stream = new MemoryStream();
  using (stream)
  {
    formatter.Serialize(stream, source);
    stream.Seek(0, SeekOrigin.Begin);
    return (T)formatter.Deserialize(stream);
  }
 }
}

Another sample is here (also only works for deep copies between the same object types)
http://ox.no/posts/extension-methods-for-copying-or-cloning-objects

So for a deep copy approach for (which is what we need for most projects), you can do the following:
• Use Reflection to navigate through the object graph and copy each property one by one (rather than just copying the pointers/references as in the Shallow copy approach) This is a tried and trusted approach, but the performance (as it is using reflection) is not as good as the Expression approach.
• Use Reflection and Emit of IL Code/Expressions (only with .NET 3.5) to perform the copy - this performs better when there are a large number of runs as it runs compiled after the first run (after the initial reflection generates the code) - example can be found here - http://whizzodev.blogspot.com/2008/06/object-deep-cloning-using-il-in-c_20.html
• Use a 3rd Party utility library (which typically use the approaches above) – such as Automapper (http://automapper.codeplex.com/)

I’ve provided samples/details of the Reflection approach below:

Deep Copy Using Reflection, Supporting Different Object Types
(Sample from http://stackoverflow.com/questions/569154/how-to-deep-copy-between-objects-of-different-types-in-c-net)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace DDKOnline.Common
{
    /// 
    /// Utility 
    /// 
    public static class DeepCopyUtility
    {

        ///  
        /// Copies the data of one object to another. The target object gets properties of the first.  
        /// Any matching properties (by name) are written to the target. 
        ///  
        /// 
The source object to copy from/// 
The target object to copy topublic static void CopyObjectData(object source, object target)
        {
            CopyObjectData(source, target, String.Empty, BindingFlags.Public | BindingFlags.Instance);
        }

        ///  
        /// Copies the data of one object to another. The target object gets properties of the first.  
        /// Any matching properties (by name) are written to the target. 
        ///  
        /// 
The source object to copy from/// 
The target object to copy to/// 
A comma delimited list of properties that should not be copied/// 
Reflection binding accesspublic static void CopyObjectData(object source, object target, string excludedProperties, BindingFlags memberAccess)
        {
            string[] excluded = null;
            if (!string.IsNullOrEmpty(excludedProperties))
            {
                excluded = excludedProperties.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            }

            MemberInfo[] miT = target.GetType().GetMembers(memberAccess);
            foreach (MemberInfo Field in miT)
            {
                string name = Field.Name;

                // Skip over excluded properties 
                if (string.IsNullOrEmpty(excludedProperties) == false
                    && excluded.Contains(name))
                {
                    continue;
                }


                if (Field.MemberType == MemberTypes.Field)
                {
                    FieldInfo sourcefield = source.GetType().GetField(name);
                    if (sourcefield == null) { continue; }

                    object SourceValue = sourcefield.GetValue(source);
                    ((FieldInfo)Field).SetValue(target, SourceValue);
                }
                else if (Field.MemberType == MemberTypes.Property)
                {
                    PropertyInfo piTarget = Field as PropertyInfo;
                    PropertyInfo sourceField = source.GetType().GetProperty(name, memberAccess);
                    if (sourceField == null) { continue; }

                    if (piTarget.CanWrite && sourceField.CanRead)
                    {
                        object targetValue = piTarget.GetValue(target, null);
                        object sourceValue = sourceField.GetValue(source, null);

                        if (sourceValue == null) { continue; }

                        if (sourceField.PropertyType.IsArray
                            && piTarget.PropertyType.IsArray
                            && sourceValue != null)
                        {
                            CopyArray(source, target, memberAccess, piTarget, sourceField, sourceValue);
                        }
                        else
                        {
                            CopySingleData(source, target, memberAccess, piTarget, sourceField, targetValue, sourceValue);
                        }
                    }
                }
            }
        }

        private static void CopySingleData(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object targetValue, object sourceValue)
        {
            //instantiate target if needed 
            if (targetValue == null
                && piTarget.PropertyType.IsValueType == false
                && piTarget.PropertyType != typeof(string))
            {
                if (piTarget.PropertyType.IsArray)
                {
                    targetValue = Activator.CreateInstance(piTarget.PropertyType.GetElementType());
                }
                else
                {
                    targetValue = Activator.CreateInstance(piTarget.PropertyType);
                }
            }

            if (piTarget.PropertyType.IsValueType == false
                && piTarget.PropertyType != typeof(string))
            {
                CopyObjectData(sourceValue, targetValue, "", memberAccess);
                piTarget.SetValue(target, targetValue, null);
            }
            else
            {
                if (piTarget.PropertyType.FullName == sourceField.PropertyType.FullName)
                {
                    object tempSourceValue = sourceField.GetValue(source, null);
                    piTarget.SetValue(target, tempSourceValue, null);
                }
                else
                {
                    CopyObjectData(piTarget, target, "", memberAccess);
                }
            }
        }

        private static void CopyArray(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object sourceValue)
        {
            int sourceLength = (int)sourceValue.GetType().InvokeMember("Length", BindingFlags.GetProperty, null, sourceValue, null);
            Array targetArray = Array.CreateInstance(piTarget.PropertyType.GetElementType(), sourceLength);
            Array array = (Array)sourceField.GetValue(source, null);

            for (int i = 0; i < array.Length; i++)
            {
                object o = array.GetValue(i);
                object tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetElementType());
                CopyObjectData(o, tempTarget, "", memberAccess);
                targetArray.SetValue(tempTarget, i);
            }
            piTarget.SetValue(target, targetArray, null);
        } 

    }
}

Tuesday, 20 April 2010

Finding Duplicates in a list using LINQ Lambda Expressions

Here is a one-liner that allows you to get a list of the duplicates in a list. I originally tried to approach the problem using the LINQ Group By syntax (like what I did here in 2008), but I found this to be a much simpler solution.

This works by utilizing the .Except extension method that compares with a blank list. Because ALL items don't match what is in the blank list, then all elements are passed to the subsequent .Any statement which picks up any item in which all fields specified in the lambda expression match.

Here's the code for listing duplicates in LINQ, comparing against multiple columns:

var duplicates =
timesheetDto.TimesheetItems.Where(timesheetItem =>
timesheetDto.TimesheetItems
.Except(new List { timesheetItem })
.Any(matchingTimesheetItem => 
timesheetItem.ActivityType  == matchingTimesheetItem.ActivityType &&
timesheetItem.ReceivingWBSElement == matchingTimesheetItem.ReceivingWBSElement &&
timesheetItem.ReceivingCostCentre == matchingTimesheetItem.ReceivingCostCentre &&
timesheetItem.SendingCostCentre == matchingTimesheetItem.SendingCostCentre
)
).ToList(); 

return duplicates.Count > 0;

Tuesday, 13 April 2010

Zach Klein's First Words (drum roll...)

So that my son Zach doesn't feel left out, I've compiled a list of Zachy's first words (at 18 months old) with a brief explanation for each for future reference (and mockery at his 21st!):
  1. Baba/Daddy (Mandarin for daddy. Again I was successful in conquering the first word department, same as with my daugter Heidi!)
  2. Maaa/Mama (Mandarin for mummy)
  3. She She (Mandarin for Thankyou - he is a very polite young man (sometimes), and says this withought fail if you give him his milk)
  4. No, No, No (while vigorously shaking his head)
  5. Fower (Zach's version of "flower")
  6. My, My, My, My (Yes - Just like the mindless flock of seagulls in the movie "Finding Nemo". Typically said 4 times)
  7. Mesh (Mess. He says this a lot)
  8. Thee Thaw (see-saw - whenever he sits on your stomach, he moves backwards and forwards to indicate he wants you to move like a see-saw)
  9. Allo - (when he manages to get hold of a phone and randomly change our Phone address books, or if he is asking for a phone, he'll point at the phone and say this). Typically followed by "Bye" as soon as he closes the phone.
  10. Oh Baobei (Mandarin for "precious" - Lisa and I says this too him a lot, but I believe he's talking about himself when he is crying and keeps saying "Oh Baobei!, Oh Baobei!". I suppose he has a good ego to think that well of himself! 
  11. Nai Nai ("Good night", typically followed by shaking of head, crying and No Nai Nai!)
  12. Dish (Indicating he wants some)
  13. Uh Oh (He says this too many times for my liking)
  14. Dadyoo (Thankyou)
  15. Taa (Thanks)
  16. Deal (Technically he doesn't say it but he understands it and does the crossed hands motion same as the TV show "Deal or No Deal" when you say this word!)
  17. Upf (Up - when he wants you to cuddle him)
  18. Off (Off - when he wants to get out of bed)
  19. Out
  20. Tree (he passes trees every morning on the way to kindergarten)
  21. Gaga (same as Heidi, for small birds only)
  22. Aydee (for his sister Heidi)
  23. jiějiě (Mandarin for older sister, when talking about Heidi)
  24. Din Din (Mandarin for light - for when he wants it turned on or off. He also loves to play with lights and power switches and uses this word when he's up to mischief)
  25. Meelk, Meelk, Oh Oh Oh (Milk)
  26. Dee Dee (When he wants his milk from the Microwave)
  27. Bubool (Bubble)
  28. Nana (Banana)
  29. Apple
  30. Book
  31. Knock Knock (Accompanied by a knocking action on his own chest or the nearest table).
  32. Ganbei (Cheers - accompanied with )
  33. All Gong (when finished his dinner)
  34. More (and No More)
  35. Go
  36. Shoo
  37. Baff (Bath)
  38. Choo Choo
  39. Wee Wee
  40. Poh (Poo)
  41. Weeeee (When going down slides or sliding down our legs)
  42. Yum
  43. Yuk
  44. Erduo (Ear - when he want Lisa to clean his ears out - he finds this very soothing)
DDK

Saturday, 10 April 2010

Fix for Error with Windows Live Messenger - "Signing in to Windows Live Messenger failed because the service is temporarily unavailable. Please try again later. Error code: 8e5e0408"

If you get the following error and you've ruled out all connectivity issues (all tests pass in the built-in connectivity checker and all ports are open as neccessary on your firewall):

"Signing in to Windows Live Messenger failed because the service is temporarily unavailable. Please try again later.
Error code: 8e5e0408"


You may be suffering from a Corrupt Windows Live Contacts file. To fix it, just delete this folder and restart Messenger again as follows:

Based on the operating system version, browse to the following location

· Windows XP:
Go to Start, click on My Computer
Browse to the location,C:\Documents and Settings\\Local Settings\Application Data\Microsoft


· Windows Vista and Windows 7:
Go to Start, click on Computer
Browse to the location, C:\Users\\AppData\Local\Microsoft
Note: Windows Logon name is the logon name that you use to sign in to Windows Operating system.
b. Locate and delete the Windows Live Contacts folder.
c. Re-launch Windows Live Messenger and try to sign in again, and verify if the issue remains.

If you're still having problems:
  1. If you get error 800702e4, Disable UAC (in Windows 7/Vista). If you can now log in successfully - you have permissions problems accessing the C:\Users\UserName directory. You should add the current user to have full control to their own user's directory to ensure they can write to the neccessary log and data files without requiring any kind of elevated UAC permissions.
  2. Try a completely clean install of Windows Live Messenger as per http://windowslivehelp.com/solution.aspx?solutionid=935bb770-b07c-4393-9de9-6bc1192dc8cc

DDK