Code First Data Annotations for Backpointer Properties - ef-code-first

I'm using Code First. I've got a Topic class and a Vote Class. Topics have votes.
public class Topic
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Vote> Votes { get; set; }
}
Vote class points back to Topic so I can get the topic of a given Vote in code.
public class Vote
{
public Guid Id { get; set; }
public bool IsUp { get; set; }
public DateTime WhenVoted { get; set; }
public Topic Topic { get; set; }
}
When I query like var topics = context.Topics.Include("Votes");, I get a stack overflow, but if I remove the reference to Topic from Vote, it loads fine. I believe there's a data annotation I can add to indicate that these represent the same relationship. Can someone point me in the right direction if that's the case?
The call stack has this over and over again which just seems to indicate that the serializer is trying to recursively render the recursive relationship. So maybe telling the Vote object not to serialize the Topic property is the right answer here, but if there is a data annotation answer, I'd like to know what it is.
System.Runtime.Serialization.dll!System.Runtime.Serialization.ClassDataContract.WriteXmlValue(System.Runtime.Serialization.XmlWriterDelegator xmlWriter, object obj, System.Runtime.Serialization.XmlObjectSerializerWriteContext context) Unknown
System.Runtime.Serialization.dll!System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(System.Runtime.Serialization.DataContract dataContract, System.Runtime.Serialization.XmlWriterDelegator xmlWriter, object obj, System.RuntimeTypeHandle declaredTypeHandle) Unknown
System.Runtime.Serialization.dll!System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(System.Runtime.Serialization.DataContract dataContract, System.Runtime.Serialization.XmlWriterDelegator xmlWriter, object obj, System.RuntimeTypeHandle declaredTypeHandle) Unknown
System.Runtime.Serialization.dll!System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(System.Runtime.Serialization.XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, System.RuntimeTypeHandle declaredTypeHandle) Unknown
System.Runtime.Serialization.dll!System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(System.Runtime.Serialization.XmlWriterDelegator xmlWriter, object obj, bool isDeclaredType, bool writeXsiType, int declaredTypeID, System.RuntimeTypeHandle declaredTypeHandle) Unknown

This was a badly worded question in that I assumed falsely that my problem could be fixed with data annotations. The answer to my actual question is to use the InverseProperty data annotation to create back pointer references (A has a collection of B, B has a parent property of A). This however didn't solve the problem since I was serializing a full object model that had the circular references in the code sample. To get around that, I went down the path of setting up my model to enforce proper serialization with the DataContract, DataMember, and ReferenceType attributes to end the circular reference, and while this worked, the fact that I had to do it was a clear indication I was serializing something not meant for serialization. In instead created a model specifically for serialization, mapped my domain to that, and serialized that instead with the default json/xml serializer and everything worked out just hunky dory.

Related

Converting Entity Framework object to JSON (without object graph)

Suppose I have a object structure like this
Library 1 ---- 1+ Book 1 ---- 1+ Page
I want to serialize a json object of a book with an array of page objects.
Using JSON.net serializer, I can get this to serialize without getting a circular reference, but the JSON still includes all of the properties of the book in each page, which includes data about the library...which can have data on other books which is a ton of noise.
From the answer from this question - Serialize Entity Framework objects into JSON, I know that I can do generics, but is this really the only way? This just seems like a ton of extra work. Especially if for a Json result that is Book with and array of page objects in it.
I am using Entity Framework 4.3.1 and Json.net 4.0.30319...
You should look at the serialization attributes.
[JsonObject(MemberSerialization.OptIn)]
public class Page
{
[JsonProperty]
public string Text { get; set; }
// not serialized because mode is opt-in
public Book Book { get; set; }
}
Original answer
The aforementioned way should be prefered in most of the cases, but there are some where it is not enough.
There are two ways of doing it.
You can implement a JsonConverter, and override the WriteJson method to write only the properties you want.
class BookConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Book);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value.GetType() == typeof(T2))
{
JObject obj = new JObject();
Book b = value as Book;
obj["titre"] = b.Name;
obj["pages"] = b.Pages;
// This line can also be
// obj.WriteTo(writer, this);
// if you also need change the way pages are serialized.
obj.WriteTo(writer, null);
}
else
{
throw new NotImplementedException();
}
}
}
You can call it like that :
string result = JsonConvert.SerializeObject(
book,
new JsonSerializerSettings
{
Converters = new JsonConverter[] { new BookConverter() }
});
You can also create a JsonBook class, and serialize it.
class JsonBook{
public JsonBook(Book b){/*...*/}
public List<Pages> l;
public string title;
// No reference to Library.
}

Map all properties of a class using reflection

I have two domain classes
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string HouseName { get; set; }
public string StreetName { get; set; }
public string PinCode { get; set; }
}
I want to map object of Employee class to another class.
I am using reflection to map empData object to another object. The code i used is
private void GetValues(object empData)
{
System.Type type = empData.GetType();
foreach (PropertyInfo pInfo in type.GetProperties())
{
//do some stuff using this pInfo.
}
}
I could easily map all the properties except the Address property in the emp object which is an object of another class.
So how can i map all the properties irrespective of its type ? i.e, if address contains object of another class it should also get mapped.
Can't you use AutoMapper for mapping classes?
You can know the type of property you are mapping by
if (propertyInfo.PropertyType == typeof(Address))
{ // do now get all properties of this object and map them}
Assuming that you want to be able to do this on any type of object and not just this specific one, you should use some sort of recursive solution. However if it's just for this object - why are you even using reflection? To me it just adds unnecessary complexity to something as simple as mapping six properties to another set of objects.
If you want to get more concrete help with code examples, you'll have to give us some more context. Why does a method named "GetValues" has a return type of void? I have a hard time coding up an example with that in mind. :)

Error when serializing EF Code First 5.0 data in WebAPI Controller

I had originally asked this question:
How Do I Resolve "A specified Include path is not valid"? which was answered, and my .Include() is now working, however, when the serializer tries to work it's magic, I get the following error:
You must write an attribute 'type'='object' after writing the attribute
with local name '__type'.
Here's what I'm doing to return the data:
var everything = dc.Categories
.Include(c => c.Products);
My class definitions are fairly straightforward:
public class Category
{
public int CategoryId { get; set; }
public string Title { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Title { get; set; }
public virtual Category Category { get; set; }
}
public class ProductDataContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
I also tried removing 'virtual' but then I get circular references. I tried making the setter on ICollection Products private (as suggested here: http://forums.asp.net/t/1773164.aspx/1), which gets the error to clear, but then my products aren't part of the returned JSON.
What do I need to do to get the data to serialize with the categories and their products within?
EDIT
Here was the stack trace I was getting:
[SerializationException: Object graph for type 'System.Collections.Generic.List`1[[Test.Models.Product, Test.Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' contains cycles and cannot be serialized if reference tracking is disabled.]
System.Web.Http.WebHost.HttpControllerHandler.EndProcessRequest(IAsyncResult result) +30206
System.Web.Http.WebHost.HttpControllerHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +10
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9478661
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +178
In order to fix this I needed to:
Disable lazy loading, and
Use the IgnoreDataMember from System.Runtime.Serialization as an attribute on the Category navigation property (the back-reference on the Product class).
Hope this helps someone.
To get around the XML-ish errors I used help from here:
http://www.strathweb.com/2012/03/serializing-entity-framework-objects-to-json-in-asp-net-web-api/
To get around the problem with the cyclic references, I used this as a guide:
MVC 4, Upshot entities cyclic references

How to delete child entities in an InverseProperty relationship using code first entity framework 4.1

I have a model and I am having trouble managing the relationships using entity framework 4.1
Here the model (simplified):
public class UserConfig {
[Key]
[Column("id", TypeName = "bigint")]
public long Id { get; set;}
[InverseProperty("UserConfigId")]
public virtual List<ColumnConfig> ColumnConfigs { get; set; }
}
public class ColumnConfig {
[Key]
[Column("id", TypeName = "bigint")]
public long Id { get; set; }
[Column("user_config_id", TypeName = "bigint")]
public long UserConfigId { get; set; }
[Column("width", TypeName = "int")]
public int Width{ get; set; }
[Column("col_name", TypeName = "varchar")]
public string ColumnName{ get; set; }
}
The model represents a user and a custom view of a table of data within a UI. They resize the columns the way they want and then save their settings. I have a webservice that accepts a list of the columns they want and their respective widths.
The problem I am having is updating the user's ColumnConfigs within my web service. The webservice does not receive the ColumnConfig id's, so my approach has been to try and first delete all the existing ColumnConfigs for the user, and then second create a new set of objects according to the new values passed in.
I can't manage to delete any of the ColumnConfig objects. Here's my code:
public void UpdateUserConfig(UserConfig uc) {
UserConfig origUserConf = ctx.ColumnConfigs.Find(new object[] {uc.Id});
origUserConf.ForEach(uc => ctx.ColumnConfigs.Remove(uc)); // remove old
origUserConf.ColumnConfigs = uc.ColumnConfigs; // add new
ctx.SaveChanges();
}
This isn't working, it gives the error:
System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
I'm not sure why is thinks there is a null constraint here. I haven't specified any fields as being required.
The easiest way to remove child relationships would be to invoke:
origUserConfig.ColumnConfigs.Clear();
Unfortunately, this wouldn't work for you for two reasons: (1) You have a non-nullable relationship defined between your Parent and Child entity, and (2) even if the relationship was defined as nullable it would orphan the child rather than deleting it.
Read this article (Deleting Foreign Key Relationships in EF4) to get an idea of how EF handles deleting child entities. In your case you fall under case number 1.
As far as the error you're getting change your foreach to use the ForEach method off the List<T> class:
origUserConf.ColumnConfigs.ForEach(col => ctx.ColumnConfigs.Remove(col))

Getting SerializeObject to use JsonProperty "name" defined inside interface

When calling "JsonConvert.SerializeObject" I am passing in an object that implements an interface. It is the interface that defines the JsonProperty attributes to set the desired JSON object property name. However when I examine the JSON object that is produced it is using the actual .NET property name, rather than JsonPropertyAttribute value. This leads me to believe it is only reflecting over the implementation of the interface to find the JsonProperty attributes, rather than the interface itself. I have verified that if I place the JsonProperty attributes on the implementing class then everything works as expected, but this is not the desired behaviour. Is there any way to make JSON.NET pick up the JsonPropertyAttributes defined upon the interface as well as (or instead of) the interface.
public interface ISpecifyDataPageToGet
{
[JsonProperty("offset")]
int PageNumber { get; }
[JsonProperty("limit")]
int PageSize { get; }
}
public class PageInfo : ISpecifyDataPageToGet
{
public PageInfo(int pageNumber, int pageSize)
{
this.PageNumber = pageNumber;
this.PageSize = pageSize;
}
// I don't want to have to define JsonProperty attribute here
public int PageNumber { get; private set; }
// Or here
public int PageSize { get; private set; }
}
public void MakeCall(ISpecifyDataPageToGet requestMessage)
{
// I'm passing instance of interface in here, but it still only picks up
// attributes defined on class implementing interface.
JsonConvert.SerializeObject(requestMessage, Formatting.None, new JsonSerializerSettings());
...
...
}
UPDATE: Reported on Codeplex project site
This has now been fixed in the Json.NET codebase by James and is working.
See the codeplex issue report as well as the Json.NET 4.0 Release 3 release notes:
New feature - JsonObject and JsonProperty attributes can now be placed on an interface and used when serializing implementing objects.

Resources