Serialization in ASP.NET Web API - xml-serialization

I am using XmlSerializer instead of DataContractSerializer in my ASP.NET Web API project and have a return object defined as
Response Object
public class MyResponse
{
public string Name {get;set;}
public CustomField<string> Username {get;set;}
public CustomField<float?> Score {get;set;}
}
Custom Field
public class CustomField<T>
{
public T Value {get;set;}
public long LastModified {get;set;}
}
I want to generate an XML response as
<MyResponse>
<FirstName>ABC</FirstName>
<Username lastModified="1234">XYZ</Username>
<Score lastModified="45678">12002</Score>
</MyResponse>
ASP.NET Web API returns a JSON object (I am aware that this happens when XmlSerialization does not work correctly) when I decorate the CustomField class as
public class CustomField<T>
{
[XmlText]
public T Value {get;set;}
[XmlAttribute]
public long LastModified {get;set;}
}
How can I get the desired XML response ?

Alright, I think I know what's going on.
If you try to run
new XmlSerializer(typeof(MyResponse))
you'll get this error:
System.InvalidOperationException: Cannot serialize member 'Value' of type System.Nullable`1[System.Single]. XmlAttribute/XmlText cannot be used to encode complex types.
So the issue is that you have a field of type 'float?' as an [XmlText]. [XmlText] can only be applied to primitives, and it doesn't look like XmlSerializer recognizes 'float?' as a primitive. If you use 'float' instead of 'float?', everything looks to be working right. If you want to indicate that sometimes there is no Score, you may want to set the Score to null instead of the Score's value to null.
Hope that helps.

Related

How can I prevent the default value of DateTimeOffset from being inserted?

I'm using Entity Framework Core 3.1.7 and created an entity called Event, which I set up like this:
public class Event
{
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; }
}
The entity configuration looks like this:
builder.Property(e => e.FirstOccurred)
.IsRequired();
I use my dbContext to persist the entity like this:
await dbContext.Events.AddAsync(new Event());
In this scenario, I was incorrectly expecting that an exception would be thrown at the Database level because the value can't be null.
What actually happens is: the entity is happily persisted with FirstOccurred set to 0001-01-01T00:00:00+00:00
This makes sense, because the default value of DateTimeOffset is used.
Now my question: How could I improve my design to prevent this default value from being inserted?
Some ideas I had already:
Leave the above code as is, but make sure that wherever the entity is used, I'm setting the values correctly. Downside: no guarantee that this will be applied consistently in a team over time.
Make DateTimeOffset nullable, which in the above AddAsync() call would actually cause an SQL exception. Downside: At first glance, DateTimeOffset? FirstOccurred might be confusing because the actual DB constraints don't allow null
Remove set; for FirstOccurred and create a constructor that requires this property to be set, e.g. new Event(DateTimeOffset.Now)
I think you're on the right track with your last idea.
Remove set; for FirstOccurred and create a constructor that requires this property to be set, e.g. new Event(DateTimeOffset.Now)
It doesn't make sense to track an Event without the timestamp and it certainly doesn't make sense to use the default value for the timestamp.
Changing your model to require a value for the timestamp ensures that you are not writing default data to the record and prevents confusion from seeing a nullable model field when the corresponding table column is non-nullable.
public class Event {
public Event (DateTimeOffset firstOccurred) { FirstOcurred = firstOcurred; }
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; }
}
Just a note from the documentation, don't remove the set; accessor, just mark it private if you don't want the value to change after construction.
Once properties are being set via the constructor it can make sense to make some of them read-only. EF Core supports this, but there are some things to look out for:
Properties without setters are not mapped by convention. (Doing so tends to map properties that should not be mapped, such as computed properties.)
Using automatically generated key values requires a key property that is read-write, since the key value needs to be set by the key generator when inserting new entities.
An easy way to avoid these things is to use private setters.
Of course, you could also maintain the flexibility of the parameter-less constructor by overriding the default value for the property.
public class Event {
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; } = DateTimeOffset.Now;
}

What is the correct way to tell WebApi2 and EF6 to not serialize a field to Json?

I have an Entity Framework Table per Type hierarchy like this:
public class WorkItem
{
public int WorkItemId {get;set;}
}
public class CancelingWorkItem : WorkItem
{
public int WorkItemIdToCancel {get;set;}
[ForeignKey("WorkItemIdToCancel")]
public virtual WorkItem WorkItemToCancel {get;set}
}
public class SomeOtherWorkItem : WorkItem
{
// more fields...
}
When I return a list of all WorkItems in the database as Json, any serialized CancelingWorkItem will contain the full definition of the WorkItemToCancel field. I could just ignore this field with JsonIgnore, but I was wondering if there was a different/better way of doing this. My repository project doesn't yet rely on Json.Net, so if I can instead tell the controller not to serialize that field, that might be a better solution.
You can use the IgnoreDataMemberAttribute attribute - It's not from an external library like JsonIgnore and I think that the default serializer and Json.NET will both recognise this attribute.

Returning a subset of a navigation propertie's object

I have a one to many relationship as outlined below. In some parts of the business layer there are queries of the Item table and in others the Client table (as well as its Items). LazyLoading and ProxyCreation are both false, reference loop handling is set to ignore.
public class Client {
public virtual ICollection<Item> Items { get; set; }
public string Name {get;set;}
}
public class Item {
public virtual Client TheClient {get;set;}
public string ItemProp {get;set;}
// another 10 properties or so
}
myitems = dbContextScopeX.Items.Include(x => x.Client).ToList();
The view has a list of items with the need to show the Client's Name (in my example). I am looking for item.Client.Name ultimate, however when myitems gets queries/serialized it contains:
myitems.Client.Items
If I set the attribute [JsonIgnore] on the Client's Item property it never comes through the graph which I need it to in other places. Is there a way to get myItems.Client.Name without having to get myitems.Client.Items in the query or without having to create an anonymous projection for the Item array?
Project the Item properties you want (be they simple or complex type) along with just the Client name into an anonymous type and serialize that.
myitems = dbContextScopeX.Items.Include(x => x.Client)
.Select(i=>new {
ItemProp = i.ItemProp,
ItemCollection = i.ItemCollection,
...
ClientName = i.Client.Name
}).ToList();
Only caveat is you have to do some manual work if you want to deserialize this back into entities.

Entity Framework and implementation of IPrincipal/IIdentity

As far as I am aware, for the property to be saved in the database it cannot be ReadOnly.
IIdentity properties: AuthenticationType, IsAuthenticated and Name are all ReadOnly.
Is making the wrapper to the properties that need to be saved the only solution or there are better ones?
EDIT:
I might not have explained my question that well. Here is the sample code for one of the ReadOnly properties, I have added UserName property for the Entity Framework:
Public Property UserName As String
Get
Return _userName
End Get
Private Set(value As String)
userName = value
End Set
Public ReadOnly Property Name As String Implements System.Security.Principal.IIdentity.Name
Get
Return UserName
End Get
End Property
What I wanted to ask is if there is any better way of doing it.
IIdentity properties are read only but the implementation can have setters. If you are using EDMX for mapping you don't have to expose these setters as public.
Edit:
This is possible in C# so hopefully you can use similar approach with VB.NET (I can only read VB code, not write):
public interface ITest {
string Name { get; }
}
public class Test : ITest {
public string Name { get; set; }
}
The class offers setter even the interface didn't define it.
The EF persists objects, not interfaces. Your object can have whatever properties you would like it to have. You cannot add an interface to your entity model, but you can add an object type which implements that interface.

Adding custom property to object returned from WCF RIA Services

I have a stored procedure in my Entity Framework Model. I've added a Function Import and mapped the results to a Complex Type.
I want to add an extra property to this Complex type, that I'll populate in my Domain Service, not coming back from the stored procedure. I added a myClass.shared.cs file and implemented added the property like so:
//myClass.shared.cs
public partial class myClass
{
public string myProperty {get;set;}
}
I populate this in my domain service when I return the object, e.g.:
public myClass GetMyClass(int myClassID)
{
myClass theClass= this.ObjectContext.StoredProc(myClassID).FirstOrDefault();
class.myProperty = 12345;
return theClass;
}
When I get the return values of this method on the client side theClass.myProperty is always null but all values from the stored procedure are populated, am I missing something?
I've tried decorating the myProperty with the [DataMember] attribute but this throws the error:
"The type 'myClass' already contains a
definition for 'myProperty'"
How can I get this to return the value set in the Domain Service to the client?
There was no need to put this in the shared.cs class. The shared.cs class copies the actual code over to the client side and is useful for adding methods etc. but to add a new property, all I had to do was add a partial class (NOT in myClass.shared.cs) and decorate it with DataMember.
public partial class myClass
{
[DataMember]
public string myProperty {get;set;}
}