combining these two methods into single one - c#

Hi all i have two classes and i have two methods where i am passing these two classes into methods individually and checking name and NarrativeHTML and rendering bulleted list.
public class LibraryHydronicEquipment : AEIMasterBase
{
public string Name { get; set; }
public HydronicEquipmentType? Type { get; set; }
[Column(TypeName = "jsonb")]
public List<HydronicSystemType> Systems { get; set; }
[Column(TypeName = "jsonb")]
public List<EquipmentProperty> Properties { get; set; }
public string NarrativeHTML { get; set; }
}
public class LibrarySteamEquipment : AEIMasterBase
{
public string Name { get; set; }
public SteamEquipmentType? Type { get; set; }
[Column(TypeName = "jsonb")]
public List<SteamSystemType> Systems { get; set; }
[Column(TypeName = "jsonb")]
public List<EquipmentProperty> Properties { get; set; }
public string NarrativeHTML { get; set; }
}
Method 1:
private void BuildBulletedList(List<LibraryHydronicEquipment> libraryHydronicEquipments)
{
List<Run> labels = new List<Run>();
foreach (var equipmentItem in libraryHydronicEquipments)
{
if (!string.IsNullOrEmpty(equipmentItem.NarrativeHTML))
{
var htmlRuns = this.DocumentHtmlConverter.Parse(equipmentItem.NarrativeHTML)
.First()
.ChildElements
.Where(c => c is Run)
.Cast<Run>()
.Select(n => n.CloneNode(true));
labels.Add(new Run(htmlRuns));
}
else if (!string.IsNullOrEmpty(equipmentItem.Name))
{
labels.Add(new Run(new Text(equipmentItem.Name)));
}
}
BuildList(labels);
}
Method 2
private void BuildBulletedList(List<LibrarySteamEquipment> librarySteamEquipments)
{
List<Run> labels = new List<Run>();
foreach (var equipmentItem in librarySteamEquipments)
{
if (!string.IsNullOrEmpty(equipmentItem.NarrativeHTML))
{
var htmlRuns = this.DocumentHtmlConverter.Parse(equipmentItem.NarrativeHTML)
.First()
.ChildElements
.Where(c => c is Run)
.Cast<Run>()
.Select(n => n.CloneNode(true));
labels.Add(new Run(htmlRuns));
}
else if (!string.IsNullOrEmpty(equipmentItem.Name))
{
labels.Add(new Run(new Text(equipmentItem.Name)));
}
}
BuildList(labels);
}
and i am calling these methods like as below
if (hydronicSystem.Equipment.Source.Count != 0)
{
BuildBulletedList(hydronicSystem.Equipment.Source);
}
if (steamSystem.Equipment.Source.Count != 0)
{
BuildBulletedList(steamSystem.Equipment.Source);
}
update :
if (hydronicSystem.Equipment.Source.Count != 0)
{
BuildBulletedList(hydronicSystem.Equipment.Source);
}
if (hydronicSystem.Equipment.Distribution.Count != 0)
{
BuildBulletedList(hydronicSystem.Equipment.Distribution);
}
if (hydronicSystem.Equipment.Terminals.Count != 0)
{
BuildBulletedList(hydronicSystem.Equipment.Terminals);
}
Is there any way we can combine these two methods into single generic method?
thanks in advance!!

Move the shared members into a base type
public interface IRender {
string Name { get; set; }
string NarrativeHTML { get; set; }
}
and have the classes derive from that type
public class LibraryHydronicEquipment : AEIMasterBase, IRender {
public string Name { get; set; }
public HydronicEquipmentType? Type { get; set; }
[Column(TypeName = "jsonb")]
public List<HydronicSystemType> Systems { get; set; }
[Column(TypeName = "jsonb")]
public List<EquipmentProperty> Properties { get; set; }
public string NarrativeHTML { get; set; }
}
public class LibrarySteamEquipment : AEIMasterBase, IRender {
public string Name { get; set; }
public SteamEquipmentType? Type { get; set; }
[Column(TypeName = "jsonb")]
public List<SteamSystemType> Systems { get; set; }
[Column(TypeName = "jsonb")]
public List<EquipmentProperty> Properties { get; set; }
public string NarrativeHTML { get; set; }
}
then refactor the method to depend on that type via generics
private void BuildBulletedList<T>(IEnumerable<T> items) where T : IRender {
List<Run> labels = new List<Run>();
foreach (T item in items) {
if (!string.IsNullOrEmpty(item.NarrativeHTML)) {
var htmlRuns = DocumentHtmlConverter
.Parse(item.NarrativeHTML)
.First()
.ChildElements
.Where(c => c is Run)
.Cast<Run>()
.Select(n => n.CloneNode(true));
labels.Add(new Run(htmlRuns));
} else if (!string.IsNullOrEmpty(item.Name)) {
labels.Add(new Run(new Text(item.Name)));
}
}
BuildList(labels);
}
Now to make a single call, concatenate the lists and then check to see if a bullet list can be built.
var source = hydronicSystem.Equipment.Source.Cast<IRender>();
var distribution = hydronicSystem.Equipment.Distribution.Cast<IRender>();
var terminals = hydronicSystem.Equipment.Terminals.Cast<IRender>();
var bullets = source.Concat(distribution).Concat(terminals);
if (bullets.Any()) {
BuildBulletedList(bullets);
}

Related

How to improve the code of mapping from List into ModelRequest

Hello this is a mapping from a list (made by a SensorID field and a SensorValue) to a request model in which all values of the SensorID are reported as properties.
I did this:
public List<SensorStatus> SensorsStatus { get; set; }
public class SensorStatus
{
[Required]
public string SensorId { get; set; }
public string SensorValue { get; set; }
}
and the model request:
public class NotificheDiStatoModel
{
public int? qrStatus { get; set; }
public bool? qrEnabled { get; set; }
public int? clessStatus { get; set; }
public bool? clessEnabled { get; set; }
public int? magStatus { get; set; }
public bool? magEnabled { get; set; }
public int? puncherStatus { get; set; }
public bool? puncherEnabled { get; set; }
public int? bleStatus { get; set; }
public bool? bleEnabled { get; set; }
}
The mapping that works for now I did it this way,but I would like to improve the code
var listasensori = req.MessageBody.SensorsStatus;
NotificheDiStatoModel outputreq = new NotificheDiStatoModel();
foreach (var sensore in listasensori)
{
if (sensore.SensorId.Equals("blestatus"))
outputreq.bleStatus = Convert.ToInt32(sensore.SensorValue);
if (sensore.SensorId.Equals("bleenabled"))
outputreq.bleEnabled = Convert.ToBoolean(sensore.SensorValue);
if (sensore.SensorId.Equals("clessstatus"))
outputreq.clessStatus = Convert.ToInt32(sensore.SensorValue);
if (sensore.SensorId.Equals("clessenabled"))
outputreq.clessEnabled = Convert.ToBoolean(sensore.SensorValue);
if (sensore.SensorId.Equals("qrstatus"))
outputreq.qrStatus = Convert.ToInt32(sensore.SensorValue);
if (sensore.SensorId.Equals("qrenabled"))
outputreq.qrEnabled = Convert.ToBoolean(sensore.SensorValue);
if (sensore.SensorId.Equals("magstatus"))
outputreq.magStatus = Convert.ToInt32(sensore.SensorValue);
if (sensore.SensorId.Equals("magenabled"))
outputreq.magEnabled = Convert.ToBoolean(sensore.SensorValue);
if (sensore.SensorId.Equals("puncherstatus"))
outputreq.puncherStatus = Convert.ToInt32(sensore.SensorValue);
if (sensore.SensorId.Equals("puncherenabled"))
outputreq.puncherEnabled = Convert.ToBoolean(sensore.SensorValue);
};
Thanks in advance.
I would make your SensorId of enum type rather than string type:
public enum SensorIdEnum {
blestatus,
bleenabled,
clessstatus,
...
}
Your SensorStatus class would become:
public class SensorStatus
{
[Required]
public SensorIdEnum SensorId { get; set; }
public string SensorValue { get; set; }
}
This would reduce risk of typo and make documentation and extension easier.
Then, I would use generics to enforce a type on the SensorValue property right from the start.
public class SensorStatus<T>
{
[Required]
public SensorIdEnum SensorId { get; set; }
public T SensorValue { get; set; }
}
This way you're making the data conversion the responsibility of the SensorStatus itself (in its constructor maybe).
And you could make your outputreq basically a Dictionary<SensorIdEnum, object> that you pass around instead of a full fledged NotificheDiStatoModel class that you don't necessarily fully use:
var listasensori = req.MessageBody.SensorsStatus;
var outputreq = new Dictionary<SensorIdEnum, object>();
foreach (var sensore in listasensori)
{
outputreq[sensore.SensorId] = sensore.SensorValue;
};
Can be simplified like this. Just add all the strings that you checked in the Equals in a list. Example below
NotificheDiStatoModel outputreq = new NotificheDiStatoModel();
var senors = new List<SensorStatus>();
var acceptedSensorIds = new List<string> {"blestatus","bleenabled","clessstatus","clessenabled","qrstatus"};
foreach (var sensore in senors)
{
acceptedSensorIds.ForEach(id =>
{
if (sensore.SensorId.Equals(id, StringComparison.OrdinalIgnoreCase))
{
var propertyInfo = outputreq.GetType().GetProperty(id);
propertyInfo.SetValue(outputreq,Convert.ToBoolean(sensore.SensorValue));
}
});
}

Retrieve part of structured data from POCOs C#

I have a List<TerminalLink> from which I want to retrieve a subset of. See the class structure below.
The subset is defined by a "map" which identifies which ChannelLink to select in any given scenario. Multiple ChannelLinks may be required per scenario, perhaps from the same ChannelGroup, perhaps from different ChannelGroups on the same TerminalLink, and perhaps from multiple TerminalLinks in my original in memory list.
The ChannelLink can only be select if it's ChannelGroupLink and TerminalLink match too (they form part of the index, I guess).
I have tried various Linq and foreach approaches, but I'm running into tens of lines of code, which is ugly and difficult to follow. I'm sure there is an elegant approach.
How do I return a pruned List<TerminalLink> containing just the data that matches my map (i.e. containing only TerminalLinks and ChannelGroupLinks with required ChannelLinks)?
Class structure:
public class TerminalLink
{
public string TerminalName { get; set; }
public string TerminalType { get; set; }
public ChannelGroupLink[] ChannelGroups { get; set; }
}
public class ChannelGroupLink
{
public string GroupName { get; set; }
public int ItemType { get; set; }
public int SubType { get; set; }
public ChannelLink[] Channels { get; set; }
}
public class ChannelLink
{
public string ChannelName { get; set; }
public string PathName { get; set; }
public string DataType { get; set; }
public bool IsOutput { get; set; }
}
EDIT to include the latest attempt:
public static List<TerminalLink> GetCompatibleChannelLinks(List<TerminalLinkMap> terminalLinkMaps, List<TerminalLink> asBuiltList)
{
List<TerminalLink> matching = new List<TerminalLink>();
var terminalsWithSomeMatchingChannelGroups = from terminal in asBuiltList
from tlm in terminalLinkMaps
where terminal.TerminalType == tlm.ItemSubTypeName
from cgsMaps in tlm.ChannelGroupsToLink
from cgs in terminal.ChannelGroups
where cgsMaps.IsLinkMatch(cgs.GroupName, cgs.ItemType, cgs.SubType)
select (
link: new TerminalLink()
{
TerminalName = terminal.TerminalName,
TerminalType = terminal.TerminalType
},
original: terminal, map: tlm
);
foreach (var possibleLink in terminalsWithSomeMatchingChannelGroups)
{
var linkCandidate = possibleLink.link;
var originalData = possibleLink.original;
var map = possibleLink.map;
foreach (var cgs in originalData.ChannelGroups)
{
foreach (var channel in cgs.Channels)
{
// WIP
}
}
}
}
EDIT 2: inclusion of TeminalLinkMap
public class TerminalLinkMap
{
public string TerminalTypeName { get; set; }
public string ItemSubTypeName { get; set; } // Match on this.
public ChannelGroupToLink[] ChannelGroupsToLink { get; set; }
}
public class ChannelGroupToLink
{
public string GroupName { get; set; } // regex match is ok.
public int ItemType { get; set; }
public int SubType { get; set; }
public ChannelToLink[] Channels { get; set; }
public ChannelGroupToLink GetClone() => GetClone(this);
public bool IsLinkMatch(string groupName, int itemType, int subType)
{
return
GroupName == groupName
&& ItemType == itemType
&& SubType == subType;
}
}
public class ChannelToLink
{
public int ItemType { get; set; }
public string DataType { get; set; }
public int DataSizeInBits { get; set; }
public bool IsOutput { get; set; }
public ChannelToLink GetClone() => GetClone(this);
public bool IsLinkMatch(string dataType, bool isOutput)
{
return
DataType == dataType
&& IsOutput == isOutput;
}
public static ChannelToLink GetClone(ChannelToLink original) => new ChannelToLink()
{
ItemType = original.ItemType,
DataType = original.DataType,
DataSizeInBits = original.DataSizeInBits,
IsOutput = original.IsOutput
};
}
There is no need for a join.
Try following:
class Program
{
static void Main(string[] args)
{
List<TerminalLink> asBuiltList = new List<TerminalLink>();
List<TerminalLink> links = asBuiltList
.Select(x => new { TerminalLink = x, ChannelGroups = x.ChannelGroups.Where(y => y.IsLinkMatch("Apple", 123, 456)) })
.Where(x => x.ChannelGroups.Count() > 0)
.Select(x => new TerminalLink() { TerminalName = x.TerminalLink.TerminalName, TerminalType = x.TerminalLink.TerminalType, ChannelGroups = x.ChannelGroups.ToArray()})
.ToList();
}
}
public class TerminalLink
{
public string TerminalName { get; set; }
public string TerminalType { get; set; }
public ChannelGroupLink[] ChannelGroups { get; set; }
}
public class ChannelGroupLink
{
public string GroupName { get; set; }
public int ItemType { get; set; }
public int SubType { get; set; }
public ChannelLink[] Channels { get; set; }
public Boolean IsLinkMatch(string groupName, int itemType, int subType)
{
return (this.GroupName == groupName) && ( this.ItemType == itemType) && (this.SubType == subType);
}
}
public class ChannelLink
{
public string ChannelName { get; set; }
public string PathName { get; set; }
public string DataType { get; set; }
public bool IsOutput { get; set; }
}
}

Executing Actions within Actions for collection without creating new collection

I have collection of objects where each object contains other collections of objects. All I want to modify my original collection of objects in a way, that no extra memory should be allocated. Everything should happen in memory.
I am looking for some templatized Action or Func so that with the mixture of declarative and functional approach, no new collection is formed and in memory my original collection is also modified.
Following is the hierarchy of collection
public class ConsignmentAddress
{
public int ConsignmentAddressId { get; set; }
public int AddressTypeId { get; set; }
public string Street { get; set; }
public string Town { get; set; }
public string ZipCode { get; set; }
public int ConsignmentId { get; set; }
}
public class ConsignmentLine
{
public int ConsignmentLineId { get; set; }
public int PackagingId { get; set; }
public double Amount { get; set; }
public double Weight { get; set; }
public int ConsignmentId { get; set; }
public int? PackagingAidId { get; set; }
}
public class PackagingAid
{
public int PackagingAidId { get; set; }
public int ConsignmentId { get; set; }
public int PackagingId { get; set; }
public double Quantity { get; set; }
}
public class Consignment
{
public int ConsignmentId { get; set; }
public int ClientSubsidiaryId { get; set; }
public int ForwarderId { get; set; }
public int Sourcepaty { get; set; }
public int ParentId { get; set; }
public int ProductId { get; set; }
public ICollection<PackagingAid> PackagingAids { get; set; }
public ICollection<ConsignmentLine> ConsignmentLines { get; set; }
public ICollection<ConsignmentAddress> ConsignmentAddresses { get; set; }
public Consignment()
{
PackagingAids = new List<PackagingAid>();
ConsignmentLines = new List<ConsignmentLine>();
ConsignmentAddresses = new List<ConsignmentAddress>();
}
}
My intention is to write generic extension method which should operate on all kind of objects in the hierarchy I mentioned above.
public static class ConsignmentExtension
{
public static T Set<T>(this T input, Action<T> updater)
{
updater(input);
return input;
}
}
Where as my client code is which is first getting list of 10000 of objects in list and just resetting few properties of ( consignment, consignmentaddresses for corresponding consignment, consignmentlines of corresponding consignment and packagingaid of corresponding consignment)
My written Foreach approach worked fine but i want templatized and efficient approach for the same collection.
consignments.ForEach(cons =>
{
cons.ConsignmentId = -1;
cons.ConsignmentAddresses.ToList().ForEach(address =>
{
address.ConsignmentId = -1;
address.ConsignmentAddressId = -1;
});
cons.ConsignmentLines.ToList().ForEach(line =>
{
line.ConsignmentId = -1;
line.ConsignmentLineId = -1;
line.PackagingAidId = -1;
});
cons.PackagingAids.ToList().ForEach(aid =>
{
aid.ConsignmentId = -1;
aid.PackagingAidId = -1;
});
});
But I am looking for something like declarative style.
var updatedConsignments = from consignment in consignments
select consignment.Set<Consignment>(con =>
{
con.ConsignmentId = -1;
con.ConsignmentAddresses.Set<dynamic>(address => { address.ConsignmentId = -1; address.ConsignmentAddressId = -1; });
con.ConsignmentLines.Set<dynamic>(line => { line.PackagingAidId = -1; line.ConsignmentId = -1;line.ConsignmentLineId = -1; });
con.PackagingAids.Set<dynamic>(aid => { aid.ConsignmentId = -1; aid.PackagingAidId = -1; });
});
Is that possible somehow?
Thanks in advance.
This could be written as
public static class ConsignmentExtension
{
public static IEnumerable<dynamic> SetEach(this IEnumerable<dynamic> input, Action<dynamic> updater)
{
foreach (var item in input)
{
updater(item);
}
return input;
}
}
Usage:
var addresses = new []
{
new ConsignmentAddress()
};
addresses.SetEach(a => a.ConsignmentId = -1);
var packages = new []
{
new PackagingAid()
};
packages.SetEach(p => p.PackagingAidId = -1);

Using Linq to pass data from one collection to another

I want to use LINQ to pass data from one custom collection to another. Its complicated because the collection has 2 sub collections.
Want to copy data to:
public class Quote
{
public int Id { get; set; }
public string Type { get; set; }
public virtual ICollection<Rate> Rates { get; set; }
}
public class Rate
{
public int Id { get; set; }
public virtual ICollection<Option> Options { get; set; }
}
public class Option
{
public int Id { get; set; }
public decimal Price { get; set; }
}
from:
public class Quote
{
public int QuoteId { get; set; }
public string Type { get; set; }
public string Destination { get; set; }
public List<RateSet> RateSets { get; set; }
}
public class RateSet
{
public int Id { get; set; }
public decimal ValueMin { get; set; }
public decimal ValueMax { get; set; }
public List<Option> Options { get; set; }
}
public class Option
{
public int Id { get; set; }
public string Name { get; set; }
public decimal? Price { get; set; }
}
I was getting somewhere with this but keeping hitting problems...
newQuotes = Quotes
.Select(x => new Quote() {
Id = x.QuoteId,
Rates = x.RateSets.Select( y => new Rate() {
Id = y.Id,
Options = y.Options.Select(z => new Option() {
Id = z.Id,
Price = z.Price
}).ToList(),....
to
Compiled without any errors
// to
public class Quote2
{
public int Id { get; set; }
public string Type { get; set; }
public virtual ICollection<Rate> Rates { get; set; }
}
public class Rate
{
public int Id { get; set; }
public virtual ICollection<Option2> Options { get; set; }
}
public class Option2
{
public int Id { get; set; }
public decimal Price { get; set; }
}
// from
public class Quote1
{
public int QuoteId { get; set; }
public string Type { get; set; }
public string Destination { get; set; }
public List<RateSet> RateSets { get; set; }
}
public class RateSet
{
public int Id { get; set; }
public decimal ValueMin { get; set; }
public decimal ValueMax { get; set; }
public List<Option1> Options { get; set; }
}
public class Option1
{
public int Id { get; set; }
public string Name { get; set; }
public decimal? Price { get; set; }
}
void Main()
{
var Quotes = new List<Quote1>();
var newQuotes = Quotes
.Select(x => new Quote2 {
Id = x.QuoteId,
Rates = x.RateSets == null ? null : x.RateSets.Select( y => new Rate {
Id = y.Id,
Options = y.Options == null ? null : y.Options.Select(z => new Option2 {
Id = z.Id,
Price = z.Price.Value
}).ToList()}).ToList()}).ToList();
}
I would make it a bit more modular:
newQuotes = Quotes.Select(x => new Quote
{
ID = x.QuoteID,
Type = x.Type,
Rates = ConvertRates(x.RateSets)
});
ConvertRates would use the same approach to create its sub objects and could either be a method or a Func:
ICollection<Rate> ConvertRates(IEnumerable<RateSet> oldRates)
{
return oldRates.Select(x => new Rate
{
ID = x.ID,
Options = ConvertOptions(x.Options)
}).ToList();
}
Basically, this is the same approach you used, just split up and readable.
I think what you need to do is define casting between each two corresponding classes, then cast one list into the other.
A simpler way may be to create methods in each class that would convert itself to the other type. Or if you don't want that kind of coupling, create a factory class that will do the conversion for you, one item at a time. Then use link to loop through and convert each item.
Like so:
public class Quote
{
public int Id { get; set; }
public string Type { get; set; }
public virtual ICollection<Rate> Rates { get; set; }
public static Quote FromData(Data.Quote input){
if (input == null) return null;
Quote output = new Quote()
{
Id = input.QuoteId,
Type = input.Type
};
output.Rates = (from i in input.RateSets
select Rate.FromData(i)).ToList();
}
}
public class Rate
{
public int Id { get; set; }
public virtual ICollection<Option> Options { get; set; }
public static Rate FromData(Data.RateSet input)
{
if (input == null) return null;
Rate output = new Rate()
{
Id = input.Id
};
output.Options = (from i in input.Options
select Option.FromData(i)).ToList();
return output;
}
}
public class Option
{
public int Id { get; set; }
public decimal Price { get; set; }
public static Option FromData(Data.Option input)
{
if (input == null) return null;
Option output = new Option()
{
Id = input.Id,
Price = input.Price ?? 0m
};
return output;
}
}
namespace Data {
public class Quote
{
public int QuoteId { get; set; }
public string Type { get; set; }
public string Destination { get; set; }
public List<RateSet> RateSets { get; set; }
}
public class RateSet
{
public int Id { get; set; }
public decimal ValueMin { get; set; }
public decimal ValueMax { get; set; }
public List<Option> Options { get; set; }
}
public class Option
{
public int Id { get; set; }
public string Name { get; set; }
public decimal? Price { get; set; }
}
}

EmitMapper's List Mapping Issue with Collections

The source class:
public class Post
{
public long ID { get; set; }
[Column(TypeName="nvarchar")]
[Required]
[StringLength(250)]
public string Name { get; set; }
[Column(TypeName="varchar")]
[StringLength(250)]
public string UrlName { get; set; }
[Column(TypeName="ntext")]
public string Excerpt { get; set; }
[Column(TypeName="ntext")]
[Required]
public string Content { get; set; }
public DateTime PostedTime { get; set; }
public DateTime? PublishedTime { get; set; }
public DateTime? LastUpdatedTime { get; set; }
public bool IsPublished { get; set; }
public virtual List<Category> Categories { get; set; }
public virtual List<Comment> Comments { get; set; }
public virtual List<Tag> Tags { get; set; }
}
the destination class
public class Post : Model
{
public long ID { get; set; }
public string Name { get; set; }
public string UrlName { get; set; }
public string Excerpt { get; set; }
public string Content { get; set; }
public DateTime PostedTime { get; set; }
public DateTime LastCommentedTime { get; set; }
public bool IsPublished { get; set; }
public List<Category> Category { get; set; }
public List<Comment> Comments { get; set; }
public List<Tag> Tags { get; set; }
}
I try using EmitMapper to map from each other; when mapping from source to desction, here is the code sampleļ¼š
[TestMethod]
public void ShouleMapEntityToModel()
{
Post eP = new Post();
eP.ID = 2;
eP.Comments = new List<Comment>();
eP.Comments.Add(new Comment()
{
ID = 2,
Author = "derek"
});
var mP = eP.Map<Post, mBlog.Core.Models.Post>();
Assert.IsNotNull(mP);
Assert.AreEqual(1, mP.Comments.Count());
}
and I got an exception,
Test method mBlog.Test.EmitMapperTest.ShouleMapEntityToModel threw exception:
System.Exception: Constructor for types [] not found in System.Collections.Generic.IList`1[[mBlog.Core.Models.Post, mBlog.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
I had the same problem, but I have found the solution. Don't user Lists for your destination object. If you use simple arrays in your mBlog.Core.Models.Post object you should get a nicely filled object. So your destination class should look like:
public class Post : Model
{
public long ID { get; set; }
public string Name { get; set; }
public string UrlName { get; set; }
public string Excerpt { get; set; }
public string Content { get; set; }
public DateTime PostedTime { get; set; }
public DateTime LastCommentedTime { get; set; }
public bool IsPublished { get; set; }
public Category[] Category { get; set; }
public Comment[] Comments { get; set; }
public Tag[] Tags { get; set; }
}
This answer shows how to handle IEnumerable to IEnumerable: EmitMapper and List
I believe that can be applied to this case too. Take a look:
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable
collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and
can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

Categories