Merge two distinct lists using LINQ

Asked

Viewed 2,051 times

2

I have two classes, Cliente and Endereco, where the client has 1 or more addresses, so it is a list of objects:

class Cliente {
    public string Nome { get; set; }
    public string Cpf { get; set; }
    public List<Endereco> enderecos { get; set; }
}

class Endereco {
    public string Rua;
    public int Numero;
}

And I need to merge into just one list. I’ve tried the following:

var query = from c in clientes
            from e in c.Enderecos
            select new { c, e };

var result = query.ToList();

However the returned list to access the object still needs to inform the property:

result.c.Nome
result.e.Rua

It is possible to merge the properties of the two objects and create a new list?

  • What do you want to get? It’s not clear.. A list of all addresses, IEnumerable<Endereco>? What kind of result do you want to get?

2 answers

5

It’s in the part select new { c, e }; which determines the returned object.

For example if you want to return an object with Name and Street do:

select new { c.Nome, e.Rua };
  • Can I select all columns? For example: c.*

  • No, you have to indicate them.

0


You can merge the two objects into new objects using the TypeMerger of Kyle Finley.

For historical preservation purposes, I will reproduce the two classes here in the answer:

TypeMerger.cs

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.ComponentModel;
using System.Threading;

namespace Finley.Common
{
    /// <summary>
    /// A Utility class used to merge the properties of heterogenious objects
    /// 
    /// Original Source: Mark J. Miller
    /// URL: http://www.developmentalmadness.com/archive/2008/02/12/extend-anonymous-types-using.aspx
    /// 
    /// Extended by: Kyle Finley
    /// Items Added: TypeMergerPolicy for ignoring properties.
    /// 
    /// </summary>
    public class TypeMerger
    {
        //assembly/module builders
        private static AssemblyBuilder asmBuilder = null;
        private static ModuleBuilder modBuilder = null;
        private static TypeMergerPolicy typeMergerPolicy = null;

        //object type cache
        private static IDictionary<String, Type> anonymousTypes = new Dictionary<String, Type>();

        //used for thread-safe access to Type Dictionary
        private static Object _syncLock = new Object();

        /// <summary>
        /// Merge two different object instances into a single
        /// object which is a super-set
        /// of the properties of both objects
        /// </summary>
        public static Object MergeTypes(Object values1, Object values2)
        {
            //create a name from the names of both Types
            String name = String.Format("{0}_{1}", values1.GetType(),
                values2.GetType());
            String name2 = String.Format("{0}_{1}", values2.GetType(),
                values1.GetType());

            Object newValues = CreateInstance(name, values1, values2);
            if (newValues != null)
                return newValues;

            newValues = CreateInstance(name2, values2, values1);
            if (newValues != null)
                return newValues;

            //lock for thread safe writing
            lock (_syncLock) {
                //now that we're inside the lock - check one more time
                newValues = CreateInstance(name, values1, values2);
                if (newValues != null)
                    return newValues;

                //merge list of PropertyDescriptors for both objects
                PropertyDescriptor[] pdc = GetProperties(values1, values2);

                //make sure static properties are properly initialized
                InitializeAssembly();

                //create the type definition
                Type newType = CreateType(name, pdc);

                //add it to the cache
                anonymousTypes.Add(name, newType);

                //return an instance of the new Type
                return CreateInstance(name, values1, values2);
            }
        }

        public static Object MergeTypes(Object values1, Object values2, TypeMergerPolicy policy)
        {
            typeMergerPolicy = policy;
            return MergeTypes(values1, values2);
        }

        /// <summary>
        /// Instantiates an instance of an existing Type from cache
        /// </summary>
        private static Object CreateInstance(String name, Object values1, Object values2)
        {
            Object newValues = null;

            //merge all values together into an array
            Object[] allValues = MergeValues(values1, values2);

            //check to see if type exists
            if (anonymousTypes.ContainsKey(name)) {
                //get type
                Type type = anonymousTypes[name];

                //make sure it isn't null for some reason
                if (type != null) {
                    //create a new instance
                    newValues = Activator.CreateInstance(type, allValues);
                }
                else {
                    //remove null type entry
                    lock (_syncLock)
                        anonymousTypes.Remove(name);
                }
            }

            //return values (if any)
            return newValues;
        }

        /// <summary>
        /// Merge PropertyDescriptors for both objects
        /// </summary>
        private static PropertyDescriptor[] GetProperties(Object values1, Object values2)
        {
            //dynamic list to hold merged list of properties
            List<PropertyDescriptor> properties = new List<PropertyDescriptor>();

            //get the properties from both objects
            PropertyDescriptorCollection pdc1 = TypeDescriptor.GetProperties(values1);
            PropertyDescriptorCollection pdc2 = TypeDescriptor.GetProperties(values2);

            //add properties from values1
            for (int i = 0; i < pdc1.Count; i++) {
                if (typeMergerPolicy == null || !typeMergerPolicy.Ignored.Contains(pdc1[i].GetValue(values1)))
                    properties.Add(pdc1[i]);
            }
            //add properties from values2
            for (int i = 0; i < pdc2.Count; i++) {
                if (typeMergerPolicy == null || !typeMergerPolicy.Ignored.Contains(pdc2[i].GetValue(values2)))
                    properties.Add(pdc2[i]);
            }
            //return array
            return properties.ToArray();
        }

        /// <summary>
        /// Get the type of each property
        /// </summary>
        private static Type[] GetTypes(PropertyDescriptor[] pdc)
        {
            List<Type> types = new List<Type>();

            for (int i = 0; i < pdc.Length; i++)
                types.Add(pdc[i].PropertyType);

            return types.ToArray();
        }

        /// <summary>
        /// Merge the values of the two types into an object array
        /// </summary>
        private static Object[] MergeValues(Object values1, Object values2)
        {
            PropertyDescriptorCollection pdc1 = TypeDescriptor.GetProperties(values1);
            PropertyDescriptorCollection pdc2 = TypeDescriptor.GetProperties(values2);

            List<Object> values = new List<Object>();
            for (int i = 0; i < pdc1.Count; i++) {
                if (typeMergerPolicy == null || !typeMergerPolicy.Ignored.Contains(pdc1[i].GetValue(values1)))
                    values.Add(pdc1[i].GetValue(values1));
            }

            for (int i = 0; i < pdc2.Count; i++) {
                if (typeMergerPolicy == null || !typeMergerPolicy.Ignored.Contains(pdc2[i].GetValue(values2)))
                    values.Add(pdc2[i].GetValue(values2));
            }
            return values.ToArray();
        }

        /// <summary>
        /// Initialize static objects
        /// </summary>
        private static void InitializeAssembly()
        {
            //check to see if we've already instantiated
            //the static objects
            if (asmBuilder == null) {
                //create a new dynamic assembly
                AssemblyName assembly = new AssemblyName();
                assembly.Name = "AnonymousTypeExentions";

                //get the current application domain
                AppDomain domain = Thread.GetDomain();

                //get a module builder object
                asmBuilder = domain.DefineDynamicAssembly(assembly,
                    AssemblyBuilderAccess.Run);
                modBuilder = asmBuilder.DefineDynamicModule(
                    asmBuilder.GetName().Name, false
                    );
            }
        }

        /// <summary>
        /// Create a new Type definition from the list
        /// of PropertyDescriptors
        /// </summary>
        private static Type CreateType(String name, PropertyDescriptor[] pdc)
        {
            //create TypeBuilder
            TypeBuilder typeBuilder = CreateTypeBuilder(name);

            //get list of types for ctor definition
            Type[] types = GetTypes(pdc);

            //create priate fields for use w/in the ctor body and properties
            FieldBuilder[] fields = BuildFields(typeBuilder, pdc);

            //define/emit the Ctor
            BuildCtor(typeBuilder, fields, types);

            //define/emit the properties
            BuildProperties(typeBuilder, fields);

            //return Type definition
            return typeBuilder.CreateType();
        }

        /// <summary>
        /// Create a type builder with the specified name
        /// </summary>
        private static TypeBuilder CreateTypeBuilder(string typeName)
        {
            TypeBuilder typeBuilder = modBuilder.DefineType(typeName,
                        TypeAttributes.Public,
                        typeof(object));
            //return new type builder
            return typeBuilder;
        }

        /// <summary>
        /// Define/emit the ctor and ctor body
        /// </summary>
        private static void BuildCtor(TypeBuilder typeBuilder, FieldBuilder[] fields, Type[] types)
        {
            //define ctor()
            ConstructorBuilder ctor = typeBuilder.DefineConstructor(
                MethodAttributes.Public,
                CallingConventions.Standard,
                types
                );

            //build ctor()
            ILGenerator ctorGen = ctor.GetILGenerator();

            //create ctor that will assign to private fields
            for (int i = 0; i < fields.Length; i++) {
                //load argument (parameter)
                ctorGen.Emit(OpCodes.Ldarg_0);
                ctorGen.Emit(OpCodes.Ldarg, (i + 1));

                //store argument in field
                ctorGen.Emit(OpCodes.Stfld, fields[i]);
            }

            //return from ctor()
            ctorGen.Emit(OpCodes.Ret);
        }

        /// <summary>
        /// Define fields based on the list of PropertyDescriptors
        /// </summary>
        private static FieldBuilder[] BuildFields(TypeBuilder typeBuilder, PropertyDescriptor[] pdc)
        {
            List<FieldBuilder> fields = new List<FieldBuilder>();

            //build/define fields
            for (int i = 0; i < pdc.Length; i++) {
                PropertyDescriptor pd = pdc[i];

                //define field as '_[Name]' with the object's Type
                FieldBuilder field = typeBuilder.DefineField(
                    String.Format("_{0}", pd.Name),
                    pd.PropertyType,
                    FieldAttributes.Private
                    );

                //add to list of FieldBuilder objects
                fields.Add(field);
            }

            return fields.ToArray();
        }

        /// <summary>
        /// Build a list of Properties to match the list of private fields
        /// </summary>
        private static void BuildProperties(TypeBuilder typeBuilder, FieldBuilder[] fields)
        {
            //build properties
            for (int i = 0; i < fields.Length; i++) {
                //remove '_' from name for public property name
                String propertyName = fields[i].Name.Substring(1);

                //define the property
                PropertyBuilder property = typeBuilder.DefineProperty(propertyName,
                    PropertyAttributes.None, fields[i].FieldType, null);

                ////define 'Get' method only (anonymous types are read-only)
                //MethodBuilder getMethod = typeBuilder.DefineMethod(
                //    String.Format("Get_{0}", propertyName)
                //    MethodAttributes.Public  MethodAttributes.SpecialName
                //         MethodAttributes.HideBySig,
                //    fields[i].FieldType,
                //    Type.EmptyTypes
                //    );

                //define 'Get' method only (anonymous types are read-only)
                MethodBuilder getMethod = typeBuilder.DefineMethod(
                    String.Format("Get_{0}", propertyName),
                    MethodAttributes.Public,
                    fields[i].FieldType,
                    Type.EmptyTypes
                    );

                //build 'Get' method
                ILGenerator methGen = getMethod.GetILGenerator();

                //method body
                methGen.Emit(OpCodes.Ldarg_0);
                //load value of corresponding field
                methGen.Emit(OpCodes.Ldfld, fields[i]);
                //return from 'Get' method
                methGen.Emit(OpCodes.Ret);

                //assign method to property 'Get'
                property.SetGetMethod(getMethod);
            }
        }

        public static TypeMergerPolicy Ignore(object value)
        {
            return new TypeMergerPolicy(value);
        }
    }
}

TypeMergerPolicy.cs

using System;
using System.Collections;
using System.Collections.Generic;

namespace Finley.Common
{
    public class TypeMergerPolicy
    {
        private IList ignored;

        public IList Ignored
        {
            get { return this.ignored; }
        }

        public TypeMergerPolicy(IList ignored)
        {
            this.ignored = ignored;
        }

        public TypeMergerPolicy(object ignoreValue)
        {
            ignored = new List<object>();
            ignored.Add(ignoreValue);
        }

        public TypeMergerPolicy Ignore(object value)
        {
            this.ignored.Add(value);
            return this;
        }

        public Object MergeTypes(Object values1, Object values2)
        {
            return TypeMerger.MergeTypes(values1, values2, this);
        }
    }
}

Use:

var query = from c in clientes
            from e in c.Enderecos
            select TypeMerger.MergeTypes(c, e);

var result = query.ToList();
  • 1

    Showw! Merged values. In this class I would only add the column names.. Thank you!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.