Entity Framework doesn't populate collection properties - c#

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.

Related

Entity Framework one-to-one relationships

I'm having some issues with understanding how to property set up one-to-one relationships for a Web API using Entity Framework Core.
Consider the following objects:
public class Car
{
[JsonIgnore]
public Int32 CarId { get; set; }
public virtual Horn Horn { get; set; }
public virtual ICollection<Wheel> Wheels { get; set; }
}
public class Horn
{
[JsonIgnore]
public Int32 HornId { get; set; }
public Sound Sound { get; set; }
}
public class Sound
{
// some other props
}
When I perform a query in my repository, one-to-many is by default excluded unless I use .Include(), however, for one-to-one properties, they are by default included when I serialize.
e.g. It gets really messy because I query for a car and it returns all it's sub-components in the JSON response.
I can set car.Horn = null, etc, but that seems difficult to maintain for complex objects. I would like it to function like .Include(), but by default be excluded (in case I do want to query the full object).
Edit: Note, this issue is recursive, the car pulls in horn which pulls in sound. On a real world example like a user table, the automatically pulled in data when serialized is huge unless specifically nullifying the child properties.
EDIT2:
Here is an example of a repository call that by default bring back all one-to-one properties:
var group = _context.Car.Include(c =>
c.Wheels).SingleOrDefault(u => u.CarId == id);
Note the Include works as expected for many-to-one, but this query, even when the Include is removed, will recursively return all child objects that are one-to-one.
It does appear some type of lazy loading is being introduced in EF CORE 2.1
This article should give you a hint.
https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
Mainly:
Turn lazy loading off for serialization
Lazy loading and serialization don’t mix well, and if you aren’t
careful you can end up querying for your entire database just because
lazy loading is enabled. Most serializers work by accessing each
property on an instance of a type. Property access triggers lazy
loading, so more entities get serialized. On those entities properties
are accessed, and even more entities are loaded. It’s a good practice
to turn lazy loading off before you serialize an entity. The following
sections show how to do this.
EDIT:
Here is the way to disable lazy loading for all entities. But note you have to achieve this several way, so check the other options in the article...
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Context mapping
modelBuilder.Entity<SessionFeedbackModel>(entity =>
{
entity.HasOne(s => s.Session).WithOne(p => p.Feedback)
.HasForeignKey<SessionFeedbackModel>(s => s.SessionId).OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<SessionQuestionModel>(entity =>
{
entity.HasOne(e => e.SessionResult).WithOne(e => e.SessionQuestion)
.HasForeignKey<SessionQuestionResultModel>(e => e.SessionQuestionId)
.OnDelete(DeleteBehavior.Restrict);
});
Models
public class SessionQuestionResultModel
{
public int Id { get; set; }
public int SessionQuestionId { get; set; }
public SessionQuestionModel SessionQuestion { get; set; }
}
public class SessionFeedbackModel
{
public int Id { get; set; }
public int SessionId { get; set; }
public SessionModel Session { get; set; }
}
EF Core 1.x or 2.x does not support 1 to 1 very well or at all, but it can be done this way, this would be radically different for EF 6.x.x

How to create 0..1-1 relationship in EF6

I have a problem trying to correctly configure a relationship in EF. I have used EF code first to generate classes from an existing database. The first table holds a list of instructions, the second holds a record of the state that each instruction is in.
Tables (simplified):
Instruction
-----------
InstructionID
CurrentInstructionStateHistoryID
InstructionStateHistory
-----------------------
InstructionStateHistoryID
InstructionID
State
So you can see that there are two relationships between the tables - a 1-many relationship based on InstructionID, which I'm not interested in, and have therefore deleted the properties for. The second relationship is based on the CurrentInstructionStateHistoryID property, which points to the "current" state of the instruction.
The classes are as follows:
public partial class Instruction
{
[Key]
public int InstructionID { get; set; }
public int? CurrentInstructionStateHistoryID { get; set; }
public virtual CurrentInstructionStateHistory InstructionStateHistory { get; set; }
}
public partial class InstructionStateHistory
{
[Key]
public int InstructionStateHistoryID { get; set; }
public int InstructionID { get; set; }
public string State { get; set; }
public virtual Instruction tblInstruction { get; set; }
}
Here's the fluent API setup to define the relationship:
modelBuilder.Entity<InstructionStateHistory>()
.HasRequired(e => e.tblInstruction)
.WithOptional(e => e.CurrentInstructionStateHistory);
So, it all compiles and runs. But when I get to a bit of code like this:
Instruction instruction = await _dal.InstructionRepository.Find(claimID);
InstructionStateHistory history = i.CurrentInstructionStateHistory;
I can see that the instruction is populated correctly, let's say the Id is 1234. When I examine the InstructionStateHistory object, what I want to see is that it's InstructionID is 1234, but instead what I see is that it's InstructionStateHistoryID, i.e. is's primary key, is 1234 and that it's related to a completely different instruction.
Somehow I need to tell EF that Instruction.CurrentInstructionStateHistoryID links to InstructionStateHistory.InstructionStateHistoryID.
I've tried many combinations of data annotations and fluent setup but have been unable to find a combination that actually works, either I get the above result or a runtime error. Any help gratefully accepted!
It seems like EF just can't handle this case, so the solution was to forget the concept of a "current" InstructionStateHistory. Instead I added a date field to the InstructionStateHistory table, and then changed the Instruction class to have a regular collection property as follows:
public virtual ICollection<InstructionStateHistory> InstructionStateHistories{ get; set; }
Then when I need the "current" state I just query the colection, sort by date and take the latest one.

ASP.NET WebAPI Creating one-level JSON

There are many related entities in Domain assembly. For example People that has navigation properties (Level1) to FamilyRelations, Houses and Persons. Beside this the Houses has own nav.prop (Level2) to Address and Address (Level3) has to City, Street ... etc.
When I set LazyLoadingEnabled to true then I'm getting JSON (on the left side in screen) with all related entities.
How can I get only one level of nesting (as on the right side in scree) or set other levels to NULL value (because I had setting Newtonsoft.Json.NullValueHandling.Ignore)?
Can I implement it without use .Include to each entity?
My class of People:
public class People : BaseEntity
{
public int PersonID { get; set; }
public int HouseID { get; set; }
public int PeopleNumber { get; set; }
public int? FamilyRelationID { get; set; }
//FK to House
public virtual House Houses { get; set; }
//FK to Person
public virtual Person Persons { get; set; }
//FK to FamilyRelations
public virtual FamilyRelations FamilyRelations { get; set; }
}
WebAPI config:
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling
= Newtonsoft.Json.NullValueHandling.Ignore;
I do not have any solution because I did not have enough experience with it. So, I need your suggestions, advices about it. Sorry for my English and if I have to add more informations, please let me know. Thanks
UPDATE
I've tried to add [JsonIgnore] or ignore those properties in mapping class but when I do request get/House then I need to get field from Address without nav.prop and when request get/People then I do not nedd Address. As a result I can't ingnore it.
Never return tracked objects to the controller. Your business logic code (which should not exist in the controller) should map your database aware objects to POCOs. This can be as simple as using
var poco = AutoMapper.Map<People>(livePerson)
And you setup in your mapping profile to ignore those properties so they're not copied.
Note my automapper-fu is rusty that syntax is rough code.
You want to be very careful with any blind mapping as it opens you up to the Mass Assignment vulnerability. This is equally true for going straight to your live tracked objects. If a user sees in their data IsAdmin: false, they might get crafty and post IsAdmin: true. This can be saved to your database with blind assignments.
If you are look for a way to ignore navigation properties in json serialization, this answer can help and you can ignore navigation properties in json serialization by it.

Serialization of Entity Framework objects with One to Many Relationship

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.

Easier way of avoiding duplicates in entity framework

Can anyone provide an easier more automatic way of doing this?
I have the following save method for a FilterComboTemplate model. The data has been converted from json to a c# model entity by the webapi.
So I don't create duplicate entries in the DeviceProperty table I have to go through each filter in turn and retrieve the assigned DeviceFilterProperty from the context and override the object in the filter. See the code below.
I have all the object Id's if they already exist so it seems like this should be handled automatically but perhaps that's just wishful thinking.
public void Save(FilterComboTemplate comboTemplate)
{
// Set the Device Properties so we don't create dupes
foreach (var filter in comboTemplate.Filters)
{
filter.DeviceProperty = context.DeviceFilterProperties.Find(filter.DeviceFilterProperty.DeviceFilterPropertyId);
}
context.FilterComboTemplates.Add(comboTemplate);
context.SaveChanges();
}
From here I'm going to have to check whether any of the filters exist too and then manually update them if they are different to what's in the database so as not to keep creating a whole new set after an edit of a FilterComboTemplate.
I'm finding myself writing a lot of this type of code. I've included the other model classes below for a bit of context.
public class FilterComboTemplate
{
public FilterComboTemplate()
{
Filters = new Collection<Filter>();
}
[Key]
public int FilterComboTemplateId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public ICollection<Filter> Filters { get; set; }
}
public class Filter
{
[Key]
public int FilterId { get; set; }
[Required]
public DeviceFilterProperty DeviceFilterProperty { get; set; }
[Required]
public bool Exclude { get; set; }
[Required]
public string Data1 { get; set; }
}
public class DeviceFilterProperty
{
[Key]
public int DeviceFilterPropertyId { get; set; }
[Required]
public string Name { get; set; }
}
Judging from some similar questions on SO, it does not seem something EF does automatically...
It's probably not a massive cut on code but you could do something like this, an extension method on DbContext (or on your particular dataContext):
public static bool Exists<TEntity>(this MyDataContext context, int id)
{
// your code here, something similar to
return context.Set<TEntity>().Any(x => x.Id == id);
// or with reflection:
return context.Set<TEntity>().Any(x => {
var props = typeof(TEntity).GetProperties();
var myProp = props.First(y => y.GetCustomAttributes(typeof(Key), true).length > 0)
var objectId = myProp.GetValue(x)
return objectId == id;
});
}
This will check if an object with that key exists in the DbContext. Naturally a similar method can be created to actually return that entity as well.
There are two "returns" in the code, just use the one you prefer. The former will force you to have all entities inherit from an "Entity" object with an Id Property (which is not necessarily a bad thing, but I can see the pain in this... you will also need to force the TEntity param: where TEntity : Entity or similar).
Take the "reflection" solution with a pinch of salt, first of all the performance may be a problem, second of all I don't have VS running up now, so I don't even know if it compiles ok, let alone work!
Let me know if that works :)
It seems that you have some common operations for parameters after it's bound from request.
You may consider to write custom parameter bindings to reuse the code. HongMei's blog is a good start point: http://blogs.msdn.com/b/hongmeig1/archive/2012/09/28/how-to-customize-parameter-binding.aspx
You may use the code in Scenario 2 to get the formatter binding to deserialize the model from body and perform the operations your want after that.
See the final step in the blog to specify the parameter type you want customize.

Categories