// Copyright 2009-2022 Josh Close // This file is a part of CsvHelper and is dual licensed under MS-PL and Apache 2.0. // See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0. // https://github.com/JoshClose/CsvHelper using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; namespace CsvHelper.Expressions { /// /// Hydrates members of an existing record. /// public class RecordHydrator { private readonly CsvReader reader; private readonly ExpressionManager expressionManager; private readonly Dictionary hydrateRecordActions = new Dictionary(); /// /// Creates a new instance using the given reader. /// /// The reader. public RecordHydrator(CsvReader reader) { this.reader = reader; expressionManager = ObjectResolver.Current.Resolve(reader); } /// /// Hydrates members of the given record using the current reader row. /// /// The record type. /// The record. public void Hydrate(T record) { try { GetHydrateRecordAction()(record); } catch (TargetInvocationException ex) { if (ex.InnerException != null) { throw ex.InnerException; } else { throw; } } } /// /// Gets the action delegate used to hydrate a custom class object's members with data from the reader. /// /// The record type. protected virtual Action GetHydrateRecordAction() { var recordType = typeof(T); if (!hydrateRecordActions.TryGetValue(recordType, out Delegate action)) { hydrateRecordActions[recordType] = action = CreateHydrateRecordAction(); } return (Action)action; } /// /// Creates the action delegate used to hydrate a record's members with data from the reader. /// /// The record type. protected virtual Action CreateHydrateRecordAction() { var recordType = typeof(T); if (reader.Context.Maps[recordType] == null) { reader.Context.Maps.Add(reader.Context.AutoMap(recordType)); } var mapping = reader.Context.Maps[recordType]; var recordTypeParameter = Expression.Parameter(recordType, "record"); var memberAssignments = new List(); foreach (var memberMap in mapping.MemberMaps) { var fieldExpression = expressionManager.CreateGetFieldExpression(memberMap); if (fieldExpression == null) { continue; } var memberTypeParameter = Expression.Parameter(memberMap.Data.Member.MemberType(), "member"); var memberAccess = Expression.MakeMemberAccess(recordTypeParameter, memberMap.Data.Member); var memberAssignment = Expression.Assign(memberAccess, fieldExpression); memberAssignments.Add(memberAssignment); } foreach (var referenceMap in mapping.ReferenceMaps) { if (!reader.CanRead(referenceMap)) { continue; } var referenceAssignments = new List(); expressionManager.CreateMemberAssignmentsForMapping(referenceMap.Data.Mapping, referenceAssignments); var referenceBody = expressionManager.CreateInstanceAndAssignMembers(referenceMap.Data.Member.MemberType(), referenceAssignments); var memberTypeParameter = Expression.Parameter(referenceMap.Data.Member.MemberType(), "referenceMember"); var memberAccess = Expression.MakeMemberAccess(recordTypeParameter, referenceMap.Data.Member); var memberAssignment = Expression.Assign(memberAccess, referenceBody); memberAssignments.Add(memberAssignment); } var body = Expression.Block(memberAssignments); return Expression.Lambda>(body, recordTypeParameter).Compile(); } } }