-
Notifications
You must be signed in to change notification settings - Fork 1
New Methods to define references as collections "SetCollectionOf"
Imagine the "ReferenceProperty" is a Collection Type:
class ExampleTargetClass
{
int IntProperty;
List<ExampleReferenceClass> ReferenceProperty;
}
class ExampleReferenceClass
{
int IntRefProperty;
string StringRefProperty;
}
Now you can say to map a property to a CsvField and add objects to the collection instead assign them directly to the property:
class MapExampleTargetClass : CsvClassMap<ExampleTargetClass>
{
public MapExampleTargetClass()
{
Map( x => x.IntProperty ).Name("Column 1");
References<MapExampleReferenceClass>( x => x.ReferenceProperty ).SetCollectionOf( typeof(ExampleReferenceClass) );
}
}
class MapExampleReferenceClass : CsvClassMap<ExampleReferenceClass>
{
public MapExampleReferenceClass()
{
Map( x => x.IntRefProperty ).Name("Column 2");
Map( x => x.StringRefProperty ).Name("Column 3");
}
}
The only requirement is, the collection type must have an method "Add". Such a method have for example all collection inherited from "ICollection".
If we put this and the "Constructor" feature of a ReferencePropertyMap-Class together, we can map csv files directly to an entity framework POCO object with navigation properties. A class like this will be defined as follows:
public class Order
{
public Order()
{
this.OrderItems = new HashSet<OrderItem>();
}
public int OrderId { get; set; }
public virtual ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int OrderItemId { get; set; }
}
As you see a 1:N navigation property is defined via an "ICollection". The concrete implementiation used is "HashSet". Now you can built a mapping like this:
class MapOrder : CsvClassMap<Order>
{
public MapOrder()
{
Map( x => x.OrderId ).Name("OrderId");
References<MapOrderItem>(x => x.OrderItems).IsCollectionOf(typeof(OrderItem));
}
}
class MapOrderItem : CsvClassMap<OrderItem>
{
public MapOrder()
{
//Do not forget this:
Constructor = Expression.New(typeof(HashSet<OrderItem>));
Map( x => x.OrderItemId ).Name("OrderItemId");
}
}
Some thoughts on the method "Reference", and using it multiple times:
Using the method "Reference" multiple times, with the same property, will not throw an error but it make no sense (so far):
Reference(x=>x.IntProperty).Name("Column1");
Reference(x=>x.IntProperty).Name("Column2");
This design will always use the value of "Column2" to map to the property. But introducing the new features it start to make sense to use it like this. Imagine a csv file with 3 columns: "ItemId", "ItemNameInEnglish", "ItemNameInSpanish". The database will, of course, have a 1:N relation between Item and Itemnames in different languages. The class you will have to map generated by the entity framework is:
public class Item
{
public Item()
{
this.ItemNames = new HashSet<ItemName>();
}
public int ItemId { get; set; }
public virtual ICollection<ItemName> ItemNames { get; set; }
}
public class ItemName
{
public int LanguageId { get; set; }
public string Name { get; set; }
}
Now you can map a csv file this way:
class MapItem : CsvClassMap<Item>
{
public MapItem()
{
Map( x => x.ItemId ).Name("ItemId");
References<MapItemNameInEnglish>(x => x.ItemNames).IsCollectionOf(typeof(ItemName));
References<MapItemNameInSpanish>(x => x.ItemNames).IsCollectionOf(typeof(ItemName));
}
}
class MapItemNameInEnglish : CsvClassMap<ItemName>
{
public MapItemNameInEnglish()
{
Constructor = Expression.New(typeof(HashSet<ItemName>));
Map( x => x.LanguageId ).SetConstantValue(1); //Here you have to set the language id of "english"
Map( x => x.Name ).Name("ItemNameInEnglish");
}
}
class MapItemNameInSpanish : CsvClassMap<ItemName>
{
public MapItemNameInSpanish()
{
Constructor = Expression.New(typeof(HashSet<ItemName>));
Map( x => x.LanguageId ).SetConstantValue(2); //Here you have to set the language id of "spanish"
Map( x => x.Name ).Name("ItemNameInSpanish");
}
}
For me it is a relevant feature. If in future versions reference should be restricted to be used for every property only one time this would not work.
Now imagine a csv file with 6 columns, specifying names in different languages. Or 6 columns specifying images of a product you want to map into a collection. You would have to write 10 different classes. To avoid this the method "References" now have a optional "params object[]" parameter. With this you can specify parameters for constructing your CsvClassMap. Now you have to write only one class:
class MapItem : CsvClassMap<Item>
{
Map( x => x.ItemId ).Name("ItemId");
References<MapItemName>(x => x.ItemNames, "English", 1).IsCollectionOf(typeof(ItemName));
References<MapItemName>(x => x.ItemNames, "Spanish", 2).IsCollectionOf(typeof(ItemName));
References<MapItemName>(x => x.ItemNames, "Greek", 3).IsCollectionOf(typeof(ItemName));
References<MapItemName>(x => x.ItemNames, "German", 4).IsCollectionOf(typeof(ItemName));
References<MapItemName>(x => x.ItemNames, "Italian", 5).IsCollectionOf(typeof(ItemName));
References<MapItemName>(x => x.ItemNames, "French", 6).IsCollectionOf(typeof(ItemName));
}
class MapItemName : CsvClassMap<ItemName>
{
public MapItemName(string columnSuffix, int languageId)
{
Constructor = Expression.New(typeof(HashSet<ItemName>));
Map( x => x.LanguageId ).SetConstantValue(languageId);
Map( x => x.Name ).Name("ItemNameIn"+columnSuffix);
}
}