Entity Framework CodeFirst KeyNotFoundException in MemberDomainMap - entity-framework-4.3

Trying to run down an error in my EF datacontext implementation that is yielding a fairly cryptic error.
Test Name: Nodes_can_be_saved
Test FullName: MyProj.Test.Integration.AFDataContextTest.Nodes_can_be_saved
Test Source: c:\Users\pvencill. \Documents\Visual Studio 2012\Projects\MyProj\MyProj.Test\Integration\AFDataContextTest.cs : line 49
Test Outcome: Failed
Test Duration: 0:00:01.4192808
Result Message:
Test method MyProj.Test.Integration.AFDataContextTest.Nodes_can_be_saved threw exception:
System.Data.Entity.Infrastructure.DbUpdateException: Error retrieving values from ObjectStateEntry. See inner exception for details. ---> System.Data.UpdateException: Error retrieving values from ObjectStateEntry. See inner exception for details. ---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
Result StackTrace:
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at System.Data.Mapping.ViewGeneration.Structures.MemberDomainMap.GetDomainInternal(MemberPath path)
at System.Data.Mapping.ViewGeneration.QueryRewriting.FragmentQueryKB.CreateIsOfTypeCondition(MemberPath currentPath, IEnumerable`1 derivedTypes, MemberDomainMap domainMap)
at System.Data.Mapping.ViewGeneration.QueryRewriting.FragmentQueryKB.CreateVariableConstraintsRecursion(EdmType edmType, MemberPath currentPath, MemberDomainMap domainMap, EdmItemCollection edmItemCollection)
at System.Data.Mapping.ViewGeneration.QueryRewriting.FragmentQueryKB.CreateVariableConstraintsRecursion(EdmType edmType, MemberPath currentPath, MemberDomainMap domainMap, EdmItemCollection edmItemCollection)
at System.Data.Mapping.ViewGeneration.ViewgenContext..ctor(ViewTarget viewTarget, EntitySetBase extent, IEnumerable`1 extentCells, CqlIdentifiers identifiers, ConfigViewGenerator config, MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap, StorageEntityContainerMapping entityContainerMapping)
at System.Data.Mapping.ViewGeneration.ViewGenerator.CreateViewgenContext(EntitySetBase extent, ViewTarget viewTarget, CqlIdentifiers identifiers)
at System.Data.Mapping.ViewGeneration.ViewGenerator.GenerateDirectionalViewsForExtent(ViewTarget viewTarget, EntitySetBase extent, CqlIdentifiers identifiers, KeyToListMap`2 views)
at System.Data.Mapping.ViewGeneration.ViewGenerator.GenerateDirectionalViews(ViewTarget viewTarget, CqlIdentifiers identifiers, KeyToListMap`2 views)
at System.Data.Mapping.ViewGeneration.ViewGenerator.GenerateAllBidirectionalViews(KeyToListMap`2 views, CqlIdentifiers identifiers)
at System.Data.Mapping.ViewGeneration.ViewgenGatekeeper.GenerateViewsFromCells(List`1 cells, ConfigViewGenerator config, CqlIdentifiers identifiers, StorageEntityContainerMapping containerMapping)
at System.Data.Mapping.ViewGeneration.ViewgenGatekeeper.GenerateViewsFromMapping(StorageEntityContainerMapping containerMapping, ConfigViewGenerator config)
at System.Data.Mapping.StorageMappingItemCollection.ViewDictionary.SerializedGenerateViews(StorageEntityContainerMapping entityContainerMap, Dictionary`2 resultDictionary)
at System.Data.Mapping.StorageMappingItemCollection.ViewDictionary.SerializedGetGeneratedViews(EntityContainer container)
at System.Data.Common.Utils.Memoizer`2.<>c__DisplayClass2.<Evaluate>b__0()
at System.Data.Common.Utils.Memoizer`2.Result.GetValue()
at System.Data.Common.Utils.Memoizer`2.Evaluate(TArg arg)
at System.Data.Mapping.StorageMappingItemCollection.ViewDictionary.GetGeneratedView(EntitySetBase extent, MetadataWorkspace workspace, StorageMappingItemCollection storageMappingItemCollection)
at System.Data.Mapping.Update.Internal.ViewLoader.InitializeEntitySet(EntitySetBase entitySetBase, MetadataWorkspace workspace)
at System.Data.Mapping.Update.Internal.ViewLoader.SyncInitializeEntitySet[TArg,TResult](EntitySetBase entitySetBase, MetadataWorkspace workspace, Func`2 evaluate, TArg arg)
at System.Data.Mapping.Update.Internal.ViewLoader.SyncContains[T_Element](EntitySetBase entitySetBase, MetadataWorkspace workspace, Set`1 set, T_Element element)
at System.Data.Mapping.Update.Internal.ExtractorMetadata..ctor(EntitySetBase entitySetBase, StructuralType type, UpdateTranslator translator)
at System.Data.Mapping.Update.Internal.UpdateTranslator.GetExtractorMetadata(EntitySetBase entitySetBase, StructuralType type)
at System.Data.Mapping.Update.Internal.ExtractorMetadata.ExtractResultFromRecord(IEntityStateEntry stateEntry, Boolean isModified, IExtendedDataRecord record, Boolean useCurrentValues, UpdateTranslator translator, ModifiedPropertiesBehavior modifiedPropertiesBehavior)
at System.Data.Mapping.Update.Internal.RecordConverter.ConvertStateEntryToPropagatorResult(IEntityStateEntry stateEntry, Boolean useCurrentValues, ModifiedPropertiesBehavior modifiedPropertiesBehavior)
--- End of inner exception stack trace ---
at System.Data.Mapping.Update.Internal.RecordConverter.ConvertStateEntryToPropagatorResult(IEntityStateEntry stateEntry, Boolean useCurrentValues, ModifiedPropertiesBehavior modifiedPropertiesBehavior)
at System.Data.Mapping.Update.Internal.ExtractedStateEntry..ctor(UpdateTranslator translator, IEntityStateEntry stateEntry)
at System.Data.Mapping.Update.Internal.UpdateTranslator.LoadStateEntry(IEntityStateEntry stateEntry)
at System.Data.Mapping.Update.Internal.UpdateTranslator.PullModifiedEntriesFromStateManager()
at System.Data.Mapping.Update.Internal.UpdateTranslator.ProduceCommands()
at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache)
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
--- End of inner exception stack trace ---
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at MyProj.Data.MyProjDataContext.SaveChanges() in c:\Users\pvencill. \Documents\Visual Studio 2012\Projects\MyProj\MyProj.Data\MyProjDataContext.cs:line 44
at MyProj.Test.Integration.AFDataContextTest.Nodes_can_be_saved() in c:\Users\pvencill. \Documents\Visual Studio 2012\Projects\MyProj\MyProj.Test\Integration\AFDataContextTest.cs:line 55
Researching the error led to few hits on Google, but the ones I found suggested that it's something to do w/ my model relationships, though in looking at the DB that the migrations generated, all seems in order to my eyes. My relevant models are as follows:
My data context DBSets and modelCreating definition:
public DbSet<Blip> Blips { get; set; }
public DbSet<SensorAdapter> Sensors { get; set; }
public DbSet<NodeReport> NodeReports { get; set; }
public DbSet<Node> Nodes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Blip>().Property(b => b.TimeStamp).HasColumnType("datetime2");
modelBuilder.Entity<Node>().HasMany<NodeReport>(n => n.NodeReports).WithRequired(nr=>nr.Node);
modelBuilder.Entity<Blip>().HasMany<NodeReport>(b => b.NodeReports);
base.OnModelCreating(modelBuilder);
}
The Blips and SensorAdapter objects worked fine prior to adding NodeReports to them, so I suspect it's in there that the project lies.
I have a base Entity object that all my stuff inherits from, which just defines an Id property of type T; that was working fine.
NodeReport inherits from Report, whose definition is here:
public abstract class Report : Entity<long>
{
public Report()
{
Status = Status.Unknown;
}
public DateTime TimeStamp { get; set; }
public Status Status { get; set; }
public String Raw { get; set; }
}
NodeReport in turn is defined thus:
public class NodeReport : Report
{
public virtual Node Node { get; set; }
//public virtual Blip Blip { get; set; }
}
I tried it both with and without the Blip on there, commented out at the moment as I try and narrow down the problem
A Node is a fairly sparse class at hte moment too:
public class Node : Entity<long>
{
public Node ()
{
NodeReports = new List<NodeReport>();
}
public String HostName { get;set; }
public String Description { get; set; }
public virtual IList<NodeReport> NodeReports { get; set; }
}
Any suggestions would be greatly appreciated, I've been beating myself up trying to figure it.

Well, after much searching through my code and rebuilding from scratch I found that the problem was actually that I had a derived class of Node that had a Uri as a property, which obviously failed mapping since it doesn't have a default constructor (and possibly other reasons). I solved it for now by simply changing the property to a String which I validate as a Uri internally, though I would have preferred a more elegant solution. I tried mapping Uri and even a custom subclass (w/ default constructor) of Uri to a complextype, but that didn't help.
Still, the question above is answered.

With the #Paul's answer I could finally figure out my problem.
I am using EF with Inheritance TPT (Table per Type).
The source code
To make it easier I'll use the same classes discribed in this tutorial.
public abstract class BillingDetail
{
public int BillingDetailId { get; set; }
public string Owner { get; set; }
public string Number { get; set; }
}
[Table("BankAccounts")]
public class BankAccount : BillingDetail
{
public string BankName { get; set; }
public string Swift { get; set; }
public Agency Agency { get; set; } /* I added it */
}
public class InheritanceMappingContext : DbContext
{
public DbSet<BillingDetail> BillingDetails { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BankAccount>().ToTable("BankAccounts");
modelBuilder.Entity<CreditCard>().ToTable("CreditCards");
}
The problem
Note that I've added a new property called Agency inside the BankAccount. Given it is a complex type, if you do not map it you'll get this annoying error at runtime!
Solution
What I did was simply ignore this property Agency, but you can also map it to EF know what to do. Both will stop the error.
The most weird thing is that even not mapping the derived entity (BankAccount) the problem occurs. It seems that EF somehow knows that you created the derivation. So, if you're trying to run EF without mapping some derivation you will probably get this error too.

I have the same issue, unfortunately none of the solutions here in StackOverflow worked for me aside from the answers on other questions related to this issue.
But I found my own fix and you can also check it in your part if you have the same issue as mine. What happen is that if some class inherits from the table that I am using on my DbSet:
public DbSet<Employee> Employees { get; set; }
On this case, if some other classes inherit from my POCO Employee, this triggers this particular error. I removed all inheritance from this class and this fixes the issue.
Take note that this inheritance issue which triggers this same issue:
The given key was not present in the dictionary.
Only happens if the inheritance is on the same project. I tried to inherit the POCO on different project and it happens to be fine.

Related

EF Core 5 Duplicating Records in the Many-to-Many Relationship

I'm having trouble implementing the many-to-many relationship using the Entity Framework Core 5 in Visual Studio.
I have the classes:
public class Medico
{
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public int Id { get; set; }
public string Nome { get; set; }
public int CRM { get; set; }
public List<Especialidade>Especialidades { get; set; }
public class Especialidade
{
public int Id { get; set; }
public string Descricao { get; set; }
public IList<Medico>Medicos { get; set; }
}
And the Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Nome,CRM")] Medico medico)
{
var lstTags = Request.Form["chkTags"];
if (!string.IsNullOrEmpty(lstTags))
{
int[] splTags = lstTags.ToString().Split(',').Select(Int32.Parse).ToArray();
if (splTags.Count() > 0)
{
var medicoEspecialidades = await _context.Especialidades.Where(t => splTags.Contains(t.Id)).ToListAsync();
foreach (var me in medicoEspecialidades)
{
medico.Especialidades.Add(me);
}
}
}
if (ModelState.IsValid)
{
_context.Medicos.Add(medico);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(medico);
}
But when I run Create, it returns me with the following error:
"Cannot insert explicit value for identity column in table 'Especialidades' when IDENTITY_INSERT is set to OFF."
If I turn off the Identity_Insert of the Specialty table in the bank, it even inserts, but duplicates the records in the Specialty table.
I've been researching and trying to find a solution for 2 days now. Can someone who has been through this give me a hand?
The application source code is here: https://www.dropbox.com/s/xn6b95h7amfpuqa/AppCompleta%205.0.rar?dl=1
The approach looks Ok, though I would check to ensure that the medico being passed in does not have any Especialidade somehow coming in from the client as these would be detached entities. The error seems to imply that Medico may have a detached Especialidade in its collection. If the checked values represents everything that should be tracked, then this collection should be cleared and the Especialidade references added.
Do you have any explicit mapping configuration for either of these entities? If not I would highly recommend using one for Many-to-Many relationships as sometimes EF can default to unexpected schema assumptions when working off convention, especially in Code First if that is the case. I would look at your schema carefully to ensure it is matching what would be expected for a Many-To-Many. For example, what is the linking table name for Medico-Especialidade? Is there an entity defined for it in the configuration? This is entirely optional and EF should work it out, however if you do have explicit mapping that might not be configured correctly, tripping up the relationships.
One other detail giving off a smell:
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public List<Especialidade>Especialidades { get; set; }
This should be:
public ICollection<Especialidade> { get; set; } = new HashSet<Especialidade>();
EF can work with lists, but when it comes to proxies and the behind the scenes EF is doing with entities it is generally better to declare your collection references as ICollection rather than concrete classes. ToListing a HashSet merely produces a List, so either = new HashSet<Especialidade>() or = new List<Especialidade>() will do. The difference would merely be the behaviour of the collection when you are populating it after "newing" up a Medico, or deserializing one.

Using Automapper to map object to realm object with Xamarin and c#

I am creating an Xamarin.iOS app and a Realm database.I would like to keep my POCO objects separate from my RealmObject so what I did was use a repository pattern and within the repository I tried to use AutoMapper to map the POCO to the RealmObject
e.g. (subset)
public class PlaceRepository : IPlaceRepository
{
private Realm _realm;
public PlaceRepository(RealmConfiguration config)
{
_realm = Realm.GetInstance(config);
}
public void Add(Place place)
{
using (var trans = _realm.BeginWrite())
{
var placeRealm = _realm.CreateObject<PlaceRealm>();
placeRealm = Mapper.Map<Place, PlaceRealm>(place);
trans.Commit();
}
}
So, if I debug my code everything maps OK and placeRealm is populated OK but when I commit nothing gets saved to the Realm db. The following is my RealmObject
public class PlaceRealm : RealmObject
{
[ObjectId]
public string Guid { get; set; }
public string Title { get; set; }
public string Notes { get; set; }
}
and this is my POCO Place
public class Place
{
public string Guid { get; set; }
public string Title { get; set; }
public string Notes { get; set; }
}
And AutoMapper is initialized like so:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Place, PlaceRealm>();
cfg.CreateMap<PlaceRealm, Place>();
});
All standard stuff. Has anyone else managed to get something similar working?
Your poco 'Place' is called 'PlaceRealm'. I suspect that is a typo. (made edit)
I suspect that Automapper is instantiating a new object overwriting your original 'placeRealm' object.
Perhaps you could try
Mapper.Map(place, placeRealm);
in place of your current mapping.
which should just copy the values to your already instatiated and tracked object.
(no need to store the return value).
You might also want to make explicit which properties (3) you are mapping as currently Automapper will map all including those in the base class.
On a side note, you may run into performance issues with Automapper. I found it to be the performance bottleneck in some apps. ExpressMapper is a nice alternative.

EF, POCO, DB First... how to do Business Logic in Property "set"?

OK, so I've been building my first large(ish) EF 4.1 POCO + MVC application. It's a replacement of a legacy system so I 'm using an existing database.
I've generated my POCO classes using DbContext T4 generation. I've got some really nice forms going on and some really nice validation happening with a lot of sexy generics in my MVC classes to cut down on boiler-plate code... All's good.
Suddenly I realized that the most sensible thing (to me) would be for some of business logic to be in the "set" of some of the properties of my POCO objects.
E.g. Suppose the following class was generated by the T4;
public partial class SalesOrderLine
{
public int ID { get; set; }
public int SalesOrderID { get; set; }
public int ProductID { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public decimal ExtendedPrice { get; set; }
public virtual Product Product { get; set; }
public virtual SalesOrder SalesOrder { get; set; }
}
Ignore for a moment the obvious argument that the calculated field "ExtendedPrice" shouldn't even be stored in the database, and just come along with me for the ride...
...then, it seems to me, logically, if this object is really supposed to represent a Sales Order Line, that I should be able to construct my object such that the following unit test will work:
SalesOrderLine sol = new SalesOrderLine();
sol.UnitPrice = 100;
sol.Quantity = 5;
Assert.IsEqual(sol.ExtendedPrice, 500);
...obviously I can't do that as long as I want the base POCO to be generated by the T4. It seems to me I have several options:
Set the generated code file's properties "do not compile", copy and paste the generated code into another file and modify the "set" to do the business logic of setting the extended price when the UnitPrice or Quantity is set. The downside here is that the logic will be run whenever an object is loaded from the database (since the EF will set the public properties and not my private fields). Additionally, this object will then need to be maintained manually for the rest of the life of the project when database changes occur.
Create an UpdateTotals function that gets called in the Validate routine that I have for my object, which gets called by the SaveChanges() on the DbContext. Obviously, the above Unit Test above would not work in that case. The system, and my integration tests however would work and would only call the code when a change was done to the object.
Decide that I'm asking the wrong question, and that I should really add methods to the object called "SetPrice" and "SetQuantity", and then qualify the set accessors of the UnitPrice and Quantity to be "internal". The downside here is that MVC will try and update the model from the form and won't be able to set those properties.
Some solution that involves downloading two or three more frameworks that create even more levels of abstraction than I already have... A repository pattern, or "use NHibernate" or something like that... You can suggest this, but I'm growing weary of how much work it is to set things up to do it the "academically correct" way. For this project, I'd rather meet halfway on the long-term-maintainability vs. speed-of-development spectrum and not over-complicate my project with a ton of extra tools and dlls... ...but I'll try an keep an open mind :)
--- EDIT: another idea ---
[5.] Another thought, since the fields are always simply calculated there should really be no need to ever set them - either from the database or otherwise. Therefore, something like this might work:
public decimal ExtendedAmount
{
get { return UnitPrice * Quantity; }
internal set { }
}
...my thought is that the EF instantiation would attempt to call the "set", but the set would do nothing, then, when the object was saved or checked for changes it would call the 'get' and that would return the calculated value and that value would get stored in the DB. The only downside here is when you were trying to use the object model to validate the database when the database had in incorrect value stored in the ExtendedAmount field. It's a little hokie, I know, but I thought it would be an interesting trick... in fact the "set" could perhaps throw an exception if (value != UnitPrice * Quantity)
--- END EDIT ---
I'm curious to hear what other have done in these kinds of cases, as I'm sure it's common. Seems like a lot of the tutorials take you as far as "generating POCO classes from the database", and then leave the rest of the project development up to you.
Cheers,
Chris
A couple ideas:
Why not use Code First? That way, you can put business logic (e.g., calculated properties) right in your entity class.
Example
public partial class SalesOrderLine
{
public int ID { get; set; }
public int SalesOrderID { get; set; }
public int ProductID { get; set; }
private decimal _unitPrice;
public decimal UnitPrice
{
get { return _unitPrice; }
set
{
if (value == _unitPrice) return;
_unitPrice = value;
CalculateExtendedPrice();
}
}
private decimal _quantity;
public decimal Quantity
{
get { return _quantity; }
set
{
if (value == _quantity) return;
_quantity= value;
CalculateExtendedPrice();
}
}
public decimal ExtendedPrice { get; set; }
public virtual Product Product { get; set; }
public virtual SalesOrder SalesOrder { get; set; }
private void CalculateExtendedPrice()
{
ExtendedPrice = UnitPrice * Quantity;
}
}
If Code First is not an option, what about making your entity a partial class (if it is not already) and putting your business logic in a separate code file (but with the same class name). This way, your main code file will get overwritten when you generate, but your secondary code file will remain. This is the usual way to deal with generated code.

Ignoring properties when serializing

I'm pulling my hair out on this one.
I am trying to implement a multi-step wizard, and i'm using the Html.Serialize html helper in MVC3 Futures. This works well, except one of the properties in my model is a SelectList. I don't want this property serialized (and it blows up when it tries anyways).
I can't use [NonSerialized] because that only works on fields, not properties. I've even tried some of the other normal ways such as [XmlIgnore] (which I didn't think would work anyways).
Can anyone suggest an attribute that will ignore a property in a model when using Html.Serialize?
EDIT:
The error I get when I try to serialize is a InvalidDataContractException. There is this message:
Type 'System.Web.Mvc.SelectList' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
However, if I do this then I have to mark all the members with [DataMember] just to exclude 1 property, which seems kind of stupid.
UPDATE:
A quick example of this is this bit of code (make sure to add reference to System.Runtime.Serialization.dll):
Test.cs
[Serializable]
public class Test
{
public int ID { get; set; }
[IgnoreDataMember]
public SelectList TestList { get; set; }
}
HomeController.cs
public ActionResult About()
{
return View(new Test() { ID = 0, TestList = new SelectList(new [] {""})});
}
Home/About.cshtml
#using Microsoft.Web.Mvc
#model MvcApplication3.Models.Test
#Html.Serialize("Test", Model)
This generates the InvalidDataContractException
public class MyViewModel
{
[IgnoreDataMember]
public SelectList Items { get; set; }
...
}
or simply:
public class MyViewModel
{
public IEnumerable<SelectListItem> Items { get; set; }
...
}

Serializing EF4.1 Entities using JSON.Net

I am building an application using MVC3, Razor view engine, Repository Pattern with Unit of Work and using EF4.1 Code First to define my data model.
Here is a bit of background (gloss over it if you want).
The application itself is just an Intranet 'Menu'.
The 2 main entities are MenuItem and Department of which:
MenuItem can have many Departments
Departments can have many MenuItems
MenuItem may have a MenuItem as a parent
This is how I have defined my Entities
public class MenuItem
{
public int MenuItemId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public virtual ICollection<Department> Departments { get; set; }
public int? ParentId { get; set; }
public virtual MenuItem ParentMenuItem { get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string Name { get; set; }
public virtual ICollection<MenuItem> MenuItems { get; set; }
}
I am using the FluentAPI to define the Self Reference Many-to-Many for the MenuItem.
The issue I am having is passing a MenuItem to the view via JSON.
The central issues are that I have a circular reference between my entities that the built in JSON parser can't deal with and I have lazy loading and proxy generation still enabled.
I am using JSON.net library from Nuget as my JSON Serializer as this seems to be a nice way round the circular reference issue. I now am unsure how to 'fix' the proxy generation issue. Currently the serializer throws The RelationshipManager object could not be serialized. This type of object cannot be serialized when the RelationshipManager belongs to an entity object that does not implement IEntityWithRelationships.
Can anyone help me with this? If I turn off proxy generation, I am going to have a hell of a time loading all of the MenuItem children so I am keen leave this on. I have read a fair amount and there seems to be a variety of different answers including projecting the entities into another object and serialize that, etc, etc. Ideally there would be some way of configuring JSON.net to ignore the RelationshipManager object?
Update
Here is what I have used as a Custom ContractResolver for JSON.Net serializer. This seems to have sorted out my issue.
public class ContractResolver : DefaultContractResolver
{
private static readonly IEnumerable<Type> Types = GetEntityTypes();
private static IEnumerable<Type> GetEntityTypes()
{
var assembly = Assembly.GetAssembly(typeof (IEntity));
var types = assembly.GetTypes().Where(t => String.Equals(t.Namespace, "Namespace", StringComparison.Ordinal));
return types;
}
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
if (!AllowType(objectType))
return new List<MemberInfo>();
var members = base.GetSerializableMembers(objectType);
members.RemoveAll(memberInfo => (IsMemberEntityWrapper(memberInfo)));
return members;
}
private static bool AllowType(Type objectType)
{
return Types.Contains(objectType) || Types.Contains(objectType.BaseType);
}
private static bool IsMemberEntityWrapper(MemberInfo memberInfo)
{
return memberInfo.Name == "_entityWrapper";
}
}
IEntity is an interface all my Code First entity objects implement.
I realise this question has an accepted answer, but I thought I would post my EF Code First solution for future viewers. I was able to get around the error message with the contract resolver below:
class ContractResolver : DefaultContractResolver
{
protected override List<System.Reflection.MemberInfo> GetSerializableMembers(Type objectType)
{
if (objectType.Namespace.StartsWith("System.Data.Entity.Dynamic"))
{
return base.GetSerializableMembers(objectType.BaseType);
}
return base.GetSerializableMembers(objectType);
}
}
This works because EF Code First classes inherit from the POCO class that you actually want serialized, so if we can identify when we are looking at an EF generated class (by checking the namespace) we are able to just serialize using the properties from the base class, and therefore only serialize the POCO properties that we were really after in the first place.
Well, you used powerful serialization API which serializes references and all members as well and now you complains that it serializes all members :)
I didn't test it but I believe this will bring you close to the solution.
JSON.NET is quite powerful tool and it should offer you the extensibility point to avoid this behavior but you will have to code it yourselves. You will need custom DataContractResolver where you define which members should be serialized. Here is the similar example for NHibernate.
You can implement some logic which will take only members present in the parent class of dynamic proxy. I hope this will not break lazy loading. To validate that current entity is proxy you can use this code to get all known proxy types:
IEnumerable<Type> types = ((IObjectContextAdapter)dbContext).ObjectContext.GetKnownProxyTypes();

Resources