Reputation: 4421
I'm lazy loading my table data to a JSON result and wending them to front end application. but when I get those data I notice that there are unnecessary elements, empty elements present in that response. And because of that my PUT
or update operation do not work for these inner JSON properties.
{
"image":null,
"paragraph":null,
"question":{
"grid":null,
"elements":[
],
"offeredAnswers":[
],
"lazyLoader":{
},
"id":"1",
"text":"How can we serve you better?",
"type":"textarea",
"questionRequired":false,
"pageFlowModifier":false,
"gridId":null,
"min":null,
"max":null
},
"lazyLoader":{
}
}
If I change the value of text
, it will not get updated, but if i change paragraph
then it will be changed in the database.
Here there's a new property called lazyLoader
, I need to get rid of that too. and elements
, offeredAnswers
are really not needed since they are empty. I achived lazy loading by adding virtual
keyword to referenced classes.
public partial class Questions
{
public Questions()
{
Elements = new HashSet<Elements>();
OfferedAnswers = new HashSet<OfferedAnswers>();
}
public string Id { get; set; }
public string Text { get; set; }
public string Type { get; set; }
public bool QuestionRequired { get; set; }
public bool PageFlowModifier { get; set; }
public int? GridId { get; set; }
public long? Min { get; set; }
public long? Max { get; set; }
public virtual Grids Grid { get; set; }
public virtual ICollection<Elements> Elements { get; set; }
public virtual ICollection<OfferedAnswers> OfferedAnswers { get; set; }
}
And I have this line in the Startup.cs
file to stop reference loop handling because Without that the POST
operation do not work since the JSON
object I'm posting is quite complex and has reference loops inside of it.
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
And I have enabled Lazy Loading Proxies.
services.AddDbContext<RDS_Context>
(options => options.UseLazyLoadingProxies().UseSqlServer(connection));
Upvotes: 2
Views: 1751
Reputation: 41
Based on a suggestion at EFCore repos and another SO answer it referenced , I'll leave this alternative approach for those who just want to get rid of the lazyLoader attribute
Create a CustomContractResolver.cs
public class CustomContractResolver : DefaultContractResolver
{
public static CustomContractResolver Instance { get; } = new CustomContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.Name == "LazyLoader")
{
property.Ignored = true;
}
return property;
}
}
And modify your Startup.cs, ConfigureServices
method to use your ContractResolver
services
.AddMvc(...)
.AddJsonOptions(options => {
options.SerializerSettings.ContractResolver = CustomContractResolver.Instance;
});
Upvotes: 1
Reputation: 3538
In Entity Framework, if you have an object where one or more of its properties use lazy loading, check its runtime type name using GetType().Name. For an object of a Car class, for example, you will notice that the runtime type is actually something called CarProxy, which is a temporary in-memory type created by Entity Framework using reflection. This "fake" proxy class's base type is Car, and has all the original Car properties, but includes an extra one called LazyLoader for properties that may need it.
If you do further checking on this "fake" CarProxy type, you will also see that Assembly.IsDynamic = true, which is indicative that the class was created dynamically using reflection (see documentation):
var TheCar = DBContext.Cars.Find(1);
Console.WriteLine(TheCar.GetType().Assembly.IsDynamic.ToString()); //will echo "true"
Luckily, Newtonsoft.Json has an override on the JsonConvert.SerializeObject() method that allows us to provide a base type, so that the resulting JSON doesn't contain properties that don't exist in that type. So, to eliminate the LazyLoader property, just provide the object's BaseType as the type parameter:
var TheCar = DBContext.Cars.Find(1);
var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheCar, TheCar.GetType().BaseType);
To make sure you don't get any circular reference loops when serializing (a very high probability when using lazy loading), call the serializer with the following setting:
var TheCar = DBContext.Cars.Find(1);
var Settings = new Newtonsoft.Json.JsonSerializerSettings
{
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheCar, TheCar.GetType().BaseType, Settings);
Note: This may only work on the first level deep when the serializer travels through the object. If there are yet more lazy-loading child properties of the object you provide to the serializer, the "LazyLoader" property may appear again. I haven't tested it so I can't say for sure.
Upvotes: 1
Reputation: 9771
You can remove your empty Object
and Array
from json by iterating JProperty
of JObject
like
class Program
{
static void Main(string[] args)
{
string json = File.ReadAllText(@"C:\Users\xxx\source\repos\ConsoleApp4\ConsoleApp4\Files\json6.json");
JObject data = JObject.Parse(json);
//Getting all those children that have value are empty from outer json
var result1 = data.Children<JProperty>()
.Where(x => (x.Value.Type == JTokenType.Object) && !x.Value.HasValues)
.ToList();
//Getting all those children that have value are empty from "question" object
var result2 = data["question"].Children<JProperty>()
.Where(x => (x.Value.Type == JTokenType.Object && !x.Value.HasValues) || (x.Value.Type == JTokenType.Array && !x.Value.HasValues))
.ToList();
//Remove all above empty object or arrays
result1.ForEach(x => x.Remove());
result2.ForEach(x => x.Remove());
var obj = data.ToObject<JObject>();
Console.WriteLine(obj);
Console.ReadLine();
}
}
Output:
Note: If you want to remove only empty lazyLoader
object throughout the json then use below lines in above code.
//Getting "lazyLoader" children that have value are empty from outer json
var result1 = data.Children<JProperty>()
.Where(x => x.Value.Type == JTokenType.Object && !x.Value.HasValues && x.Name == "lazyLoader")
.ToList();
//Getting "lazyLoader" children that have value are empty from "question" object
var result2 = data["question"].Children<JProperty>()
.Where(x => (x.Value.Type == JTokenType.Object && !x.Value.HasValues && x.Name == "lazyLoader"))
.ToList();
Output:
Upvotes: 1
Reputation: 2619
You could separate you database entity model from the model you would like to show in your front end. Map the properties of the entity model you need and want to include into your new front end model.
A quicker way of doing without implementing a new model can be by using anonymous objects, like:
myDbContext.MyObjects.Select(x => new { Prop1 = x.Property1, Prop2 = x.Property2 });
If you really have the LazyLoader property in your entity class (eg. injecting ILazyLoader as described here), you could instead decorate it with the JsonIgnore attribtue.
Upvotes: 3