CsvHelper icon indicating copy to clipboard operation
CsvHelper copied to clipboard

Runtime Entity and Property mapping

Open bborad opened this issue 8 years ago • 14 comments

Hi

I am trying to map entity and properties runtime similar to what @itaylorweb has done at #36 but it seems "CsvPropertyMap" is no longer available in latest version and there is no example at http://joshclose.github.io/CsvHelper/#mapping-runtime-mapping either.

Does anyone know how to do this with current version of CsvHelper?

Thanks

bborad avatar Oct 24 '17 03:10 bborad

The old documentation went into a 2.x folder http://joshclose.github.io/CsvHelper/2.x/#mapping-runtime-mapping.

CsvPropertyMap changed to MemberMap as it now encompasses both properties and fields.

JoshClose avatar Oct 24 '17 03:10 JoshClose

is there new version of that document?

could you post an example here.

bborad avatar Oct 24 '17 03:10 bborad

To add to this, I'm in the process of updating from v2: Old Code (for reference):

var propertyInfo = typeof(MyModel).GetProperty(propertyName);
var newMap = new CsvPropertyMap(propertyInfo);
newMap.Name(columnName);
myClassMap.PropertyMaps.Add(newMap);

Error was with CsvPropertyMap which I managed to resolve with the following (hopefully this helps @bborad ):

var propertyInfo = typeof(MyModel).GetProperty(propertyName);
var newMap = MemberMap.CreateGeneric(typeof(MyModel), propertyInfo);
//newMap.Name(columnName);
myClassMap.MemberMaps.Add(newMap);

Which is working great except that I can't define the column name as I can with a predefined ClassMap eg:

public sealed class MyClassMap : ClassMap<MyModel>
{
    public OrderClassMap()
    {
        Map(m => m.Description).Name("Object Description");
    }
}

It seems that MemberMap.CreateGeneric isn't giving me the generic version which has the extra functions for defining csv field names etc: Error CS1061 'MemberMap' does not contain a definition for 'Name' and no extension method 'Name' accepting a first argument of type 'MemberMap' could be found

Kirlac avatar Oct 25 '17 01:10 Kirlac

@Kirlac You can specify the CSV column name by newMap.Data.Names.Add("CSV Column Name")

here's what I do

                var map = MemberMap.CreateGeneric(typeof(MyClass), t.GetProperty(m.PropertyName));
                map.Data.Names.Add(m.CsvColumnName);
                custCodeMap.MemberMaps.Add(map);

bborad avatar Oct 25 '17 02:10 bborad

Yeah I was digging through the source code to try and figure that one out. Thanks!

Now I just need to work out why I'm getting this CsvHelper.ReaderException: No header record was found. at CsvHelper.CsvReader.ParseNamedIndexes() at CsvHelper.CsvReader.ReadHeader() error and I should be done with the upgrade

Kirlac avatar Oct 25 '17 02:10 Kirlac

@JoshClose

How does Reference mapping done in run time. What I have is

User {
  public int UserID { get; set; }
  public string FName{ get; set; }
  public string LName{ get; set; }
  public int? UserTpyeID { get; set; }
 public virtual UserType Role { get; set; } 
}

UserType {
   public int UserTypeID { get; set; }
  public string UserTypeName { get; set; }
}

Csv will be like User, Role

UserA, admin UserB, user

I need to map Role to User->UserType.UserTypeName column. Could you please help on how to achieve this in runtime mapping.

Thanks

bborad avatar Oct 25 '17 02:10 bborad

@Kirlac

That's what I did as well. I am planing to post my code as an example if I can complete it with CsvHelper (else have to roll back everything and have to do it normal way - reading row - create instance and add or update). I will help people understand how to do runtime mapping with new version including mapping reference tables.

bborad avatar Oct 25 '17 02:10 bborad

@bborad Don't know how much this helps, but I was able to get runtime reference mapping like this (using your above user/role example):

var userMap = new UserMap();
var userTypeMap = new UserTypeMap();

// Define user mapping
var userInfo = typeof(User).GetProperty("FName");
var newUserMap = MemberMap.CreateGeneric(typeof(User), userInfo);
newUserMap.Data.Names.Add("User");
userMap.MemberMaps.Add(newUserMap);

// Define role mapping
var userTypeInfo = typeof(UserType).GetProperty("UserTypeName");
var newRoleMap = MemberMap.CreateGeneric(typeof(UserType), userTypeInfo);
newRoleMap.Data.Names.Add("Role");
userTypeMap.MemberMaps.Add(newRoleMap);

// Add reference between them through the "Role" property on "User"
var roleInfo = typeof(User).GetProperty("Role");
var refMap = new MemberReferenceMap(roleInfo, userTypeMap);
userMap.ReferenceMaps.Add(refMap);

Kirlac avatar Oct 25 '17 03:10 Kirlac

Thanks @Kirlac , it worked....

bborad avatar Oct 25 '17 05:10 bborad

I posted an example yesterday, but it looks like it didn't save.

One thing that isn't good is you don't have access to map.Name() and have to do map.Data.Names.Add(). That isn't very helpful and I'll change that for the next version.

There should also be an easier way to do reference mapping. With a class map you can do Map( m => m.A.B.C.Id ). There needs to be a helper method to create that reference chain for you if you're runtime mapping.

JoshClose avatar Oct 25 '17 14:10 JoshClose

Hi @Kirlac

Would mind quick look at my reference mapping issue - Mapping Multiple Navigation-Reference properties of same type #814

Thanks

bborad avatar Oct 27 '17 07:10 bborad

@Kirlac You can specify the CSV column name by newMap.Data.Names.Add("CSV Column Name")

here's what I do

                var map = MemberMap.CreateGeneric(typeof(MyClass), t.GetProperty(m.PropertyName));
                map.Data.Names.Add(m.CsvColumnName);
                custCodeMap.MemberMaps.Add(map);

Hey @bborad , I'm using the same approach:

                    var newMap = MemberMap.CreateGeneric(typeof(CsvEntry), propertyInfo);
                    newMap.Data.Names.Add(columnName);
                    customMap.MemberMaps.Add(newMap);

Then I'm registering the classMap using csvWriter.Configuration.RegisterClassMap(customMap); But, unfortunately, when I write the records, my header names are appended by the character '1'. Eg. "Name" becomes "Name1". Please help! Thank you :)

GautamGadipudi avatar Nov 07 '18 10:11 GautamGadipudi

It would seem that this functionality is now subsumed by the ClassMap.Map(...) and RegisterClassMap(...) APIs. Is this still useful? Should this issue just be pulled in under #703?

marcrocny avatar Jan 10 '19 20:01 marcrocny

@GautamGadipudi

Then I'm registering the classMap using csvWriter.Configuration.RegisterClassMap(customMap); But, unfortunately, when I write the records, my header names are appended by the character '1'. Eg. "Name" becomes "Name1". Please help! Thank you :)

I know It's too late, but I'll share what I found.

 var newMap = MemberMap.CreateGeneric(typeof(CsvEntry), propertyInfo);
newMap.Data.Names.Add(columnName);
newMap.Data.Index = 0;
customMap.MemberMaps.Add(newMap);

By adding an index you can get rid of the number appending to the csv file header.

seokhoyoun avatar Nov 17 '21 01:11 seokhoyoun