Only return selected fields in Web API results - asp.net

First of all, this is not exactly a duplication of the dozens of other posts and I have tried all of them and none of them work.
I have a model that contains many more values than my web api consumers need.
public class Publication
{
[Key]
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
public DateTime SpaceDeadline { get; set; }
public DateTime MaterialsDeadline { get; set; }
public DateTime CreatedDt { get; set; }
public string CreatedBy { get; set; }
public DateTime UpdatedDt { get; set; }
public string UpdatedBy { get; set; }
}
I only want say a few of the fields to be passed in the API. I've tried this code but instead of leaving out say UpdateBy in the Json result, it returns it with a null value. How do I get rid of that? I've tried several dozen variations but they either fail to compile or fail to return results.
public IQueryable<Publication> GetPublications()
{
return db.Publications
.ToList()
.Select(p => new Publication {
PublicationID = p.PublicationID,
PublicationTitle = p.PublicationTitle,
Frequency = p.Frequency,
NextIssueDate = p.NextIssueDate
})
.AsQueryable();
}

Don't serialize your DAO. Create a complete contract and then serialize it selectively. To creating different contracts for different cases, you could simplify it using Json.Net; you could just create a custom contract resolver and use it as a parameter of SerializeObject() like so
static void Main(string[] args)
{
var person = new TestContract {FirstName = "John", LastName = "Doe", Age = 36};
var firstNameContract = new SelectiveSerializer("firstname");
var allPropertiesContract = new SelectiveSerializer("firstname, lastname, age");
var allJson = JsonConvert.SerializeObject(
person,
Formatting.Indented,
new JsonSerializerSettings {ContractResolver = allPropertiesContract});
var firstNameJson = JsonConvert.SerializeObject(
person,
Formatting.Indented,
new JsonSerializerSettings {ContractResolver = firstNameContract});
Console.WriteLine(allJson);
// {
// "FirstName": "John",
// "LastName": "Doe",
// "Age": 36
// }
Console.WriteLine(firstNameJson);
// {
// "FirstName": "John",
// }
}
public class SelectiveSerializer : DefaultContractResolver
{
private readonly string[] _fields;
public SelectiveSerializer(string fields)
{
var fieldColl = fields.Split(',');
_fields = fieldColl
.Select(f => f.ToLower().Trim())
.ToArray();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = o => _fields.Contains(member.Name.ToLower());
return property;
}
}
public class TestContract
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Without much effort, you could probably work this into your default mediatype formatter (in the pipeline) to look for a parameter in the request called 'fields' or whatever and then use the custom contract resolver if present, and then it would be seamless default behavior to limit fields if specified or serialize the entire object if not specified.
On the academic side, here is the justification:
Any modification to the data is considered a "view concern" which means, in an API, it should controlled by query parameters and accept header. In this case, the "representation" of the data is application/json and you've chose to "filter" the returned fields. All of this can (and should be, imo) be handled during serialization. So your "model" in this case will always be the full model vs. some subset of the model. The full model in this example contains first name, last name, and age. In reality, this could be hundreds of properties. If you want to allow the client to choose a subset of the complete model, this is how you could do it with selective serialization.
You can similar behaviors in graph apis. There, the default for large models is that you get an empty object if you don't specify fields, forcing the client to be very specific about what it asks for, which is great when payload size matters (e.g. mobile applications). And, there's nothing stopping from creating field presets like 'name' which could mean 'firstname, lastname' or 'all' which includes all properties.
I've never been a fan of having hundreds of data objects that all serve some ad hoc requirement for a data set that is used in 20 different contexts where some cases require more data while others require less. IMO if you have to go through the same process to get the data, whether it complete or not, you shouldn't waste your time creating additional objects to frame the data for the sake of the client, and this should help you achieve that.

It's because you're returning a collection of Publication objects so you will get every property that is contained in that class, whether you populate it or not. If you want to return a subset of the properties then create a class that has only the properties you want to return and create an instance of that class in your query.
public IQueryable<WhatIReallyWantToReturn> GetPublications()
{
return db.Publications
.ToList()
.Select(p => new WhatIReallyWantToReturn {
PublicationID = p.PublicationID,
PublicationTitle = p.PublicationTitle,
Frequency = p.Frequency,
NextIssueDate = p.NextIssueDate
})
.AsQueryable();
}
private class WhatIReallyWantToReturn
{
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
}

using Newtonsoft.Json;
public class Publication
{
[Key]
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
public DateTime SpaceDeadline { get; set; }
public DateTime MaterialsDeadline { get; set; }
[JsonIgnore]
public DateTime CreatedDt { get; set; }
[JsonIgnore]
public string CreatedBy { get; set; }
[JsonIgnore]
public DateTime UpdatedDt { get; set; }
[JsonIgnore]
public string UpdatedBy { get; set; }
}

as Craig W. said you can use viewmodel ,also you can use anonymous type
(notice viewmodel is better way because you can use some utilities like automapper for mapping your property automatically)

JsonIgnore annotation has worked for me
[JsonIgnore]
public int Ranking { get; set; }

Here is a great article (Dec 2019) on the subject. It offers a solution for data shaping by making use of ExpandoObject and Type Reflection. The properties that the client requires can then be passed through the request as a query parameter (i.e. separated by a comma). The article also offers solution to the JSON Serialization problem.
Startup.cs file:
services.AddControllers(config =>
{
config.RespectBrowserAcceptHeader = true;
config.ReturnHttpNotAcceptable = true;
})
.AddXmlDataContractSerializerFormatters()
.AddNewtonsoftJson();

+1 for Sinaesthetic's answer.
I just finished reading an article, about GraphQL which solves exactly this problem. You can define exactly which fields do you need in the same request. No need for creating new endpoints every single time, when the caller needs just a specific subset of the properties.
If you can do this in .NET WEB API too without creating new models and endpoints, with just a very little extra effort, why wouldn't you (instead of exchanging Web Api for GraphQL).
Actually his SelectiveSerializer could be upgarded with reflection, so if you want to define which props you need in
C#, you can do this by providing property expressions, so you don't have to worry about misstyping prop names.
I bet there are other solutions for this, but the basic concept is the most important that we can define which fields we need in our json without creating new models.

Related

Out-only field in JSON deserialization [duplicate]

We have some configuration files which were generated by serializing C# objects with Json.net.
We'd like to migrate one property of the serialised class away from being a simple enum property into a class property.
One easy way to do this, would be to leave the old enum property on the class, and arrange for Json.net to read this property when we load the config, but not to save it again when we next serialize the object. We'll deal with generating the new class from the old enum separately.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
There are actually several fairly simple approaches you can use to achieve the result you want.
Let's assume, for example, that you have your classes currently defined like this:
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
}
enum Fizz { Alpha, Beta, Gamma }
class Bang
{
public string Value { get; set; }
}
And you want to do this:
string json = #"{ ""ObsoleteSetting"" : ""Gamma"" }";
// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);
// migrate
config.ReplacementSetting =
new Bang { Value = config.ObsoleteSetting.ToString() };
// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);
To get this:
{"ReplacementSetting":{"Value":"Gamma"}}
Approach 1: Add a ShouldSerialize method
Json.NET has the ability to conditionally serialize properties by looking for corresponding ShouldSerialize methods in the class.
To use this feature, add a boolean ShouldSerializeBlah() method to your class where Blah is replaced with the name of the property that you do not want to serialize. Make the implementation of this method always return false.
class Config
{
public Fizz ObsoleteSetting { get; set; }
public Bang ReplacementSetting { get; set; }
public bool ShouldSerializeObsoleteSetting()
{
return false;
}
}
Note: if you like this approach but you don't want to muddy up the public interface of your class by introducing a ShouldSerialize method, you can use an IContractResolver to do the same thing programmatically. See Conditional Property Serialization in the documentation.
Approach 2: Manipulate the JSON with JObjects
Instead of using JsonConvert.SerializeObject to do the serialization, load the config object into a JObject, then simply remove the unwanted property from the JSON before writing it out. It's just a couple of extra lines of code.
JObject jo = JObject.FromObject(config);
// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();
json = jo.ToString();
Approach 3: Clever (ab)use of attributes
Apply a [JsonIgnore] attribute to the property that you do not want to be serialized.
Add an alternate, private property setter to the class with the same type as the original property. Make the implementation of that property set the original property.
Apply a [JsonProperty] attribute to the alternate setter, giving it the same JSON name as the original property.
Here is the revised Config class:
class Config
{
[JsonIgnore]
public Fizz ObsoleteSetting { get; set; }
[JsonProperty("ObsoleteSetting")]
private Fizz ObsoleteSettingAlternateSetter
{
// get is intentionally omitted here
set { ObsoleteSetting = value; }
}
public Bang ReplacementSetting { get; set; }
}
For any situation where it's acceptable to have your deserialization-only property be marked internal, there's a remarkably simple solution that doesn't depend on attributes at all. Simply mark the property as internal get, but public set:
public class JsonTest {
public string SomeProperty { internal get; set; }
}
This results in correct deserialization using default settings/resolvers/etc., but the property is stripped from serialized output.
I like sticking with attributes on this one, here is the method I use when needing to deserialize a property but not serialize it or vice versa.
STEP 1 - Create the custom attribute
public class JsonIgnoreSerializationAttribute : Attribute { }
STEP 2 - Create a custom Contract Reslover
class JsonPropertiesResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
//Return properties that do NOT have the JsonIgnoreSerializationAttribute
return objectType.GetProperties()
.Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
.ToList<MemberInfo>();
}
}
STEP 3 - Add attribute where serialization is not needed but deserialization is
[JsonIgnoreSerialization]
public string Prop1 { get; set; } //Will be skipped when serialized
[JsonIgnoreSerialization]
public string Prop2 { get; set; } //Also will be skipped when serialized
public string Prop3 { get; set; } //Will not be skipped when serialized
STEP 4 - Use it
var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });
Hope this helps! Also it's worth noting that this will also ignore the properties when Deserialization happens, when I am derserializing I just use the converter in the conventional way.
JsonConvert.DeserializeObject<MyType>(myString);
Use setter property:
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }
[JsonIgnore]
private string _ignoreOnSerializing;
[JsonIgnore]
public string IgnoreOnSerializing
{
get { return this._ignoreOnSerializing; }
set { this._ignoreOnSerializing = value; }
}
Hope this help.
After i spent a quite long time searching how to flag a class property to be De-Serializable and NOT Serializable i found that there's no such thing to do that at all; so i came up with a solution that combines two different libraries or serialization techniques (System.Runtime.Serialization.Json & Newtonsoft.Json) and it worked for me like the following:
flag all your class and sub-classes as "DataContract".
flag all the properties of your class and sub-classes as "DataMember".
flag all the properties of your class and sub-classes as "JsonProperty" except those you want them not to be serialized.
now flag the properties the you do NOT want it to be serialized as "JsonIgnore".
then Serialize using "Newtonsoft.Json.JsonConvert.SerializeObject" and De-Serialize using "System.Runtime.Serialization.Json.DataContractJsonSerializer".
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace LUM_Win.model
{
[DataContract]
public class User
{
public User() { }
public User(String JSONObject)
{
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
User user = (User)dataContractJsonSerializer.ReadObject(stream);
this.ID = user.ID;
this.Country = user.Country;
this.FirstName = user.FirstName;
this.LastName = user.LastName;
this.Nickname = user.Nickname;
this.PhoneNumber = user.PhoneNumber;
this.DisplayPicture = user.DisplayPicture;
this.IsRegistred = user.IsRegistred;
this.IsConfirmed = user.IsConfirmed;
this.VerificationCode = user.VerificationCode;
this.Meetings = user.Meetings;
}
[DataMember(Name = "_id")]
[JsonProperty(PropertyName = "_id")]
public String ID { get; set; }
[DataMember(Name = "country")]
[JsonProperty(PropertyName = "country")]
public String Country { get; set; }
[DataMember(Name = "firstname")]
[JsonProperty(PropertyName = "firstname")]
public String FirstName { get; set; }
[DataMember(Name = "lastname")]
[JsonProperty(PropertyName = "lastname")]
public String LastName { get; set; }
[DataMember(Name = "nickname")]
[JsonProperty(PropertyName = "nickname")]
public String Nickname { get; set; }
[DataMember(Name = "number")]
[JsonProperty(PropertyName = "number")]
public String PhoneNumber { get; set; }
[DataMember(Name = "thumbnail")]
[JsonProperty(PropertyName = "thumbnail")]
public String DisplayPicture { get; set; }
[DataMember(Name = "registered")]
[JsonProperty(PropertyName = "registered")]
public bool IsRegistred { get; set; }
[DataMember(Name = "confirmed")]
[JsonProperty(PropertyName = "confirmed")]
public bool IsConfirmed { get; set; }
[JsonIgnore]
[DataMember(Name = "verification_code")]
public String VerificationCode { get; set; }
[JsonIgnore]
[DataMember(Name = "meeting_ids")]
public List<Meeting> Meetings { get; set; }
public String toJSONString()
{
return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
}
}
}
Hope that helps ...
Depending on where in the application this takes place and if it's just one property, one manual way you can do this is by setting the property value to null and then on the model you can specify that the property be ignored if the value is null:
[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }
If you are working on an ASP.NET Core web app, you can globally set this for all properties in all models by setting this in your Startup.cs file:
public void ConfigureServices(IServiceCollection services) {
// other configuration here
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}
with reference to #ThoHo's solution, using the setter is actually all that is needed, with no additional tags.
For me I previously had a single reference Id, that I wanted to load and add to the new collection of reference Ids. By changing the definition of the reference Id to only contain a setter method, which added the value to the new collection. Json can't write the value back if the Property doesn't have a get; method.
// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }
// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }
This class is now backwards compatible with the previous version and only saves the RefIds for the new versions.
To build upon Tho Ho's answer, this can also be used for fields.
[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }
[JsonIgnore]
public string IgnoreOnSerializing;
If you use JsonConvert,IgnoreDataMemberAttribute is ok.My standard library not refrence Newton.Json,and I use [IgnoreDataMember] to control object serialize.
From Newton.net help document.
Is there any simple way to mark (e.g. with attributes) a property of a C# object, so that Json.net will ignore it ONLY when serializing, but attend to it when deserializing?
The easiest way I've found as of this writing is to include this logic in your IContractResolver.
Sample code from above link copied here for posterity:
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property if an employee is their own manager
return (Manager != this);
}
}
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
{
property.ShouldSerialize =
instance =>
{
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
All of the answers are good but this approach seemed like the cleanest way. I actually implemented this by looking for an attribute on the property for SkipSerialize and SkipDeserialize so you can just mark up any class you control. Great question!
Jraco11's answer is very neat. In case, if you want to use the same IContractResolver both for serialization and deserialization, then you can use the following:
public class JsonPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.IsDefined(typeof(JsonIgnoreSerializationAttribute)))
{
property.ShouldSerialize = instance => false;
}
return property;
}
}
thats will do the trick, create a property with set only
example 1:
https://dotnetfiddle.net/IxMXcG
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => IsPartialResult = (value != null && value.HasValues); }
public bool IsPartialResult { get; private set; }
example 2:
private JArray _disabledProtections;
[JsonProperty("disabled-protections")]
public JArray DisabledProtections { set => _disabledProtections = value; }
public bool IsPartialResult => _disabledProtections != null && _disabledProtections.HasValues;
Use [JsonIgnore] attribute in the public property of the model class.

Validation Annotation - Entity Framework Core

I have a model which is posted to my backend from a cshtml view. This model is also a representation of a Database-Table.
This code looks for the HTTP POST like the following:
[HttpPost]
public IActionResult CreateAKG(Conversation akg)
{
if (ModelState.IsValid && ValideD3OrD4Values(akg))
{
akg.RDPflichfelderBefuellt = true;
}
else
{
akg.RDPflichfelderBefuellt = false;
}
if (akg.Kommentare == null)
{
akg.Kommentare = new List<Kommentar>();
}
if (akg.AuftragsklaerungsgespraechId == 0)
{
this.MyDatabase.Conversation.Add(akg);
}
else
{
this.MyDatabase.Conversation.Update(akg);
}
this.MyDatabase.SaveChanges();
return RedirectToAction("Index");
}
The class which represents the model is called Conversation. There are some properties which are annotated by Validation Annotations. The annotation should only be used by the Controller / ModelState.IsValid and not for the Database-Table.
Here is the code sample:
public class Conversation
{
public int ConversationId { get; set; }
[Required(ErrorMessage = "This field is required.")]
public DateTime? DatumAKG { get; set; }
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungFertigungskosten { get; set; }
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungQVP { get; set; }
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungLayoutkosten { get; set; }
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungBeschaffung { get; set; }
public bool RDPflichfelderBefuellt { get; set; }
}
The ModelState.IsValid is only used to validate if a boolean is true or false. Other validation is not needed.
My problem now is, that in reason of the data-annotation a string which could normally be NULL in a Database is now in the database-design configured as NOT NULL.
If i try to store a new conversation in the database, a error is throw that some string values could not be null.
What i want to do is:
Validation on Controller
No Validation Annotation which changes the Database-Design
Since properties and requirements are different you should have 2 different objects
// this is you database object
public class Conversation {
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
public string KontierungQVP { get; set; }
}
// this your Data Transfer Object
public class ConversationDTO {
[MaxLength(256, ErrorMessage = "This field cannot be more than 256 characters")]
[Required]
public string KontierungQVP { get; set; }
}
Your EF models should always represent the database. If you want to add extra validations or conditions you should do it with another object and simply transfer between those objects. Having 2 different types of objects gives you more modularity.
EDIT:
There is a way to do what you ask but it's not a recommended approach and it might cause issues. You want to use 2 contexts and configure the required properties using FluentAPI and not data annotations.
// Call this method in your context.
protected override void OnModelCreating(your_builder){
modelBuilder.Entity<Conversation>()
.Property(p => p.KontierungQVP)
.IsRequired();
}
Generally you want to have 2 different contexts. One where you will initialize your database. And another one where you will have your required attributes defined by the FluentAPI (not data annotations).
So to recap one DbContext for your DB creation and one other for your operations. Of course this will lead to disparencies and it is easy to forget a validation on the database and so on.

Serializing a BsonArray with C# driver

Problem: I have a Mongo document that includes two arrays. One of the arrays is large, with subdocuments. This one serializes with no problem. Another is a simple array of this type:
staffgroups {"Tech","Sales"}
This one will not serialize. I get errors saying it's a BsonArray. The closest I've been able to get to serializing it produces a string. I need a JSON object.
Code time:
public class specialtyGroup
{
public ObjectId _id { get; set; }
public string name { get; set; }
public string location { get; set; }
public coachConfig config { get; set; }
public schedules[] coaches { get; set; }
public BsonArray staffgroups { get; set; }
}
And the webservice:
public void GetGroups()
{
var client = new MongoClient();
var db = client.GetDatabase("MongoTul");
var coll = db.GetCollection<specialtyGroup>("specialtyGroups");
string cname = HttpContext.Current.Request.Params["loc"];
var creatures = coll.Find(b => b.location == cname)
.ToListAsync()
.Result;
JavaScriptSerializer js = new JavaScriptSerializer();
Context.Response.Write(js.Serialize(creatures));
}
I've tried using aggregation and projecting. I've tried creating an additional class for staffgroups (which works for my complex array). And a few other things. All no good.
Most common error looks like this: Unable to cast object of type 'MongoDB.Bson.BsonString' to type 'MongoDB.Bson.BsonBoolean'.
I spent hours on this before posting here, then after posting I figured it out in 30 mins. Sorry.
The answer is staffgroups should be "public string[] staffgroups {get; set;}
So if any other rubes like me have that question, there's the answer.

ASP.NET MVC: How to Insert Data Into Multiple Tables?

I have two tables (NPG_Chemical and NPG_Chemical_Synonym):
public partial class NPG_Chemical
{
[Key]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_ID { get; set; }
[StringLength(256)]
public string Chemical { get; set; }
}
public partial class NPG_Chemical_Synonym
{
[Key]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_Synonym_ID { get; set; }
[ForeignKey("NPG_Chemical_ID")]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_ID { get; set; }
[StringLength(512)]
public string Synonym { get; set; }
}
In the NPG_ChemicalController I have something like:
[HttpPost]
public ActionResult Create(NPG_ChemicalViewModel model)
{
using (var context = new NPG_Model())
{
var chemical = new NPG_Chemical();
chemical.Chemical = model.NPG_Chemical.Chemical;
context.NPG_Chemical.Add(chemical);
var synonym = new NPG_Chemical_Synonym();
synonym.Synonym = model.NPG_Chemical_Synonym.Synonym;
synonym.NPG_Chemical_ID = chemical.NPG_Chemical_ID;
context.NPG_Chemical_Synonym.Add(synonym);
context.SaveChanges();
}
return View();
}
and NPG_ChemicalViewModel:
namespace NPG_Administrative_Utility.Models
{
public class NPG_ChemicalViewModel
{
public NPG_ChemicalViewModel()
{
NPG_Chemical = new NPG_Chemical();
NPG_Chemical_Synonym = new NPG_Chemical_Synonym();
}
public NPG_Chemical NPG_Chemical { get; set; }
public NPG_Chemical_Synonym NPG_Chemical_Synonym { get; set; }
}
}
When I try to create a view based on NPG_ChemicalViewModel, it shows:
Can any one help me on this?
You'll need a view model. At the simplest, you can just do something like:
public class NPG_ChemicalViewModel
{
public NPG_ChemicalViewModel()
{
NPG_Chemical = new NPG_Chemical();
NPG_Chemical_Synonym = new NPG_Chemical_Synonym();
}
public NPG_Chemical NPG_Chemical { get; set; }
public NPG_Chemical_Synonym NPG_Chemical_Synonym { get; set; }
}
Then, change your action to accept this:
public ActionResult Create(NPG_ChemicalViewModel model)
In your view, you would generate the individual properties like:
#Html.EditorFor(m => m.NPG_Chemical.Chemical)
However, it's far better to only include the properties on your view model that you want to be edited:
public class ChemicalViewModel
{
public string Chemical { get; set; }
public string Synonym { get; set; }
}
Then, in your action, you just map this posted data where it should go:
var chemical = new NPG_Chemical();
chemical.Chemical = model.Chemical;
context.NPG_Chemical.Add(chemical);
var synonym = new NPG_Chemical_Synonym();
synonym.Synonym = model.Synonym;
synonym.NPG_Chemical_ID = chemical.NPG_Chemical_ID;
context.NPG_Chemical_Synonym.Add(synonym);
That said, there's some significant issues with your code here. First, it looks like you're dealing with a one-to-one or one-to-many relationship here between Chemical and Synonym, but right now, you have no foreign keys being utilized. You should add a navigation property to your synonym class:
[ForeignKey("NPG_Chemical_ID")]
public NPG_Chemical Chemical { get; set; }
That tells Entity Framework that you have a relationship and among other things allows it to automatically fill in IDs as necessary. For example, with that, you could now simply do:
synonym.Chemical = chemical;
Instead of directly referencing the ID. That way, if the id is autogenerated or otherwise unknown before saving, the relationship will still be preserved. Whereas, without it, you'd have to save chemical first, set the autogenerated id on synonym and then save the synonym in a separate transaction.
Second, if you're going to use keys typed as "numeric". Then, you're going to be responsible for generating a unique numeric string for each record. That's a huge pain, as it's going to require checking a proposed id against other existing record ids before actually saving. Otherwise, you run the risk of a primary key collision. It's far better to use a standard autoincrementing PK or barring that, at least a GUID, where you're assured a reasonably low risk of collisions occurring.
Third, you should absolute not use using with your context. Here it's not a big deal, since, you're only saving and not reading data from the database, but in a typical view, lazy-loading will kick you in the posterior quick doing that. Your context should be request-scoped, either as an instance variable on your controller (since the controller is newed up and disposed with each request) or using dependency injection. You never want to create an instance of your context anywhere else, including an action method.

Web API error failed to serialize the response body

Im fairly new to ASP.NET MCV 4 as well as Mongo DB and trying to build web API.
I thought I had finally got it right but when I start the app and enter: http://localhost:50491/api/document into my browser I get this error message
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
Here is my code
This is the Document Class
public class Document
{
[BsonId]
public ObjectId DocumentID { get; set; }
public IList<string> allDocs { get; set; }
}
This is where the Connection to the DB is made:
public class MongoConnectionHelper
{
public MongoCollection<BsonDocument> collection { get; private set; }
public MongoConnectionHelper()
{
string connectionString = "mongodb://127.0.0.1";
var server = MongoServer.Create(connectionString);
if (server.State == MongoServerState.Disconnected)
{
server.Connect();
}
var conn = server.GetDatabase("cord");
collection = conn.GetCollection("Mappings");
}
Here is the ApiController Class:
public class DocumentController : ApiController
{
public readonly MongoConnectionHelper docs;
public DocumentController()
{
docs = new MongoConnectionHelper();
}
public IList<BsonDocument> getAllDocs()
{
var alldocs = (docs.collection.FindAll().ToList());
return alldocs;
}
}
I read futher on and the error message suggested:
Type 'MongoDB.Bson.BsonObjectId' with data contract name 'BsonObjectId:http://schemas.datacontract.org/2004/07/MongoDB.Bson' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
That is all good and well but how do I do that?
Either a) don't serialize your document classes over Web API, and create some DTOs meant to be serialized, or b) use something else as ID.
If you want an easy auto-generated ID, and you're OK with the fact that it will consume slightly more space, you can resort to the following "hack":
public class Document
{
public Document()
{
Id = ObjectId.GenerateNewId().ToString();
}
public string Id { get; set; }
}
This way, you'll get MongoIDs, but they'll be stored as a string.
If you need Web API2 responce in XML format , you need to handle the default Id like below
eg: ObjectId("507f191e810c19729de860ea")
Either you need to remove the Id from serialization.
[DataContract]
public class Document
{
[BsonId]
public string Id { get; set; }
[DataMember]
public string Title { get; set; } //other properties you use
}
Or You can change the Type of ID with custom logic
public class GuidIdGenerator : IIdGenerator
{
public object GenerateId(object container, object document)
{
return Guid.NewGuid();
}
public bool IsEmpty(object id)
{
return string.IsNullOrEmpty(id.ToString());
}
}
public class Document
{
[BsonId(IdGenerator = typeof(GuidIdGenerator))]
public string Id { get; set; }
public string Title { get; set; } //other properties you use
}

Resources