Reputation: 1005
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
};
Upvotes: 1
Views: 1963
Reputation: 42387
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);
Upvotes: 0
Reputation: 42387
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);
Upvotes: 2