Does Order Matter in the XML Read by the DataContractSerializer? - wcf

I have the following code:
[DataContract(Namespace = "")]
public class User
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public string FullName { get; set; }
}
//Deserialization test
public void Test()
{
//CASE 1.
//string xml = "<User><UserName>john</UserName>" +
// "<FullName>John Lennon</FullName></User>";
//CASE 2.
string xml = "<User><FullName>John Lennon</FullName>" +
"<UserName>john</UserName></User>";
byte[] byteArray = Encoding.UTF8.GetBytes(xml);
User user = null;
using (MemoryStream stream = new MemoryStream(byteArray))
{
DataContractSerializer serializer =
new DataContractSerializer(typeof(User), "User", "");
user = (User)serializer.ReadObject(stream);
}
}
In case 1, FullName property isn't deserialized, but in case 2 it is deserialized properly. Why?

Because order is significant. Alphabetic order is used unless you specify the order in your DataMember attributes.
This is explained in this MSDN article.
In general, it's a good practice to always explicitly specify Order on your DataMember attributes:
[DataMember(IsRequired=true, Order=0)]
public string FullName { get; set; }
[DataMember(IsRequired=true, Order=1)]
public string UserName { get; set; }

Related

ASP.NET model that requires a reference to a IdentityUser?

I have a model Blueprint that requires a reference to an IdentityUser. When I run Add-Migration CreateBlueprintSchema the following error is thrown:
No suitable constructor was found for entity type 'Blueprint'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'author' in 'Blueprint(IdentityUser author, string name, string data)'.
How do I resolve this issue?
Blueprint.cs
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using System.Text;
namespace FactorioStudio.Models
{
public class Blueprint
{
[MaxLength(40)]
public string Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Data { get; set; }
[Required]
public IdentityUser Author { get; set; }
[Required]
public DateTime CreationDate { get; set; }
public Blueprint? ParentBlueprint { get; set; }
public Blueprint(IdentityUser author, string name, string data)
{
Author = author;
Name = name;
Data = data;
CreationDate = DateTime.UtcNow;
string hashSource = Author.Id +
Name +
CreationDate.ToString("s", System.Globalization.CultureInfo.InvariantCulture) +
Data;
using SHA1 sha1 = SHA1.Create();
byte[] hashBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(hashSource));
string hash = BitConverter.ToString(hashBytes).Replace("-", String.Empty).ToLower();
Id = hash;
}
}
}
change your model like this:
public class Blueprint
{
[MaxLength(40)]
public string Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Data { get; set; }
[Required]
public IdentityUser Author { get; set; } = new IdentityUser();
//.......
}
or just add a non-parameter constructor in your model.
You can refer to this;
Edit=======================
For testing convenience, I manually added a foreign key(If I don't add Fk by myself, EF core will create shadow foreign key property, But i can't use it directly), Then I create a Blueprint without referring to an existing IdentityUser, You can see it will report a SqlException that show error in FK.
As I wrote in the comment, EF Core cannot set navigation properties using a constructor. Instead, use a custom value generator to create the hash for the ID when saving the entity.
public class BlueprintHashGenerator : ValueGenerator<string>
{
public override bool GeneratesTemporaryValues => false;
public override string Next(EntityEntry entry)
{
if (entry.Entity is not Blueprint bp)
{
throw new ApplicationException("Unexpected entity");
}
string hashSource = bp.Author.Id +
bp.Name +
bp.CreationDate.ToString("s", System.Globalization.CultureInfo.InvariantCulture) +
bp.Data;
using SHA1 sha1 = SHA1.Create();
byte[] hashBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(hashSource));
return BitConverter.ToString(hashBytes).Replace("-", String.Empty).ToLower();
}
}
then in model builder
builder.Entity<Blueprint>().Property(bp => bp.Id).HasValueGenerator<BlueprintHashGenerator>().ValueGeneratedNever();
It will generate the value on SaveChanges, so ensure all properties are set before calling save (Author, Name, CreationDate, Data).

How to solve may not be abstract and must be include a default constructor?

I have a database [![as this picture][1]][1]
I want to get a chart from this database and when I tried to use SQL query on the controller side, I got an error like this ;
[![this is error type][2]][2]
This is my controller code ;
public ActionResult Index2(String makineadi, DateTime? startdate, DateTime? enddate)
{
var data = entities.Database.SqlQuery<DataPoint>("SELECT Sıcaklık, Recete_Sure From Recete Where Machine_IP ='" + makineadi + "' and Tarih between'"+startdate+"' and '"+enddate+"'").ToList();
ViewBag.DataPoints = JsonConvert.SerializeObject(data);
return View();
}
And here is my class definition for getting the chart to JSON serialization;
[DataContract]
public class DataPoint
{
public DataPoint(int Sıcaklık, int Recete_Sure)
{
this.Sıcaklık = Sıcaklık;
this.Recete_Sure = Recete_Sure;
}
//Explicitly setting the name to be used while serializing to JSON.
[DataMember(Name = "Sıcaklık")]
public Nullable<int> Sıcaklık { get; set; }
//Explicitly setting the name to be used while serializing to JSON.
[DataMember(Name = "Recete_Sure")]
public Nullable<int> Recete_Sure { get; set; }
}
What should I do to fix it?
[1]: https://i.stack.imgur.com/ObwWR.png
[2]: https://i.stack.imgur.com/zQNgO.png
Add default constructor in class.
[DataContract]
public class DataPoint
{
public DataPoint()
{
}
public DataPoint(int Sıcaklık, int Recete_Sure)
{
this.Sıcaklık = Sıcaklık;
this.Recete_Sure = Recete_Sure;
}
//Explicitly setting the name to be used while serializing to JSON.
[DataMember(Name = "Sıcaklık")]
public Nullable<int> Sıcaklık { get; set; }
//Explicitly setting the name to be used while serializing to JSON.
[DataMember(Name = "Recete_Sure")]
public Nullable<int> Recete_Sure { get; set; }
}
It will resolve issue

Using ReadAsAsync<T>() to deserialize complex Json object

I want to use ReadAsAsync() in my mvc project with .net 4.0. The result comes as null.
If I enter the uri to address bar, the result in chrome as(tag names are changed):
<ns2:MyListResponse xmlns:ns2="blablabla">
<customerSessionId>xxcustomerSessionIdxx</customerSessionId>
<numberOfRecordsRequested>0</numberOfRecordsRequested>
<moreResultsAvailable>false</moreResultsAvailable>
<MyList size="1" activePropertyCount="1">
<MySummary order="0">
<id>1234</id>
<name>...</name>
.
.
</MySummary>
</MyList>
</ns2:MyListResponse>
If I use the statement in code :
using (var client = new HttpClient())
{
var response = client.GetAsync(apiUri).Result;
var message = response.Content.ReadAsStringAsync().Result;
var result1 = JsonConvert.DeserializeObject<MyListResponse>(message);
var result2 = response.Content.ReadAsAsync<MyListResponse>().Result;
}
the message comes in string format as "{\"MyListResponse\":{\"customerSessionId\"...}" which corresponds to a json object as:
{"MyListResponse":
{"customerSessionId":"xxcustomerSessionIdxx",
"numberOfRecordsRequested":0,
"moreResultsAvailable":false,
"MyList":
{"#size":"1",
"#activePropertyCount":"1",
"MySummary":
{"#order":"0",
"id":1234,
"name":"...",
.
.
}
}
}
}
and the properties of result1 and result2 came as null or default values. Class definitions are below. I want to read the content as an object but I couldn't. What do you advice to solve this? What am I doing wrong? Thanks in advance.
public class MySummary
{
public int #Order { get; set; }
public string Id { get; set; }
public string Name { get; set; }
.
.
}
public class MyList
{
public int #Size { get; set; }
public int #ActivePropertyCount { get; set; }
public MySummary MySummary{ get; set; }
}
public class MyListResponse
{
public string CustomerSessionId { get; set; }
public int NumberOfRecordsRequested { get; set; }
public bool MoreResultsAvailable { get; set; }
public MyList MyList { get; set; }
}
I defined a new class as:
public class ResponseWrapper
{
public MyListResponse MyListResponse { get; set; }
}
then I used this wrapper with,
var result1 = JsonConvert.DeserializeObject<ResponseWrapper>(message);
var result2 = response.Content.ReadAsAsync<ResponseWrapper>().Result;
then it worked. I need only MySummary object but I should write more classes to make it work.
After reading your solution I came up with one that doesn't need an extra class:
private static async Task<U> Execute<U>(HttpClient client, string path)
{
U output = default(U);
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
var jsonAsString = await response.Content.ReadAsStringAsync();
output = JsonConvert.DeserializeObject<U>(jsonAsString);
}
else
{
throw new ApplicationException(string.Format("Response message is not OK. Issues in action: {0}", path));
}
return output;
}
For the sake of future readers, I think the correct approach is using ReadAsAsync overload that takes IEnumerable<MediaTypeFormatter> and provide a formatter with the same settings used on the server for serialization. That should fix it.
It is possible to use at client ReadAsAsync with MyListResponse directly (in consequence without ResponseWrapper). To do this, you can define "BodyStyle = WebMessageBodyStyle.Bare" in the operation contract of "apiuri" in stead of "BodyStyle = WebMessageBodyStyle.Wrapped" (server side, i.e. service contract).

Add Identity key to Sub Class in ravenDB

I have this two class :
public class BlogPost
{
public string Id { get; set; }
public string Title { get; set; }
public string Category { get; set; }
public string Content { get; set; }
public DateTime PublishedAt { get; set; }
public string[] Tags { get; set; }
public BlogComment[] Comments { get; set; }
}
public class BlogComment
{
public string Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
and add to documents like this :
// Creating a new instance of the BlogPost class
BlogPost post = new BlogPost()
{
Title = "Hello RavenDB",
Category = "RavenDB",
Content = "This is a blog about RavenDB",
Comments = new BlogComment[]
{
new BlogComment() {Title = "Unrealistic", Content = "This example is unrealistic"},
new BlogComment() {Title = "Nice", Content = "This example is nice"}
}
};
is there a way that my comments have Identity key like my BlogPost class?
and another question:
is there a way that get comment object without using post. something like this :
using( var session = doc.OpenSession() )
{
return session.Load<BlogComment>( ID );
}
or
using( var session = doc.OpenSession() )
{
return ( from comment in session.Query<BlogComment>()
where comment.Title == title
select comment ).FirstOrDefault();
}
You can just have an integer property on BlogPost, increment that and set that value whenever you add a new comment. That would give you identity style ids within the scope of the post.

Serialize Partial DataContract

I have a DataContract that looks like:
[DataContract(Name = User.Root, Namespace = "")]
public class RegisterUser
{
[DataMember(Name = User.EmailAddress)]
public string EmailAddress { get; set; }
[DataMember(Name = User.UserName)]
public string UserName { get; set; }
[DataMember(Name = User.Password)]
public string Password { get; set; }
[DataMember(Name = User.FirstName)]
public string FirstName { get; set; }
[DataMember(Name = User.LastName)]
public string LastName { get; set; }
[DataMember(Name = User.PhoneNumber)]
public string PhoneNumber { get; set; }
[DataMember(Name = "RequestMessage")]
public string RequestMsg { get; set; }
}
And I would like to get the elements out of it. So instead of
<ROOT> <Element1/>...</ROOT>. I would just like to get <Element1/> (for partial xsd validation).
I thought I could use this function:
public static string Serialize<T>(T obj)
{
DataContractSerializer ser = new DataContractSerializer(obj.GetType());
String text;
using (MemoryStream memoryStream = new MemoryStream())
{
ser.WriteObject(memoryStream, obj);
byte[] data = new byte[memoryStream.Length];
Array.Copy(memoryStream.GetBuffer(), data, data.Length);
text = Encoding.UTF8.GetString(data);
}
return text;
}
and just pass it
string str = Serialize(test.EmailAddress);
That works great but the xml looks like:
"<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/\">myemail.com</string>"
I lost the DataMember info. How can I retain that as well?
Use WriteObjectContent instead of WriteObject:
http://msdn.microsoft.com/en-us/library/ms573853.aspx