3

I am processing a csv file that occasionally has text in an int field due to user input error. It is the first column and basically it is supposed to be a unique record id. I am catching the errors using engine.ErrorManager.ErrorMode = ErrorMode.SaveAndContinue;

What I would rather do is just have the error ignored and replace the text with a numeric value of my choosing and still have that row included with the processed results.

I would even be fine with the process just sticking in the default value that I have declared in the file definition class file: [FieldNullValue(typeof(int), "0")]

One other thing is I am determining what file to parse at runtime like so:

Type t = assembly.GetType(assemblyName.Name + ".FileDefinitions." + className);  

FileHelperEngine engine = new FileHelperEngine(t);   

So I am not sure how to implement D.Lambert's suggestion in the context of what to put in for <CustomersFixedWithNumericId> So to clarify there are 7 different file definitions (class files) that get uploaded/processed, but all of them have the CustomerId field.

Breadtruck
  • 1,943
  • 5
  • 25
  • 39
  • That's the mother of Bad Ideas. Now it is your problem. – Hans Passant Mar 26 '11 at 20:25
  • It was my problem either way. I need to scrub the data ( all of it ) and let the user know where the problems are. Since I am reading the csv into a datatable, I figured why not keep all the records in row order and show the user that there is a problem with the record for that field. – Breadtruck Mar 27 '11 at 18:27

1 Answers1

2

Given the current state of FileHelpers, you really have to define that field as a string and try to do your own validation on it.

I looked at a couple different ways of addressing this -- first, I tried setting up a record using a property rather than a field. This could conceivably have allowed you to create a String property and validate inputs on the Set. This turned out to be difficult because:

  • Attributes are set to target fields only (fairly easy fix if you want to modify FileHelpers code.
  • Private fields for properties have to be marked w/ [FieldNotInFile] attribute (annoyance).
  • Properties aren't handled in RecordInfo.CreateCoreFields() method. This one is a little more involved to fix, since you really would have to create a merged list of properties and fields in the correct order. This is the point where I stopped looking into this method.

Next plan: use the record definition as-is, and validate during the read. Set up an event handler on the engine:

    engine.AfterReadRecord += new Events.AfterReadHandler<CustomersFixedWithNumericId>(engine_AfterReadRecord);
    var res = engine.ReadFile(path);

Then handle bad values in the handler:

    void engine_AfterReadRecord(EngineBase engine, Events.AfterReadEventArgs<CustomersFixedWithNumericId> e)
    {
        int intVal;
        int.TryParse(e.Record.CustomerID, out intVal);
        e.Record.CustomerID = intVal.ToString();
    }

Neither of these are perfect, but I think the second is reasonably close.

Addendum: This shows the technique above with a late-bound class:

public void TestMethod1()
{
    var assembly = System.Reflection.Assembly.GetExecutingAssembly();
    Type t = assembly.GetType("FileHelpers.Tests.CustomersFixedWithNumericId");
    FileHelperEngine engine = new FileHelperEngine(t);

    string path = @"pathtofile\BadCustomersFixedNumericId.txt";

    engine.AfterReadRecord += new Events.AfterReadHandler<object>(engine_AfterReadRecord);
    var res = engine.ReadFile(path);
}

void engine_AfterReadRecord(EngineBase engine, Events.AfterReadEventArgs<object> e)
{
    // validation here
}
D. Lambert
  • 1,304
  • 6
  • 12
  • Thanks, this looks reasonable, I will try it out. I am surprised I wasn't notified that you answered this question, weird! – Breadtruck Mar 30 '11 at 20:51
  • No, but its mainly due to my lack of understanding what I am doing :). – Breadtruck Apr 09 '11 at 05:47
  • I updated my question to reflect my problem with regards to your solution – Breadtruck Apr 11 '11 at 02:56
  • I had tried that but I get the error ... "The type or namespace name 't' could not be found (are you missing a using directive or an assembly reference?)" – Breadtruck Apr 11 '11 at 18:06
  • Ok, try replacing "CustomersFixedWithNumericId" in the event declarations with "object" for both chunks of code above. The event handler will no longer be able to refer to "e.Record.CustomerID" now, though, because e.Record is now just an object, but it'll run. This is starting to feel pretty brittle, though - I think I'd start to look at a way to force all the type-specific handling out to the dynamic assemblies, but that's sort of beyond the scope of this question. – D. Lambert Apr 11 '11 at 19:49
  • Well I couldn't get "object" to work either but maybe I don't understand what you mean. Even if I put in the class name defining the file import, it doesn't appear to work, compiler screams that it can't convert my Class name to an object. Your dealing with a noob, so sorry – Breadtruck Apr 11 '11 at 23:17
  • Answer edited. This should work, but I can't say I'd recommend this approach. As I said, I think I'd look at a way to push that validation down into the assembly in a way that lets the assembly that knows about that record definition handle it. – D. Lambert Apr 12 '11 at 17:19
  • Thanks for your help so far, so how would I reference the fields within e.Record ?? Better yet I was thinking, if I could loop over the object's properties/fields and check if it was an int , then try parse, and if it didn't parse stick a default value of 0 that would be the best solution for my problem. I do much more processing on the file, I just want to get every record in the file stuck in my Datatable if you know what I mean. – Breadtruck Apr 13 '11 at 15:38
  • Yeah, this is one of the places where you start to get complicated by making everything late-bound. You'd have to reflect over the properties on the record in order to access them, and since you have to define even the int fields as strings, you'd need some other indicator to show the fields that you want to do int validation on -- probably a custom attribute. You start to get pretty deep into the weeds in a hurry this way. – D. Lambert Apr 13 '11 at 16:13
  • So maybe the best thing for me to do would be to alter the filemakers source where the error is generated, and just put in my own fix. I just figured there was something already built into the framework to handle this. What do you think? – Breadtruck Apr 13 '11 at 19:49
  • I think the last thing I'd try before going that route is to move the engine.ReadFile plus the event handler into the record definition class (or some class in that assembly). You'll remove a major source of difficulty if you can get rid of the reflection over the record class' fields. This way, you can still use reflection to load that assembly and class by name, then call a new method ("Import" or whatever) on that class. Inside the record definition class, it'll know about all its own fields and you won't have to use reflection to inspect them all. – D. Lambert Apr 14 '11 at 12:50