Attributes and elements missing from serialized object - c#

I have some generated proxy classes, that contain properties with the XMLAttribute attribute, e.g.
[System.Xml.Serialization.XmlAttributeAttribute(Form=System.Xml.Schema.XmlSchemaForm.Qualified, Namespace="http://www.egem.nl/StUF/StUF0301")]
public Verwerkingssoort verwerkingssoort
{
get
{
return this.verwerkingssoortField;
}
set
{
this.verwerkingssoortField = value;
}
}
This and several other properties don't show up in the SOAP message however. The proxies were generated using svcutil.
As I'm writing this question I notice that the properties that do show up as attributes are strings, while this is an Enum (but I haven't done a thorough check, yet). Does somebody know if this is the problem, or what else it could be and what possible solutions there are?
Update:
After some more checking, I found that no Enums are included as attribute, but Strings and Bools work fine.
Update 2:
In a simple case an Enum as attribute is serialized just fine.
Update 3:
If I replace XmlAttributeAttribute(...) with XmlIgnoreAttribute() and add the following property:
[System.Xml.Serialization.XmlAttributeAttribute("verwerkingssoort")]
public string verwerkingssoortString
{
get
{
return this.verwerkingssoortField.ToString();
}
set
{
this.verwerkingssoortField = (Verwerkingssoort)System.Enum.Parse(typeof(Verwerkingssoort), value, true);
}
}
it works fine, but making changes like this throughout the generated code would be a rather Herculean task, and when the contract changes, I would need to do it all over again. So I'm still looking for a better solution.
Update 4:
It turns out that this problem is not limited to attributes. I just noticed that a property that should be serialized to an element is omitted as well. The same things apply: it is an Enum, and it works fine if I change it to a string.
Thanks, regards,
Miel.

Have you tried setting Type property of the XmlAttributeAttribute class to the type of your enum?

Related

Mapping Enum to string column with custom SqlMapper.ITypeHandler - Dapper

I have a large number of PL/SQL stored procs that return columns with single character strings representing some kind of status value from a fixed range. In the project I'm working on, these columns have been mapped by Dapper to string properties on the domain objects, which are awkward and unreliable to manage, so I'd like to switch to enums.
If I used enums with single character names like enum Foo {A, P} I'm pretty sure Dapper would map them correctly but I don't want that, I want enums with descriptive labels like so:
enum Foo {
[StringValue("A")]
Active,
[StringValue("P")]
Proposed
}
In the above example, StringValueAttribute is a custom attribute and I can use reflection to convert the "A" to Foo.Active, which works fine - except I need Dapper to perform that conversion logic for me. I wrote a custom type handler to do this:
public class EnumTypeHandler<T> : SqlMapper.TypeHandler<T>
{
public override T Parse(object value)
{
if (value == null || value is DBNull) { return default(T); }
return EnumHelper.FromStringValue<T>(value.ToString());
}
public override void SetValue(IDbDataParameter parameter, T value)
{
parameter.DbType = DbType.String;
parameter.Value = EnumHelper.GetStringValue(value as Enum);
}
}
//Usage:
SqlMapper.AddTypeHandler(typeof(Foo),
(SqlMapper.ITypeHandler)Activator.CreateInstance(typeof(EnumTypeHandler<>).MakeGenericType(typeof(Foo)));
The registration with SqlMapper.AddTypeHandler() seems to work fine, but when my DbConnection.Query() code runs, I get an error saying that the value 'A' could not be converted - the error is thrown from Enum.Parse, suggesting that Dapper isn't actually calling my type handler at all despite it being registered. Does anyone know a way around this?
Another user has reported this as an issue on Dapper's github site. Seems like it's a deliberate optimisation specifically around enums in Dapper, so I've changed my database model rather than trying to change the mapping code. I looked at trying to modify Dapper itself, but the source code of Dapper is optimised like nothing I've ever seen, emitting opcodes to perform conversions in the most performant way possible - no way I want to start trying to work out how to make changes there.

An easy way to validate an XML against a C# Class

I use the XML format to keep settings for my C# project.
Theses XMLs are deserialized into C# classes.
Often enough, a class design changes but I forget to update the XML. Deserialization usually works fine and missing elements just get their default values.
This behavior is not desirable for me. I would like to have automatic validation that asserts the class and the XML have exactly the same structure.
I know I can create XML schemas (e.g using XSD) for my classes, but I could not figure an easy way to do it automatically and not manually (recall my classes' design changes often). Also, this solution seems kind of unnatural. I do not really need XML schemas. I have classes and their serialized XML instances. Adding schemas seems superfluous.
Thanks a bunch.
Why you don't create a method in your settings class that can be invoked after xml deserialization?
Suppose you have a class like this:
class Settings {
public string Property1 { get; set; }
public int Property2 { get; set; }
public bool IsValid() {
if(string.IsNullOrEmpty(Property1)) return false;
if(Property2 == 0) return false;
}
}
Using IsValid you can check everything in your class. Please, remember that this is an example. I think is good to manage object validation. If you change something in the time, you can edit the validation method to check new situations.
To go with Roberto's idea and take it a step further you could get all the properties via reflection:
var props = yourClass.GetType().GetProperties()
Inside of your validation function you could loop over those properties with:
foreach(var prop in props) // or foreach(var prop in yourClass.GetType().GetProperties())
{
//...Validation of property
}
If one of the properties has its standard-value you throw a custom exception that tells you you did not update your XML-file properly.
You can implement this using Version Tolerant Serialization (VTS) https://msdn.microsoft.com/en-us/library/ms229752%28v=vs.110%29.aspx
The Serialization Callbacks is what you are looking for in the VTS capabilities

How to specify order of debugger visualizers in Visual Studio

I've been working on a debugger visualizer for Visual Studio for some time and while the actual visualizer works fine. The problem is that it always places itself at the top of the visualizer list when examining a variable which really annoys some of the users who rather have Text as the top one (since the top one is also default when opening VS).
I can't find any support for this on DialogDebuggerVisualizer or DebuggerVisualizerAttribute which were my first thoughts so I've been scouring SO/MSDN/Google for information on how to affect the sort order of the visualizers (preferably to put mine last in the list) but to no avail.
Below is how I register my visualizer, it then just shows a form based on the value that is being visualized.
using Microsoft.VisualStudio.DebuggerVisualizers;
[assembly: System.Diagnostics.DebuggerVisualizer(
typeof(Shorthand.VSAddins.JsonVisualizer.JsonVisualizer),
typeof(VisualizerObjectSource),
Target = typeof(string),
Description = "Json Visualizer")]
namespace Shorthand.VSAddins.JsonVisualizer
{
public class JsonVisualizer : DialogDebuggerVisualizer
{
protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
{
var json = objectProvider.GetObject() as string;
var form = new VisualizerForm { Json = json };
windowService.ShowDialog(form);
}
}
}
Does anyone know if it is possible to affect the order of the visualizers or should I just let it be?
I don't think there is a solution. But there is a workaround:
Define your own Text Visualizer and put appropriate DebuggerVisualizer attribute before the attribute of your JsonVisualizer. The result will be that string will be readable by default and Json Visualizer can be chosen. A window with a multi-line textbox is not too much work.
It is probably not even necessary to write visualizer. It should be possible to use internal one but I don't know its name (Which class is used for "Text Visualizer"?).
It will always appear first, by design. The under the hood cast has found the best match for the variable it is reflecting on.
however, you could do either of two things. You could make the visualizer only appear when the sting contains ':'
Or you could use reflection to reorder the visualisers by adding them to the end of the collection in the order you want, then removing the originals from the collection.
For the latter you will most likely have to change the collection from readonly to writable. Via reflection.
There is no reliable source to draw on other than your will to succeed.
I guess that VS 'under the hood' can distinguish between type of string and type of xml quite easily, but Xml is just a string too, so a key question here would be, how does VS tell the difference between the two?
Could you dissect the VS XML visualizer to see how it works (even if you have to use reflector on the DLL to do it, you might get to see the method that works it out)

how to iterate through reader for notnull without repeating code over and over

First off, I am new to programming (especially with C#) and thanks for your help.
I have a static web form with about 150 form objects (most checkboxes). I decided to go 1 record per form submission in the sql db. So, for example, question X has a choice of 5 checkboxes. Each of these 5 checkboxes has a column in the db.
I have the post page complete(working) and am building an edit page where I load the record and then populate the form.
How I am doing this is by passing a stored proc the id and then putting all the returned column values into the according object properties, then setting the asp control object to them.
An example of setting the asp controls to the selected value:
questionX.Items[0].Selected = selectedForm.questionX0
questionX.Items[1].Selected = selectedForm.questionX1
questionX.Items[2].Selected = selectedForm.questionX2
As you see, this is very tiresome since there are over 150 of these to do. Also, I just found out if the response is NULL then I get the error that it cant be converted to a string. So, I have added this line of code to get past it:
This is the part where I am populating the returned column values into the object properties (entity is the object):
if (!String.IsNullOrEmpty((string)reader["questionX0"].ToString()))
{entity.patientUnderMdTreatment = (string)reader["questionX0"];}
So, instead of having to add this if then statement 150+ times. There must be a way to do this more efficiently.
First of all, it seems that you are using string.IsNullOrEmpty(value), but this won’t check for the special DBNull value that is returned from databases when the data is null. You should use something more akin to value is DBNull.
The rest of your problem sounds complex, so please don’t be put off if my answer is complex too. Personally I would use custom attributes:
Declare a custom attribute
The following is a skeleton to give you the idea. You may want to use the “Attribute” code snippet in Visual Studio to find out more about how to declare these.
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class QuestionColumnAttribute : Attribute
{
public string ColumnName { get; private set; }
public QuestionColumnAttribute(string columnName)
{
ColumnName = columnName;
}
}
Use the custom attribute in the entity class
Where you declare your entity class, add this custom attribute to every field, for example where patientUnderMdTreatment is declared:
[QuestionColumn("questionX0")]
public string patientUnderMdTreatment;
Iterate over the fields
Instead of iterating over the columns in the reader, iterate over the fields. For each field that has a QuestionColumnAttribute on it, get the relevant column from the reader:
foreach (var field in entity.GetType().GetFields())
{
var attributes = field.GetCustomAttributes(typeof(QuestionColumnAttribute), true);
if (attributes.Length == 0)
continue;
object value = reader[attributes[0].ColumnName];
if (!(value is DBNull))
field.SetValue(entity, value.ToString());
}
For the first part of your question where you set the ASP controls, you can use a similar strategy iterating over the fields of selectedForm, and this is probably simpler because you don’t need a custom attribute — just take only the fields whose name starts with “questionX”.
this is a quick & easy way of doing it.. there are some suggestions to investigate LINQ, and I'd go with those first.
for (int i = 0; i < 150; i++)
{
if (!String.IsNullOrEmpty((string)reader["questionX" + i.ToString()].ToString()))
{entity.patientUnderMdTreatment = (string)reader["questionX" + i.ToString()];}
}
... though this wouldn't be any good with the
questionX.Items[0].Selected = selectedForm.questionX0
questionX.Items[1].Selected = selectedForm.questionX1
questionX.Items[2].Selected = selectedForm.questionX2
lines
so I hear two questions:
- how to deal with null coming from IDataReader?
- how to deal with multiple fields?
Lets start with simple one. Define yourself a helper method:
public static T IsDbNull<T>(object value, T defaultValue)
{
return (T)(value is DBNull ? defaultValue : value);
}
then use it:
entity.patientUnderMdTreatment = IsDbNull<string>(reader["question"], null);
Now how to map entity fields to the form? Well that really is up to you. You can either hardcode it or use reflection. The difference of runtime mapping vs compile-time is likely to be completely irrelevant for your case.
It helps if your form fields have identical names to ones in the DB, so you don't have to do name mapping on top of that (as in Timwi's post), but in the end you'll likely find out that you have to do validation/normalization on many of them anyway at which point hardcoding is really what you need, since there isn't a way to dynamically generate logic according to the changing spec. It doesn't matter if you'll have to rename 150 db fields or attach 150 attributes - in the end it is always a O(n) solution where n is number of fields.
I am still a little unsure why do you need to read data back. If you need to preserve user's input on form reload (due to validation error?) wouldn't it be easier/better to reload them from the request? Also are entity and selectedForm the same object type? I assume its not a db entity (otherwise why use reader at all?).
Its possible that there are some shortcuts you may take, but I am having hard time following what are you reading and writing and when.
I recommend using the NullableDataReader. It eliminates the issue.

Downcasting in C#

I'm facing a problem that I don't know how to solve and am hoping the community can help.
I'm writing an app that manages "Lead" objects. (These are sales leads.) One part of my program will import leads from a text file. Now, the text file contains lots of potential leads, some of which I will want to import and some of which I won't.
For ease of programming (and use), I'm parsing the text file into a List<Lead> object, and using a DataGridView to display the leads by setting the DataSource property of the DataGridView.
What I want to do is add a column to the grid, called "Import," with a checkbox that the user can check to indicate whether or not each lead should be imported.
My first thought is to derive a class from Lead:
public Class LeadWithImportCheckbox : Lead
{
bool bImport = false;
public bool Import
{
get { return bImport;}
set { bImport = value;}
}
}
However, the parsing engine returns a list of Lead objects. I can't downcast a Lead to a LeadWithImportCheckbox. This fails:
LeadWithImportCheckbox newLead = (LeadWithImportCheckbox)LeadFromParsingEngine;
This is an invalid cast.
The other option I see is to create a constructor for LeadWithImportCheckbox:
public LeadWithImportCheckbox(Lead newlead)
{
base.Property1 = newlead.Property1;
base.Property2 = newlead.Property2;
....
base.Property_n = newlead.Property_n;
}
This is problematic for two reasons. One, the Lead object has several dozen properties and writing this constructor is a PITA.
But worse, if I ever change the underlying structure of Lead, I need to remember to go back and change this constructor for LeadWithImportCheckbox. This is a danger to my code maintenance.
Is there a better way of accomplishing my goal?
or, to avoid the PITA aspect, use reflection... (try this...)
EDIT: use property, not Field as I had originally written...
public class NewLead : Lead
{
public bool Insert;
public NewLead(Lead lead, bool insert)
{
Insert = insert;
foreach (PropertyInfo pi in typeof(Lead).GetProperties())
GetType().GetProperty(pi.Name).SetValue
(this, pi.GetValue(lead,null), null);
}
}
public class LeadListItem
{
public Lead Lead { get; set; }
public bool ShouldImport { get; set; }
}
i.e. don't copy the Lead object's contents, just store a reference to it in a new LeadListItem object, which adds extra info "outside" the original object.
If you want the properties of Lead to appear in the grid, there is almost certainly a way of doing that. Why not ask that question, instead of downvoting me for telling you the right answer to this question!
A couple options you might have missed:
You could update the Lead object itself to have an Import property (that defaults to false).
You could have your "ImportLead" object treat the Lead as payload (even make it generic, if you want), so you don't need the big constructor.
Build a new Lead object list or enumerable that only contains the objects you want to import in the first place.
You can only downcast, if the object to be downcast is really an object of that type.
An easier way to solve your problem would be to have a DisplayLead class, such as:
public class DisplayLead {
Lead lead;
bool bImport;
}
which would also help you separating stored data from their representation in a GUI.
What you want to do is display the checkbox column on your grid and not have it related at all to your Lead objects. You use the marked columns (and possible the original List) to build a new set of List which will be your import list.
Then handle whatever you wish to do with the newly created List.
Edit: One thing to be careful of when working with lists is the fact every class object is actually only a pointer to the class so if you work with the original list and do something like:
List<Lead> Importable = new List<Lead>();
for(int i=0, i++, i<viewGrid.Count)
if(viewGrid[i].CheckedColumn.Checked)
Importable.Add(OriginalList[i]);
That objects will exist in both lists and if you edit data of a Lead on either list both will be changed.
I cannot downcast to something it is not. If the object was instantiated as a Lead, then it can't be downcast to any derived class. If it were instantiated as a LeadWithImportCheckbox and then returned to your code as Lead, then you can downcast it.
Protip: Check type at runtime with is operator.
There are many ways to do this, but the "right" way pops out because of what you said, here:
For ease of programming (and use), I'm
parsing the text file into a
List object, and using a
DataGridView to display the leads by
setting the DataSource property of the
DataGridView.
What I want to do is add a column to
the grid, called "Import," with a
checkbox that the user can check to
indicate whether or not each lead
should be imported.
Your Lead object stands well on its own, and you want to attach some metadata to it -- you don't want to create another Lead classification (i.e. the LeadWithImportCheckbox class).
So, the best approach in your case is to have a class like so:
public class LeadInfo
{
private Lead lead;
private bool shouldImport;
public LeadInfo(Lead lead)
{
this.lead = lead;
this.ShouldImport = false;
}
public bool ShouldImport
{
get { return shouldImport; }
set { shouldImport = value; }
}
}
This will scale well when you want to add more metadata to your list, like if you want to send yourself email reminders about them every week.
I've seen the correct solution listed so many times I feel like a heel posting it again, but the best way to approach this is to write a wrapper for the Lead object that includes the import flag.
If the properties of the Lead object don't appear in the GridView because you're databinding to the object, then write passthrough properties that mirror the Lead properties on the wrapper object.
The issue is that you want something displayed to the user that isn't an inherent part of the data model. The answer is to wrap the data before presenting it to the user so you can control what they see without changing the underlying model.
If you're concerned that the Lead object will change so many times in the future that changes to the wrapper will be cumbersome, you could look into dynamic code generation based on the Lead object that will automatically generate a wrapper object with the same fields as the Lead object plus the import flag. Though frankly, that's a lot more work than you'll probably need for something as straightforward as this.
As a quick and dirty solution, you can create your 'checkbox' object as a different object that contains an instance of Lead.
public GridLead {
public bool Import { get; set; }
public Lead Lead { get; set; }
}
This way you can easily add more 'grid' properties to this object, while still always retaining a reference to the Lead details without hardcoding property cloning into it.
Recommend you try modifying (upgrading) your imported lead objects.
Try starting with the examples here...
If your Lead class had a copy constructor (e.g. "Lead(Lead otherLead)"), LeadWithImportCheckbox would inherit that and you could just call the base Lead constructor in the LeadWithImportCheckbox constructor - hence no need for LeadWithImportCheckbox to be aware of the details of Lead.

Categories

Resources