I am attempting to use EF with Code First and the Web API. I don't have any problems until I get into serializing Many-to-Many relationships. When I attempt to execute the following web api method below I get the following error message:
public class TagsController : ApiController
{
private BlogDataContext db = new BlogDataContext();
// GET api/Tags
public IEnumerable<Tag> GetTags()
{
return db.Tags.AsEnumerable();
}
}
I get the following error:
'System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D'
with data contract name
'Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies'
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.
I have read some SO articles (article 1, article 2) that the fix is to add the following attribute:
[DataContract (IsReference=true)]
but this has had no effect. Also using [IgnoreDataMember] does not have an effect. The only option that does seem to work is to set Configuration.ProxyCreationEnabled to false. Is this my only option? Am I missing something?
Sample POCO objects:
Tag
[DataContract(IsReference = true)]
public class Tag
{
public Tag()
{
this.Blogs = new HashSet<Blog>();
}
[Key]
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[IgnoreDataMember]
public virtual ICollection<Blog> Blogs { get; set; }
}
Blog
[DataContract(IsReference = true)]
public class Blog
{
public Blog()
{
this.Tags = new HashSet<Tag>();
}
[Key]
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[IgnoreDataMember]
public virtual ICollection<Tag> Tags { get; set; }
}
When you see an object like:
System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D
It is a runtime EF Generated version of a proxy to what would normally be considered a POCO object.
Entity Framework has created this object because it tracks when the objects has changed so when you call .SaveChanges() it can optimize what to do. The downfall of this is that you aren't actually using the specific object you defined, thus Data Contracts and Frameworks (Json.net) cannot use them as they would your original POCO object.
To Prevent EF from returning this object you have two choices (ATM):
First, Try turning off Proxy object creation on your DbContext.
DbContext.Configuration.ProxyCreationEnabled = false;
This will completely disable the create of Proxy objects for every query to the specific DbContext. (This does not affect the cached object in the ObjectContext).
Secondly, use EntityFramework 5.0+ with AsNoTracking()
(ProxyCreationEnabled is still available in EF 5.0 as well)
You should also be able to
DbContext.Persons.AsNoTracking().FirstOrDefault();
or
DbContext.Persons.
.Include(i => i.Parents)
.AsNoTracking()
.FirstOrDefault();
Instead of globally disabling proxy creation for the DbContext, this only turns it off per query. (This DOES affect the cached object in the ObjectContext, it is not cached)
I wanted to leave proxy creation inteact, and found that using the ProxyDataContractResolver seemed to resolve the issue for me. See msdn for a reference on how to use it in wcf, which isn't exactly WebAPI, but should get you going along the right path.
Related
I'm getting started with ASP.NET Core and EF Core 6. I'm trying to build a simple web api whith two models and an 1-n relationship between the models.
Model A:
public class ModelA
{
public Guid Id { get; set; }
public string StringProperty { get; set; }
public ICollection<ModelB> ModelBs { get; set; }
}
Model B:
public class ModelB
{
public Guid Id { get; set; }
public string StringProperty { get; set; }
public Guid ModelAId { get; set; }
public ModelA ModelA { get; set; }
}
When I try to create a ModelB by using the POST-endpoint of the ModelB-Controller, it expects me to pass the ModelA as well. If I do provide it, I get a duplicate key error because EF tries to create a new ModelA in the database, which causes a duplicate key error.
I must only use the ModelB-model as parameter for the post method and explicitly must not use any kind of intermediate model.
I would like to only use the ModelA id, not the entire ModelA object:
//Desired post-request
{
"stringProperty": "value",
"modelAId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
Making the ModelA reference nullable allows for post-requests as described above:
public class ModelB
{
public Guid Id { get; set; }
public string StringProperty { get; set; }
public Guid ModelAId { get; set; }
public ModelA? ModelA { get; set; }
}
But that feels wrong since an instance of ModelB must not exist without a reference to a ModelA instance.
Is there any way to achieve this without using DTOs or making the reference nullable?
I'm probably missing something trivial here.
I think you should star to use DTOs on your project! For example, model B would have only the data necessary to create the item, i.e StringProperty and ModelAId, and inside of your controller, you would associate with the existing ModelA.
You can have a look on the entity framework on the link below.
https://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx
Even if you create a DTO(highly recommended, passing a whole another object just to show relationship is a waste of bandwidth and bad practice ) or not you will accept ModelAId from the user, not the whole object anyway so.
Edit:
if you want to trick the product owner. Just create a base class without ModelA prop and all the rest props in ModelB now make ModelB inherit this and add ModelA explicitly. Now create ModelBPost also inheriting from this and use this as a parameter for POST data this way the product owner knows the fields are exactly the same and you pass the verification error.
Old Answer
How about you get the model A fresh from DB and assign that to ModelB
like
public IAction PostModelB(ModelB modelB)
{
modelB.ModelA = context.ModelAs.First(x => x.Id == modelB.ModelAId);
//now since efcore is tracking this it know this object already exists
context.ModelBs.Add(modelB);
}
However, sometimes EFCore screws up and you get an already tracked error(which is quite unfortunate after so much time it still can't do it properly). If this happens(which it might since you are persistent on not using a DTO and accept a whole object just for relationship) you will have to set the navigation property null and only use the ModelAId property to insert the new record or you can get the instance which efcore holds:
var modelA = context.ModelAs.First(x => x.Id == modelB.ModelAId);
var trackedinstance = context.ChangeTracker.Entry(modelA)?.Entity ?? modelA;
modelB.ModelA = trackedinstance;
modelB.ModelAId = modelA.Id;
guys.
I have a question about EF Core DbCommandInterceptor.
Let's have a class with 2 fields like this
public class User
{
public Guid Id { get; set; }
public string SameData { get; set; }
}
public class TestClass
{
public Guid Id { get; set; }
public Guid TestClassId { get; set; }
[NotMapperd, MyAttr]
public TestClass TestClass { get; set; }
}
where User and TestClass are both located in the different contexts (for example, UserDbContext, TestDbContext). MyAttr is the marker attribute, nothing more.
So, I want to write an interceptor that raises up each time we try to get info about TestClasses, but after data got it should get an additional data about User with cross-request to UserDbContext (It possible, because I have User Id after the command execution and can use this Id in the request)
I know, that it should be DbCommandInterceptor.ReaderExecuted or DbCommandInterceptor.ReaderExecutedAsync in this case, but I cannot understand how to get information about objects in the result (I can get rows but I cannot understand what should I do, how should I map it). I can use additional libraries in the project if needed (like Dapper and others).
Could anyone helps me to get
Result Type - concrete entity type or entity collection type?
Result as a C# object (POCO or POCO collection)?
Thank you.
I know, that it should be DbCommandInterceptor.ReaderExecuted or DbCommandInterceptor.ReaderExecutedAsync in this case, but I cannot understand how to get information about objects in the result
The EF Command interceptors don't support that. You can replace the DataReader with a different one. But the query was generated to fill a particular object graph, which is not exposed by the interceptor API.
I'm having a problem very similar to the ones mentioned in these questions:
Why is Entity Framework navigation property null?
Why EF navigation property return null?
The plot twist in my case is that the navigation collection properties are populated by EF, but only after I've queried DbSet<T> properties of the dependent types in the DbContext. To make my situation clearer, here's how my model is set up:
[Table(nameof(Composer))]
internal class ComposerRelationalDto : RelationdalDtoBase
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public virtual ICollection<NameRelationalDto> LocalizedNames { get; set; } = new HashSet<NameRelationalDto>();
public virtual ICollection<ArticleRelationalDto> Articles { get; set; } = new HashSet<ArticleRelationalDto>();
}
[Table(nameof(ComposerName))]
internal class NameRelationalDto : RelationdalDtoBase
{
[Key]
public long Id { get; set; }
[Required]
[ForeignKey(nameof(Composer))]
public Guid Composer_Id { get; set; }
public ComposerRelationalDto Composer { get; set; }
}
[Table(nameof(ComposerArticle))]
internal class ArticleRelationalDto : RelationdalDtoBase
{
[Key]
public long Id { get; set; }
[Index]
public Guid StorageId { get; set; }
[Required]
[ForeignKey(nameof(Composer))]
public Guid Composer_Id { get; set; }
public ComposerRelationalDto Composer { get; set; }
[Required]
[MaxLength(5)]
public string Language { get; set; }
}
In the corresponding repository I filter ComposerRelationalDto objects by their name:
DbContext.Set<NameRelationalDto>().Where(nameWhereClause).GroupBy(n => n.Composer_Id).Select(group => group.FirstOrDefault().Composer)
The set of ComposerRelationalDtos has empty collections for the Articles and LocalizedNames properties, even though the data has been correctly persisted in the database. However, if I load all DTOs of type ArticleRelationalDto and NameRelationalDto in a QuickWatch while debugging, then the same filter no longer returns empty collections and all relevant objects are present in the collection properties.
What I've tried so far was to
enable lazy loading and the creation of proxies explicitly
configure the one-to many-relationships manually:
modelBuilder.Entity<ComposerRelationalDto>().HasMany(c => c.LocalizedNames).WithRequired(n => n.Composer).HasForeignKey(n => n.Composer_Id);
modelBuilder.Entity<ComposerRelationalDto>().HasMany(c => c.Articles).WithRequired(a => a.Composer).HasForeignKey(a => a.Composer_Id);
and finally I just tried fiddling with the DbQuery<T>.Include() method DbContext.Set<ComposerRelationalDto>().Include(c => c.Articles) which unfortunately throws an ArgumentNullException from one of the internal methods it calls.
Basically, whatever fixes or workarounds I've tried haven't helped, so I must ask for more help.
Edit:
I modified the dependent types' Composer property to be virtual. However, the problem persists.
After using .Select(group => group.FirstOrDefault().Composer).Include(c => c.Articles).Include(c => c.LocalizedNames) I now no longer get an ArgumentNullException (maybe I was getting the ArgumentNullException because I was initially using .Include() in a QuickWatch?), but rather a MySqlException: Unknown column 'Join2.Id' in 'field list'; the Data dictionary contains Key: "Server Error Code" Value: 1054. Also the generated SQL is ridiculously large and barely legible.
I figured it out. It was the internal access modifier on class declarations. A shame, because I really wanted to make the rest of the solution entirely database-agnostic (hence the unusual use of DTOs for code first, instead of the actual entities, as was already pointed out in the comments) and I wanted to enforce this in a strict manner.
Anyway, I played around some more with access modifiers and I could only manage restricting the DB object's visibility by making them public with internal protected constructors. Any other combination of class and ctor visibility involving internal caused the problem to reappear. No luck with InternalsVisibleTo, either.
This question - Entity Framework Code First internal class - is it possible? - seems to suggest that using an internal class shouldn't be a problem for EF, but it appears it is, after all, somewhat of a problem. If it wasn't then (Julie Lerman's answer dates back to 2011), it is now. I'm using EF 6.2.0 at the moment.
Just as mentioned in this post, I am getting a Json serialization error while serializing an Entity Framework Proxy:
A circular reference was detected while serializing an object of type
'System.Data.Entity.DynamicProxies.PurchaseOrder_446B939192F161CDBC740067F174F7A6059B0F9C0EEE68CD3EBBD63CF9AF5BD0'.
But the difference is, I don't have a circular reference in my entities, and it only occurs in our production environment. Locally everything works fine...
My Entities:
public interface IEntity
{
Guid UniqueId { get; }
int Id { get; }
}
public class Entity : IEntity
{
public int Id { get; set; }
public Guid UniqueId { get; set; }
}
public class PurchaseOrder : Entity
{
public string Username { get; set; }
public string Company { get; set; }
public string SupplierId { get; set; }
public string SupplierName { get; set; }
public virtual ICollection<PurchaseOrderLine> Lines { get; set; }
}
public class PurchaseOrderLine : Entity
{
public string Code { get; set; }
public string Name { get; set; }
public decimal Quantity { get; set; }
}
The GetCurrent action on my PurchaseOrderController throwing the exception:
public class PurchaseOrderController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public PurchaseOrderController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public JsonResult GetCurrent()
{
return Json(EnsurePurchaseOrder(), JsonRequestBehavior.AllowGet);
}
private PurchaseOrder EnsurePurchaseOrder()
{
var company = RouteData.GetRequiredString("company");
var repository = _unitOfWork.GetRepository<PurchaseOrder>();
var purchaseOrder = repository
.Include(p => p.Lines)
.FirstOrDefault
(
p => p.Company == company &&
p.Username == User.Identity.Name
);
if (purchaseOrder == null)
{
purchaseOrder = repository.Create();
purchaseOrder.UniqueId = Guid.NewGuid();
purchaseOrder.Company = company;
purchaseOrder.Username = User.Identity.Name;
_unitOfWork.SaveChanges();
}
return purchaseOrder;
}
}
Option 1 (recommended)
Try turning off Proxy object creation on your DbContext.
DbContext.Configuration.ProxyCreationEnabled = false;
Typically this scenario is because the application is using POCO objects (Either T4 Generated or Code-First). The problem arises when Entity Framework wants to track changes in your object which is not built into POCO objects. To resolve this, EF creates proxy objects which lack the attributes in the POCO objects, and aren't serializable.
The reasons why I recommend this approach; using a website means that you probably don't need change tracking (stateful) on Entity Framework objects, it free's up memory and cpu because change tracking is disabled and it will work consistantly on all your objects the same way.
Option 2
Use a serializer (like JSON.Net which is already included in ASP.Net 4) that allows customization to serialize the object(s).
The reasons I do not recommend this approach is that eventually custom object serialization logic will be need to serial proxy objects as other objects types. This means you have a dependency on logic to deliver a result downstream. Changing the object means changing logic, and in an ASP.Net MVC project (any version) instead of only changing a View you have some thing else to change that is not readily known outside of whoever wrote the logic first.
Option 3 (Entity Framework 5.x +)
Use .AsNoTracking() which will disable the proxy objects on the specific query. If you need to use change tracking, this allows a nice intermediate solution to solution #1.
Your POCO entities are perfectly serializable. Your problem is that the dynamic proxies the EF runtime creates for you are usually not. You can set the context.Configuration.ProxyCreationEnabled to false but then you lose lazy loading. My strong recommendation to you is to use Json.NET which supports serialization for EF entities:
ADO.NET Entity Framework support accidently added to Json.NET
Popular high-performance JSON framework for .NET
I spent countless hours attempting all of the various solutions I found scattered throughout the web, including:
[JsonIgnore]
Internal Getters
Disabling LazyLoadingEnabled and ProxyCreationEnabled
Setting ReferenceLoopHandling to "ignore"
Carefully using explicit loading where needed
All of which ultimately proved fruitless for me. Ignoring a property helped one query, but hurt 3 others. It felt like the programming equivalent to whack-a-mole.
The context of my problem was that the data going in an out of my application had to be JSON. No way around it. Inserts and updates obviously pose much less of a problem. But selecting data that's stored in a normalized database (and in my case including a version history) to be serialized is a nightmare.
The solution:
Return the data (properties) you need as anonymous objects.
A code example:
In this case I needed the 3 latest tickets, based on "Date Scheduled". But also needed several properties stored in related entities.
var tickets =
context.TicketDetails
.Where(t => t.DateScheduled >= DateTime.Now)
.OrderBy(t => t.DateScheduled)
.Take(3)
.Include(t => t.Ticket)
.Include(t => t.Ticket.Feature)
.Include(t => t.Ticket.Feature.Property)
.AsEnumerable()
.Select(
t =>
new {
ID = t.Ticket.ID,
Address = t.Ticket.Feature.Property.Address,
Subject = t.Ticket.Subject,
DateScheduled = String.Format("{0:MMMM dd, yyyy}", t.DateScheduled)
}
);
And voila, no self referencing loops.
I realize this situation may not be adequate in all cases given that entities and objects may change. But it's certainly worth some consideration if all else fails.
Whatever classes have the reference of other class just add attribute like this
[Newtonsoft.Json.JsonIgnoreAttribute]
public virtual ICollection<PurchaseOrderLine> Lines { get; set; }
Now everything work smooth
I was having the same issue, what I have done is have passed only needed column to view , In my case. only 2.
List<SubCategory> lstSubCategory = GetSubCateroy() // list from repo
var subCategoryToReturn = lstSubCategory.Select(S => new { Id = S.Id, Name = S.Name });
return this.Json(subCategoryToReturn , JsonRequestBehavior.AllowGet);
I had the same error, however I saw it both on production server and locally. Changing the DbContext configuration didn't quite solve my issue. A different solution was presented to me with the
[IgnoreDataMember]
attribute on DB entity references. See the post here if this sounds more pertinent to your issue.
ASP.NET Web API Serialized JSON Error: "Self Referencing loop"
In your DbContext class, add this line of code:
this.Configuration.ProxyCreationEnabled = false;
For example:
public partial class EmpDBEntities : DbContext
{
public EmpDBEntities()
: base("name=EmpDBEntities")
{
this.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Department> Departments { get; set; }
public virtual DbSet<Employee> Employees { get; set; }
}
The circular reference happens because you use eager loading on the object.
You have 3 methods:
Turn off eager loading when your loading your Query (linq or lambda)
DbContext.Configuration.ProxyCreationEnabled = false;
Detach the objects (= no eager loading functionality & no proxy)
Repository.Detach(entityObject)
DbContext.Entry(entityObject).EntityState = EntityState.Detached
Clone the properties
You could use something like AutoMapper to clone the object, don't use the ICloneable interface, because it also clones the ProxyProperties in the object, so that won't work.
In case you are building an API, try using a separte project with a different configuration (that doesn't return proxies)
PS. Proxies is the object that's created by EF when you load it from the Entity Framework. In short: It means that it holds the original values and updated values so they can be updated later. It handles other things to ;-)
I had the same problem and resolved it by un-checking Json.NET in the project Extensions in the Reference Manager.
(see the image http://i.stack.imgur.com/RqbXZ.png)
I also had to change the project.csproj file to map the correct path for the new version:
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
and still had to configure the web.config
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
Note that in the web.config file I was forced to refer to the OLDER (6.0.0.0) version though the installed version was 6.0.5.
Hope it helps!
In delving into Fluent nHibernate, I discovered a potential breaker for using it...
Given the following POCO code.
public class Customer
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Details Details { get; set; }
}
public class Details
{
public virtual int Id { get; set; }
public virtual IList<Orders> Orders { get; set; }
}
public class CustomerMap : ClassMap<Customer>
{
// perform mapping
}
public class DetailsMap : ClassMap<Details>
{
// perform mapping
}
I loaded up ASP.NET MVC and attempted to use Json Serialization.
using System.Web.Script.Serialization;
public static partial class JsonExtensions
{
public static string ToJson(this object item)
{
return new JavaScriptSerializer().Serialize(item);
}
}
And Lo, when I passed a query from my nHibernate context to the ToJson method, I got an error!
A circular reference was detected while serializing an object of type 'System.Reflection.RuntimeModule'.
It seems to do this whether I pull a single object, or a list of objects ..or anything for that matter. I've even tried marking my classes as [Serializable] with the same result. This doesn't happen with the exact same classes using the Microsoft Entity Framework Code-Only approach.
Can I not deserialize nHibernate DTOs into JSON?
Adding more code for examination.
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var customers= session.CreateCriteria(typeof(Customer)).List<Customer>();
foreach (var customer in customers)
{
Console.WriteLine(customer.ToJson());
}
Console.ReadLine();
}
}
This is just a hunch but you might like to investigate what type it is really trying to serialize- nHibernate generates proxies for each POCO at runtime (so it can do stuff like lazy loading of foreign key entities, etc.). This may be why you are getting this error.
Try specifying the exact type to be serialized or perhaps create a brand new object to serialize, populating its properties with that of the nHibernate POCO.
EDIT:
This seems to be much more likley the answer to your problems:
http://www.west-wind.com/WebLog/posts/147218.aspx
Basically, check all of your POCO's for any circular references (e.g. a Customer POCO that has an Order POCO as a property while the Order POCO has a list of Customer's as a property)