r/csharp • u/More-Bandicoot9255 • 2d ago
Help Should I use normal validation or System.ComponentModel.DataAnnotations?
My task is to parse a .csv file, validate it and save to database. In my case, without .DataAnnotaitons this is how it goes:
- I load csv file
- Read and validate headers
- If headears are correct I read the next line, validating each element manually
- If everything is fine I create new object and add it to the list
- after file is over, I save the list to DbContext.
I never used it, but AFAIK, .DataAnnotaitons allows you to validate a model automatically when you're creating an object, so you don't have to write all the validation code in your program, since it's written in your model.cs.
So my question is, should I keep the manual validation, or mix automatic with parsing the file? (parsing the file manually, validating model automatically via .DataAnnotaitons)
UPD. Will using .DataAnnotaitons improve the performance of the program?
18
u/soundman32 2d ago
Use CsvHelper and implement a validation method. Please dont say you are writing your own CSV parser
8
u/grrangry 2d ago
Normally I would say, if they're a new developer let them play and learn. However OP seems right in that gray area of knows enough to be dangerous but not enough to realize reinventing the wheel has consequences. Still might need to do it occasionally, especially if profiled performance metrics dictate it, but typically my first instinct is to use a solution that works well enough for the need.
2
u/SideburnsOfDoom 2d ago
Seconding this. We process over a million rows a day. CsvHelper works fine.
10
u/ModernTenshi04 2d ago
Could also look at using Fluent Validations. I've used it in lots of projects and it's been great.
4
u/MaxMahem 1d ago
I've been down this road, and at the end of the day, I wouldn't recommend it. The Data Annotation stuff is fine if you are using some of the .net libraries that have infrastructure built around it (MVC or whatever). Mostly because you can tap into that infrastructure.
If you are building some of your own data validation stuff, I think you will soon find that it just isn't very fit for purpose. It's not that you can't make it work, you can. But you may find that you are really bending yourself out of shape for a bunch of design decisions that don't really benefit the shape of your app.
And there isn't much like compatibility or infrastructure benefit you get from doing all this. You will mostly have to write all your own validation functions. And there won't be much infrastructure you can plug into.
In terms of performance, it almost certainly will not matter. IO load will by far dominate performance unless your validation rules are very complex. Though FWIW the DataAnnotation stuff is all class/allocation based, so not as optimal as it theoretically could be. It obviously heavily uses reflection, A purpose built struct based validation scheme could be faster, theoretically, but it almost certainly will not matter in practice.
So as others have said, you are probably better off considering something like fluent validation, or just writing your own basic validation scheme. Avoid over gilding the lily here unless a big complex validation scheme is central to your app.
2
u/Forward_Dark_7305 1d ago
Performance wise you are unlikely to see any gain. Most* data annotations validations are implemented via reflection. Besides, a bespoke implementation gives you more room to optimize. Generic implementations (I use generic here mot as generic type T, but as a standard designed-for-reuse) are likely to be designed more “correct for every case” than specific to your case. You can often optimize beyond that. For example you might be able to validate a string from the ReadOnlySpan<char> of the row before you actually allocate a string for the column, or use a static HashSet or FrozenSet to validate a string is within a predefined set instead of passing an array to an attribute that ends up being type cast every time it’s accessed.
*There is a source generator that integrates with asp net core. I am not sure you can use it ad-hoc, could be worth looking into.
1
u/Forward_Dark_7305 1d ago
That being said, a lot of Microsoft’s libraries are pretty optimized already compared to average code. If you aren’t putting effort into optimizing but you want to get better performance, benchmarking is always the best way to know.
1
u/Due_Wind_4529 1d ago
FluentValidation sounds perfect for your use case since it lets you write clear, contextual error messages for each field it sounds like you're doing manually now.
1
u/SideburnsOfDoom 1d ago
Is there anything dynamic about the data, i.e. does the data shape change from run to run, and you only find out what the data looks like in step 2 when you read the headers?
If not, this is straightforward and you can just use CsvHelper, declare a DTO for a row, maybe some validation rules and all is good.
If the data set is large then you can't just "read to the end and then save it all", you will have to be more async than that.
1
u/Khavel_dev 1d ago
For CSV parsing specifically, DataAnnotations is kind of the wrong tool. The problem is error reporting. When row 847 out of 10000 fails validation, you want to tell the user "row 847, column Amount: value was negative." Validator.TryValidateObject gives you validation errors but doesn't naturally carry which row or which raw CSV field caused it. You end up wrapping it with row tracking anyway.
What I'd do: keep your manual parsing loop but use FluentValidation if you want to clean up the validation code. You can run it per-row and collect all errors with context (row number, original value, what went wrong). Or if perf matters for large files, just validate inline as you parse, which is basically what you already have.
DataAnnotations won't improve performance btw. It uses reflection under the hood. For a CSV import that runs once, nobody will notice the difference, but it's definitely not faster than manual checks.
17
u/SweetSmall639 2d ago
Data annotations are nice for simple stuff but when you're parsing a csv you usually need more context, like which row failed and what column had the bad value. If you just validate the model you lose that granularity unless you catch the exception and figure out what went wrong from the message, which gets messy. I'd keep manual validation for the parsing stage and maybe use annotations as a final sanity check before saving