Reputation: 1001
I have the domain classes separated from the ones I use in the views, so when retrieving data I have to map the domain classes to the view ones. Until now this have been straight forward, but now I have a case in which I need to map parent-child classes from the domain to parent-child classes on the view.
Using foreach
structures works fine, but I have quite a few linq methods that do the mapping between domain and view classes, that need to be refactored to accomodated to the new requirements and would be faster if I knew how to do it with linq. Thanks in advance.
As an example of what I'm trying to accomplish see code below:
In the repository I have the classes:
public class Parent
{
public int ParentId { get; set; }
public string ParentName { get; set; }
};
public class ChildA : Parent
{
public string ChildPropertyA { get; set; }
};
public class ChildB : Parent
{
public string ChildPropertyB { get; set; }
};
Then in the UI I have the classes:
public class ParentVM
{
public int ParentIdVM { get; set; }
public string ParentNameVM { get; set; }
};
public class ChildAVM : ParentVM
{
public string ChildPropertyAVM { get; set; }
};
public class ChildBVM : ParentVM
{
public string ChildPropertyBVM { get; set; }
};
Now I will have a service class in which the methods will look like the one below:
public GetParentVMs()
{
var parents = initializeRepositoryClass();
var parentsVM = MapRepositoryToViewClasses(parents);
ShowResult(parentsVM);
}
Where:
public List<Parent> initializeRepositoryClass()
{
var parents = new List<Parent>(){
new ChildA(){ParentId=1, ParentName="Parent 1", ChildPropertyA="A"},
new Parent(){ParentId=2, ParentName="Parent 2"},
new ChildB(){ParentId=3, ParentName="Parent 3", ChildPropertyB="B"},
};
return parents;
}
private List<ParentVM> MapRepositoryToViewClasses(List<Parent> parents)
{
var parentsVM = new List<ParentVM>();
foreach (var item in parents)
{
if (item is ChildA)
{
var itemVM = item as ChildA;
parentsVM.Add(
new ChildAVM() { ParentIdVM = itemVM.ParentId, ParentNameVM = itemVM.ParentName, ChildPropertyAVM = itemVM.ChildPropertyA }
);
}
else if (item is ChildB)
{
var itemVM = item as ChildB;
parentsVM.Add(
new ChildBVM() { ParentIdVM = itemVM.ParentId, ParentNameVM = itemVM.ParentName, ChildPropertyBVM = itemVM.ChildPropertyB }
);
}
else
{
var itemVM = item as Parent;
parentsVM.Add(
new ParentVM() { ParentIdVM = itemVM.ParentId, ParentNameVM = itemVM.ParentName }
);
}
}
return parentsVM;
}
private void ShowResult(List<ParentVM> parentsVM)
{
foreach (var item in parentsVM)
{
if (item is ChildAVM)
{
var ca = (ChildAVM)item;
Console.WriteLine("Child A " + ca.ChildPropertyAVM);
}
else if (item is ChildBVM)
{
var cb = (ChildBVM)item;
Console.WriteLine("Child B " + cb.ChildPropertyBVM);
}
else
{
Console.WriteLine("Parent ");
}
}
}
The code above will work, but I like to change the method MapRepositoryToViewClasses to another that uses linq, and looks a like the one below:
private List<ParentVM> MapRepositoryToViewClassesLinq(List<Parent> parents)
{
var parentsVM =
from p in parents
case
p is ChildA then select new ChildAVM() {ChildPropertyAVM = p.ChildPropertyA, ...};
else
p is ChildB then select new ChildBVM() {ChildPropertyBVM = p.ChildPropertyB, ...};
else
select new ParentVM() {ParentIdVM = p.ParentId};
return parentsVM.ToList();
}
Any ideas? Thanks.
Upvotes: 0
Views: 372
Reputation: 5833
You need some changes in your code to make it better
1) You have to introduce a factory to create VM's instances.
class VMFactory
{
public ParentVM Create(Parent obj)
{
var childA = obj as ChildA;
if (childA != null)
{
return new ChildAVM() { ParentIdVM = childA.ParentId, ParentNameVM = childA.ParentName, ChildPropertyAVM = childA .ChildPropertyA };
}
var childB = obj as ChildB;
if(childB != null)
{
return new ChildBVM() { ParentIdVM = childB.ParentId, ParentNameVM = childB.ParentName, ChildPropertyBVM = childB.ChildPropertyB };
}
return new ParentVM() { ParentIdVM = obj.ParentId, ParentNameVM = obj.ParentName };
}
}
2) Now you can simplify your code at MapRepositoryToViewClasses method
private List<ParentVM> MapRepositoryToViewClasses(List<Parent> parents)
{
// Factory instance can be provided by the outer scope
var factory = new VMFactory();
var parentsVM = new List<ParentVM>();
foreach (var item in parents)
{
parentsVM.Add(factory.Create(item));
}
return parentsVM;
}
3) Final step, let's use Linq to map
private List<ParentVM> MapRepositoryToViewClasses(List<Parent> parents)
{
// Factory instance can be provided by the outer scope
var factory = new VMFactory();
return parents.Select(factory.Create).ToList();
}
It's done
Yet another attempt to solve it
1) Create the extensions to solve common tasks.
static class Ext
{
public static ParentVM Map<TIn>(this TIn obj, Func<TIn, ParentVM> func)
where TIn : Parent
{
var source = obj as TIn;
return source != null
? func(obj)
: null;
}
}
2) Use the extension method to get VMs
private List<ParentVM> MapRepositoryToViewClassesLinq(List<Parent> parents)
{
var tmp = from p in parents
select
p.Map<ChildA>(c => new ChildAVM() { ParentIdVM = c.ParentId, ParentNameVM = c.ParentName, ChildPropertyAVM = c.ChildPropertyA }) ??
p.Map<ChildB>(c => new ChildBVM() { ParentIdVM = c.ParentId, ParentNameVM = c.ParentName, ChildPropertyBVM = c.ChildPropertyB }) ??
new ParentVM() { ParentIdVM = obj.ParentId, ParentNameVM = obj.ParentName };
return tmp.ToList();
}
Upvotes: 1
Reputation: 13179
If you setup mapping extensions for each type, then the mapping process becomes trivial.
SQL Fiddle: https://dotnetfiddle.net/a4eQ6S
How to map (GetParentsVM()
)
var parents = initializeRepositoryClass();
var parentsVM = parents.Map();
Mapping Extensions
public static class ParentMappings
{
public static ChildAVM Map(this ChildA model)
{
return new ChildAVM()
{
ParentIdVM = model.ParentId,
ParentNameVM = model.ParentName,
ChildPropertyAVM = model.ChildPropertyA,
};
}
public static ChildBVM Map(this ChildB model)
{
return new ChildBVM()
{
ParentIdVM = model.ParentId,
ParentNameVM = model.ParentName,
ChildPropertyBVM = model.ChildPropertyB,
};
}
public static ParentVM Map(this Parent model)
{
if (model is ChildA)
return ((ChildA)model).Map();
else if (model is ChildB)
return ((ChildB)model).Map();
else
return new ParentVM()
{
ParentIdVM = model.ParentId,
ParentNameVM = model.ParentName,
};
}
public static List<ParentVM> Map(this List<Parent> parents)
{
return parents.Select(p => p.Map()).ToList();
}
}
Upvotes: 1