Reputation: 17701
i am trying to use projection map to map the property values from source to destination as mentioned here in this question Projection mapping looks like as below but getting error near select statement
Error is : The type arguments for the method 'Enumerable.Select<TSource, Tresult>(IEnumerable, Func<Tsource, int, Tresult>)' cannot be inferred from usage.Try specifying type arguments explicitly
and below is the code sample
public static IQueryable<TDest> ProjectionMap<TSource, TDest>(IQueryable<TSource> sourceModel)
where TDest : new()
{
var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
var destProperties = typeof(TDest).GetProperties().Where(p => p.CanWrite);
var propertyMap = from d in destProperties
join s in sourceProperties on new { d.Name, d.PropertyType } equals new { s.Name, s.PropertyType }
select new { Source = s, Dest = d };
var itemParam = Expression.Parameter(typeof(TSource), "item");
var memberBindings = propertyMap.Select(p => (MemberBinding)Expression.Bind(p.Dest, Expression.Property(itemParam, p.Source)));
var newExpression = Expression.New(typeof(TDest));
var memberInitExpression = Expression.MemberInit(newExpression, memberBindings);
var projection = Expression.Lambda<Func<TSource, TDest>>(memberInitExpression, itemParam);
return sourceModel.Select(projection);
}
and then i am using above method below
private static MechanicalData TransformMechanicalData(MechanicalData sourceMechanicalData, Dictionary<string, MasterSection> masterSectionMappedLibrary)
{
return new MechanicalData()
{
Acoustic = sourceMechanicalData.Acoustic
.Where(a => a != null)
.Select(ProjectionMap<LibraryAcoustic, LibraryAcoustic>(sourceMechanicalData.Acoustic.AsQueryable())).ToList() ?? new(),
}
}
Could any one please let me know where I am doing wrong, many thanks in advance.
Update:
Acoustic = sourceMechanicalData.Acoustic
.Where(a => a != null)
.Select(acoustic => new LibraryAcoustic
{
Id = acoustic.Id,
IsApproved = true,
NoiseCriteria = acoustic.NoiseCriteria,
SourceOfData = acoustic.SourceOfData,
SourceOfDataId = acoustic.SourceOfData.Id,
MasterSection = masterSectionMappedLibrary["Library Acoustic"]
}).ToList() ?? new(),
calling that transformMechanicalData method in below
if (spaceTypeReader.HasRows)
{
while (spaceTypeReader.Read())
{
var id = spaceTypeReader.IsDBNull(0) ? default : Guid.Parse(spaceTypeReader.GetString(0));
var mechanicalDataJson = spaceTypeReader.IsDBNull(1) ? "null" : spaceTypeReader.GetString(1);
var srcMechanicalDataJson = JsonConvert.DeserializeObject<MechanicalData>(mechanicalDataJson);
fixedSpaceTypesMechanicalData[id] = TransformMechanicalData(srcMechanicalDataJson, masterSectionMappedLibrary);
}
}
and mechanical data class be like
public class MechanicalData
{
public List<LibraryAcoustic> Acoustic { get; set; }
.........
}
Update 2:
model for libraryAcoustic
public class LibraryAcoustic
{
public double? NoiseCriteria { get; set; }
[ForeignKey("SourceOfData")]
public Guid? SourceOfDataId { get; set; }
public virtual CodeStandardGuideline SourceOfData { get; set; }
public Guid Id { get; set; }
public MasterSection MasterSection { get; set; }
public bool? IsApproved { get; set; }
}
FROM Model
"Acoustic": [
{
"Id": "d9254132-d11d-48dd-9b74-c0b60d1e4b8a",
"IsApproved": null,
"SourceOfData": {
"Id": "c5bf3585-50b1-4894-8fad-0ac884343935",
"CodeStandardGuidelineType": "GUIDELINE_OR_STANDARD"
},
"MasterSection": null,
"SourceOfDataId": null,
"NoiseCriteria": 1,
}
],
TO model:
"Acoustic": [
{
"Id": "d9254132-d11d-48dd-9b74-c0b60d1e4b8a",
"IsApproved": true,
"SourceOfData": {
"Id": "c5bf3585-50b1-4894-8fad-0ac88434393",
"CodeStandardGuidelineType": "GUIDELINE_OR_STANDARD"
},
"MasterSection": {Name:"test"},
"SourceOfDataId": "c5bf3585-50b1-4894-8fad-0ac884343935",
"NoiseCriteria": 1,
}
],
Test Class update:
SourceOfData sourceOfData = new SourceOfData()
{
Id = new Guid("c5bf3585-50b1-4894-8fad-0ac884343935"),
Name = "test"
};
TestClassA170 testClassA170 = new TestClassA170()
{
Category = "test",
SourceOfData = sourceOfData,
SourceOfDataId = null,
IsApproved = true,
MinOutdoorAirACH = 1,
MinTotalAirACH = 2,
DirectExhaust = DirectExhaust.NO,
PressureRelationship = PressureRelationship.NEGATIVE,
RecirculatedAir = RecirculatedAir.NO,
SpaceFunction = "10"
};
List<TestClassA170> list = new List<TestClassA170>();
list.Add(testClassA170);
Upvotes: 0
Views: 203
Reputation: 27528
You have to create mapping helper class which accespts additionally Dictionary as paraneter:
public static class PropertyMapper<TSource, TDest>
{
private static Expression<Func<TSource Dictionary<string, MasterSection>, TDest>> _mappingExpression;
private static Func<TSource, Dictionary<string, MasterSection>, TDest> _mapper;
static PropertyMapper()
{
_mappingExpression = ProjectionMap();
_mapper = _mappingExpression.Compile();
}
public static Func<TSource, Dictionary<string, MasterSection>, TDest> Mapper => _mapper;
public static string MasterKeyFromClassName(string className)
{
// you have to do that yourself
throw new NotImplementedException();
}
public static Expression<Func<TSource, Dictionary<string, MasterSection>, TDest>> ProjectionMap()
{
var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
var destProperties = typeof(TDest).GetProperties().Where(p => p.CanWrite);
var propertyMap =
from d in destProperties
join s in sourceProperties on new { d.Name, d.PropertyType } equals new { s.Name, s.PropertyType }
where d.Name != "MasterSection"
select new { Source = s, Dest = d };
var itemParam = Expression.Parameter(typeof(TSource), "item");
var dictParam = Expression.Parameter(typeof(Dictionary<string, MasterSection>), "dict");
var memberBindings = propertyMap.Select(p => (MemberBinding)Expression.Bind(p.Dest, Expression.Property(itemParam, p.Source))).ToList();
var masterSectionProp = destProperties.FirstOrDefault(s => s.Name == "MasterSection");
if (masterSectionProp != null)
{
Expression<Func<Dictionary<string, MasterSection>, string, MasterSection>> dictRetrievalTemplate = (dict, value) => dict[value];
var masterPropertyBind = Expression.Bind(masterSectionProp, ExpressionReplacer.GetBody(dictRetrievalTemplate, dictParam, Expression.Constant(MasterKeyFromClassName(typeof(TSource).Name)));
memberBindings.Add(masterPropertyBind);
}
var sourceOfDataProp = destProperties.FirstOrDefault(s => s.Name == "SourceOfDataId");
if (sourceOfDataProp != null)
{
memberBindings.Add(Expression.Bind(sourceOfDataProp, Expression.Property(Expression.Property(itemParam, "SourceOfData"), "Id")));
}
var isApprovedProp = destProperties.FirstOrDefault(s => s.Name == "IsApproved");
if (isApprovedProp != null)
{
memberBindings.Add(Expression.Bind(isApprovedProp, Expression.Constant(true)));
}
var newExpression = Expression.New(typeof(TDest));
var memberInitExpression = Expression.MemberInit(newExpression, memberBindings);
var projection = Expression.Lambda<Func<TSource, Dictionary<string, MasterSection>, TDest>>(memberInitExpression, itemParam, dictParam);
return projection;
}
class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> { { toReplace, toExpr } }).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
.ToDictionary(i => (Expression) lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
}
}
}
Then rewrite your function:
private static Expression<MechanicalData TransformMechanicalData(MechanicalData sourceMechanicalData, Dictionary<string, MasterSection> masterSectionMappedLibrary)
{
return new MechanicalData()
{
Acoustic = sourceMechanicalData.Acoustic
.Where(a => a != null)
.Select(a => PropertyMapper<LibraryAcoustic, LibraryAcoustic>.Mapper(a, masterSectionMappedLibrary)).ToList(),
}
}
Upvotes: 1