1

I'm trying to create a class instance from my Linq query result. Since I have to do this for many classes, I'm trying to find the most suitable shortcut. I'm wondering whether I can make the "select" part of the query any shorter.

My class:

public class current_control_id_class
{
    public string asset_class { get; set; }
    public string region_code { get; set; }
    public string instance_code { get; set; }
    public int sdi_control_id { get; set; }
    public int rows_loaded { get; set; }  
}

My assignment function:

foreach (var results in query)
{
    foreach (PropertyInfo result in results.GetType().GetProperties())
    {
        string name = result.Name;

        foreach (PropertyInfo info in used.GetType().GetProperties())
        {
            if (result.Name == info.Name)
            {
                Console.WriteLine("Result {0} matches class {1} and the value is {2}", result.Name, info.Name, result.GetValue(results,null));
            }                        
        }
    }
}

My query (i know this works)

current_control_id_class used = new current_control_id_class();

var query =
    from result in t_sdi_current_control_id.AsQueryable()
    where result.asset_class == asset_class
    && result.region_code == region
    && result.instance_code == site
    select new current_control_id_class() { 
        rows_loaded = result.rows_loaded,
        sdi_control_id = result.sdi_control_id,
        asset_class = result.asset_class,
        hsbc_region_code = result.hsbc_region_code,
        hsbc_instance_code = result.hsbc_instance_code
    };
2
  • Sounds like you might be able to use AutoMapper. Commented Sep 13, 2013 at 8:27
  • Is t_sdi_current_control_id a collection/array of current_control_id_class ? Or does the property names always match between the query and the class you are trying to instantiate ? Commented Sep 13, 2013 at 8:56

2 Answers 2

2

You might be able to use AutoMapper to map instances of t_sdi_current_control_id to instances of current_control_id_class:

First initialise the mapping:

Mapper.CreateMap<t_sdi_current_control_id, current_control_id_class>();

Then use it:

var query =
    from result in t_sdi_current_control_id.AsQueryable()
    where result.asset_class == asset_class
    && result.region_code == region
    && result.instance_code == site
    select Mapper.Map<current_control_id_class>(result);
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! That would be perfect! However in the company I work, I would be instantly fired for downloading anything from the internet. Don't ask....
0

If you don't want to use a third-party library, here's some tested code to do it for you:

/// <summary>
/// Maps instances of <typeparam name="TSource"/> to new instances of
/// <typeparam name="TDestination"/> by copying across accessible public
/// instance properties whose names match.
/// </summary>
/// <remarks>
/// Internally uses a compiled Expression, so mapping should be quick at
/// the expense of <see cref="Mapper"/> initialisation.
/// </remarks>
public class Mapper<TSource, TDestination>
    where TDestination : new()
{
    readonly Func<TSource, TDestination> map;

    public Mapper()
    {
        this.map = GenerateMapping();
    }

    static Func<TSource, TDestination> GenerateMapping()
    {
        var sourceProperties = GetPublicInstancePropertiesWithAccessors<TSource>(property => property.GetGetMethod());
        var destinationProperties = GetPublicInstancePropertiesWithAccessors<TDestination>(property => property.GetSetMethod());

        var source = Expression.Parameter(typeof(TSource));
        var destination = Expression.Variable(typeof(TDestination));

        var copyPropertyValues = from sourceProperty in sourceProperties
                                 from destinationProperty in destinationProperties
                                 where sourceProperty.Name.Equals(destinationProperty.Name, StringComparison.Ordinal)
                                 select Expression.Assign(
                                     Expression.Property(destination, destinationProperty),
                                     Expression.Property(source, sourceProperty)
                                 );

        var variables = new[] { destination };
        var assignNewDestinationInstance = Expression.Assign(destination, Expression.New(typeof(TDestination)));
        var returnDestinationInstance = new Expression[] { destination };
        var statements =
            new[] { assignNewDestinationInstance }
            .Concat(copyPropertyValues)
            .Concat(returnDestinationInstance);
        var body = Expression.Block(variables, statements);
        var parameters = new[] { source };
        var method = Expression.Lambda<Func<TSource, TDestination>>(body, parameters);

        return method.Compile();
    }

    /// <summary>
    /// Gets public instance properties of <typeparamref name="T"/> that
    /// have accessible accessors defined by <paramref name="getAccessor"/>.
    /// </summary>
    static IEnumerable<PropertyInfo> GetPublicInstancePropertiesWithAccessors<T>(Func<PropertyInfo, MethodInfo> getAccessor)
    {
        var type = typeof(T);
        var publicInstanceProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
        return from property in publicInstanceProperties
               let accessor = getAccessor(property)
               where accessor != null
               select property;
    }

    public TDestination Map(TSource source)
    {
        return map(source);
    }
}

Use it like this:

//Keep this around so it gets re-used.
var mapper = new Mapper<t_sdi_current_control_id, current_control_id_class>();

var result = mapper.Map(value);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.