I am testing lazy loading in a C# Console Application. For some reason Lazy loading is not working.
I have checked the LazyLoadingEnabled and ProxyCreationEnabled properties of the context.Configuration. They are true.
My property is virtual.
I have checked the other similar SO questions without success. I am not sure what might be happening.
This is my code (simplified to not show namespaces):
static void Main(string[] args) {
Models.DataContext dc = new Models.DataContext();
Console.WriteLine("Context Lazy {0}. Proxy Creation {1} ",
dc.Configuration.LazyLoadingEnabled,
dc.Configuration.ProxyCreationEnabled);
var grp = dc.Groups.FirstOrDefault();
Console.WriteLine("GroupId {1}, AttrSet is null = {0}",
grp.AttrSet == null , grp.Id);
var grp2 = dc.Groups.Include("AttrSet").FirstOrDefault();
Console.WriteLine("GroupId {1}, AttrSet is null = {0}",
grp2.AttrSet == null, grp2.Id);
}
class Group {
public System.Guid Id { get; set; }
public string Name { get; set; }
public virtual AttrSet AttrSet { get; set; }
}
class AttrSet {
public System.Guid Id { get; set; }
public string Name { get; set; }
}
The output of running this is:
Context Lazy True. Proxy Creation True
GroupId 186ebc8a-dec7-4302-9f84-5a575577baac, AttrSet is null = True
GroupId 186ebc8a-dec7-4302-9f84-5a575577baac, AttrSet is null = False
I am sure that the loaded record is correct and it has a proper AttrSet in the database.
Any ideas?
Update
I created a very simple testing project in case any one actually wants to look at the code.
See: https://bitbucket.org/josea/eflazy (GIT: https://josea#bitbucket.org/josea/eflazy.git).
Proxy generation is not occurring. Why?? Because your POCOs are PRIVATE!! EF can't see them to derive proxies from them. Make your POCOs public and it'll work the way you expect.
public class Group
Are you using anything to configure the 1:1 relationship between the 2 classes? Because it doesn't look like you are here, which would cause Entity Framework to not be able to load the relationship.
You can use Data Annotations to define the FK relationship as so:
public class AttrSet {
public System.Guid Id { get; set; }
public string Name { get; set; }
[Required, ForeignKey("MyGroup")]
public int GroupID { get; set; }
public virtual Group MyGroup { get; set; }
}
This should give you the relationship you wanted. AttrSetId is whatever you've named the FK column in your table, so change that if it is different.
public class Group {
public System.Guid Id { get; set; }
public string Name { get; set; }
public System.Guid AttrSetId {get;set;}
[ForeignKey("AttrSetId")]
public virtual AttrSet AttrSet { get; set; }
}
Edit:
Add this line to your AttrSet class:
public virtual ICollection<Group> Groups {get;set;}
Add this next line to your OnModelCreating in your Models.DataContext. If for some reason you don't already have that function overridden, it'll look like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Group>().HasOptional(x => x.AttrSet).WithMany(x => x.Groups);
}
I put HasOptional instead of HasRequired as I assumed you could save a Group without an AttrSet. If that is not true, and the FK is not nullable, then you should use HasRequired.
Related
My first question - be kind :-).
In the code below, I am attempting to reference an "Include(d)" entity (Schedules) to obtain its Name property. In EF 6, both "schedule" and "schedule_2" return the correct value of Name. In EF Core, "schedule" returns NULL and "schedule_2" returns the correct value of Name.
I do not understand why I should have to load the "schedules" List. Shouldn't the .Include force an Eager Load of the Schedules for each Election such that each Election Schedule's Name property would be available for the "schedule" assignment?
// Relevant Model entities in database
// DbSet<Election> Elections { get; set; }
//
// The following are the related classes defined in the database context...
public class Election
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Schedule> Schedules { get; set; }
}
public class Schedule
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public int? CfsElectionId { get; set; }
public string Name { get; set; }
[Required] // sets cascade delete
[ForeignKey("CFSElectionID")]
public virtual Election Election { get; set; }
}
class Program
{
static void Main()
{
var db = new FfmsDbContext();
var elections = db.Elections
.Include(i => i.Schedules)
.ToList();
//The following returns NULL?
var schedule = elections.First().Schedules?.First().Name ?? "NULL";
var schedules = db.Schedules
.ToList();
//The following returns the correct Name property?
var schedule_2 = elections.First().Schedules?.First().Name ?? "NULL";
Console.WriteLine($#"sched: {schedule}");
Console.WriteLine($#"schedules.First().Name: {schedules.First().Name}");
Console.WriteLine($#"sched2: {schedule_2}");
Console.WriteLine("Done...");
Console.ReadLine();
}
}
/*
Output...
sched: NULL
schedules.First().Name: Candidates
sched2: Candidates
Done...
*/
Turns out that my problem ended up being in the References of the Class.
I had accidentally chosen System.Data.Entity as the offered choice for .Include.
The correct reference should have been Microsoft.EntityFrameworkCore.
Once I adjusted the reference, the .Include worked as desired.
Hi guys I have an issue with EF core version 6.4.4.
I am developing an .NET core (3.1) app that is multi-threaded (not the issue but just giving context) using MySQL (8.x) as database.
The issue that I am currently having is that my child row is getting removed when I update the parent row which doesn't cause a crash but it results in data loss as I am collecting data every second to create a chart.
I didn't have this issue initially but when I started adding foreign key constraints, indexes and refactored the code a bit for optimization I suddenly got this issue.
What I tried
So at first I thought that EF core set the parent as null when updating another entity or maybe that some required fields where null which resulted in a delete but this was not the case so I tried to use Z.EntityFramework.Extensions.EFCore for the ability to perform single updates (meaning that I will only update the given entity and not the referenced enitities aswell) but this didn't fix it.
I also checked all my indexes and foreignkeys to allow duplicates to make sure that I didn't delete a row because it had a duplicate key. All my indexes allowed duplicate rows (by setting unique false).
Classes
public class Parent
{
public int Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public ParentSatus Status { get; set; } //this is the property that I want to update
public decimal valueX{ get; set; }
public decimal valueY{ get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public decimal SomeValue{ get; set; }
public decimal SomeValue2{ get; set; }
public string IdForExternalAPI { get; set; }
public DateTime CreatedAt { get; set; }
public ChildStatus Status { get; set; }
public ChildType Type { get; set; }
public virtual Parent Parent { get; private set; }
public virtual int ParentId { get; set; } //This is for foreignkey constraint stuff see below
}
ModelBuilder
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>().HasOne(e => e.Parent).WithOne().HasForeignKey<Child>(e => e.ParentId).OnDelete(DeleteBehavior.Cascade).IsRequired();
modelBuilder.Entity<Parent>().HasIndex(e => e.SomeIndex).IsUnique(false);
modelBuilder.Entity<Child>().HasIndex(e => e.ParentId).IsUnique(false);
}
So obviously the relationship is a none-to-one which might be the cause but I don't want a one-to-one as the parent needs to get referenced by some other classes and the Child is also a parent for some other classes.
Can someone help with this?
I will add the answer if I find it some day.
Also some clarification: I did not add all the extra stuff as they have no part in this issue but will add it if it is necessary.
Edit:
Code where I update the parent
foreach (var parent in parentRepository.ReadParents(new Func<Parent, bool>(e => e.Status == Status.ONGOING)).ToList())
{
bool isStillOngoing = //Calculate the current status based on other entities
if (!isStillOngoing)
{
//Do some calculations here
}
parent.Status = isStillOngoing ? Status.ONGOING : Status.FINISHED;
//TODO: Bug here that deletes the child
parentRepository.UpdateParent(parent);
}
Ok I fixed it by turning the relationship around. Looks like I was too tired yesterday it should have been a none-to-many.
Fixed by changing the modelbuilder.
The fix
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>().HasMany<Child>().WithOne(e=> e.ParentId).HasForeignKey(e => e.ParentId).OnDelete(DeleteBehavior.Cascade).IsRequired();
modelBuilder.Entity<Parent>().HasIndex(e => e.SomeIndex).IsUnique(false);
modelBuilder.Entity<Child>().HasIndex(e => e.ParentId).IsUnique(false);
}
Thank you all for the help.
I am a beginner of EntityFramework. The codes below is extracted form my project.
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public virtual int UserType { get; set; }
}
public class Person : User
{
public override int UserType
{
get
{
return 0;
}
set
{
base.UserType = 0;
}
}
public string PersonName { get; set; }
public ICollection<Sunny.Models.WorkExperience> WorkExperiences { get; set; }
}
public class WorkExperience
{
[Key]
public int ExperienceId { get; set; }
public int PersonId { get; set; }
public string Job { get; set; }
[ForeignKey("PersonId")]
public Person Person { get; set; }
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
this.Map<User>(user => user.ToTable("User"));
this.Map<Person>(person => person.ToTable("Person"));
}
}
public class DbContext : System.Data.Entity.DbContext
{
public DbContext() : base("name=Model")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserConfiguration());
modelBuilder.Conventions.Remove<Conventions.PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
public DbSet<User> Users { get; set; }
public DbSet<Person> Persons { get; set; }
public DbSet<WorkExperience> WorkExperiences { get; set; }
}
static void Main(string[] args)
{
DbContext context = new Models.DbContext();
Person person = new Models.Person();
person.UserId = 1;
person.PersonName = "Name";
context.Persons.Add(person);
WorkExperience experience = new Models.WorkExperience();
experience.PersonId = 1;
experience.Job = "Coder";
context.WorkExperiences.Add(experience);
context.SaveChanges();
context = new DbContext();
Console.Write(context.WorkExperiences.First().Person == null);
Console.Read();
}
The running result of the Main method above is displaying true ,That is to say ,the value of the property WorkExperiences.Person always be null .But i have inserted data into the tables .
How to let the property WorkExperiences.Person load with the referenced key value ? Thanks in advance for any help.
Entity framework won't automatically load associated entities unless you specifically query for them.The reason is that it would be too easy to load far more than you expected if you always loaded all navigation properties - you might end up pulling most of your database back even on a simple query, if you have a lot of relationships. Imagine if you went to Amazon and it ran a query for your orders, which then included all products in those orders, which then included all sellers from those products, which then included all products from those sellers, ...
Entity Framework gives you several techniques to control when you want to load related data.
You can use DbExtensions.Include() to force it to include a related entity with the original query, which means one trip to the database:
Console.Write(context.WorkExperiences.Include(w => w.Person).First().Person == null);
Alternatively, you can use .Load() to force the load of an entity which isn't loaded:
var firstWE = context.WorkExperiences.First();
firstWE.Reference("Person").Load();
Console.Write(firstWE.Person == null);
Or you can enable lazy loading, which will make it load on demand the first time you access the property. You do this by adding virtual to it (which allows EF the ability to add some code to your property and load on demand):
public virtual Person Person { get; set; }
I have two tables in my DataBase, BUNTS, which contains information about pieces of steel
CREATE TABLE BUNTS (
BUNTCODE INTEGER NOT NULL,
BUNTNAME VARCHAR(20),
BUNTSTEEL INTEGER,
......
);
and POLL_WEIGHT_BUNTS, which contains information about operations that had been performed on each bunt
CREATE TABLE POLL_WEIGHT_BUNTS (
PWBCODE INTEGER NOT NULL,
PWBBUNTCODE INTEGER,
PWBDEPARTMENTFROM INTEGER,
PWBDEPARTMENTTO INTEGER
....
);
The relationship is one-to-many. I mapped those tables to models. Everything worked just fine.
Recently I've decided to add a field to table BUNTS which would reference to the last operation that had been performed on bunt:
BUNTLASTOPER INTEGER
Now my models look like this:
[Table("BUNTS")]
public class Bunt
{
[Key]
[Column("BUNTCODE")]
public int? Code { set; get; }
[Column("BUNTNAME")]
public string Name { set; get; }
[Column("BUNTSTEEL")]
public int? SteelCode { set; get; }
[Column("BUNTLASTOPER")]
public int? LastOperationID { set; get; }
[ForeignKey("LastOperationID")]
public BuntOperation LastOperation { set; get; }
public virtual ICollection<BuntOperation> Operations { set; get; }
}
[Table("POLL_WEIGHT_BUNTS")]
public class BuntOperation
{
[Key]
[Column("PWBCODE")]
public int? Code { set; get; }
[Column("PWBBUNTCODE")]
public int? BuntCode { set; get; }
[ForeignKey("BuntCode")]
public Bunt Bunt { set; get; }
[Column("PWBDEPARTMENTFROM")]
public int? DepartmentFromCode { set; get; }
.....
}
After I've made this, when I try to query Operations like this
return _context.Operations;
it generates an SQL-statement with new incorrect field Bunt_Code
SELECT
"B"."PWBCODE" AS "PWBCODE",
"B"."PWBBUNTCODE" AS "PWBBUNTCODE",
"B"."PWBDEPARTMENTFROM" AS "PWBDEPARTMENTFROM",
....
"B"."Bunt_Code" AS "Bunt_Code"
FROM "POLL_WEIGHT_BUNTS" AS "B"
I assume that now EF looks for a field that is a foreign key for BUNTS table, and cant find it. So it generates Bunt_Code field, which is missing in my database. But I already have a property Bunt in BuntOperation class, which references to BUNTS table. What am I missing?
UPDATE
seems like this solves my problem
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Bunt>().HasOptional(b => b.LastOperation).WithMany();
modelBuilder.Entity<Bunt>().HasMany(b => b.Operations).WithRequired(op => op.Bunt);
}
seems like this solves my problem
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Bunt>().HasOptional(b => b.LastOperation).WithMany();
modelBuilder.Entity<Bunt>().HasMany(b => b.Operations).WithRequired(op => op.Bunt);
}
I'm trying to figure out, how to implement navigation properties to my entities... But my navigation properties is always null:
I've set up two entities:
Entity 1 contains this lines:
public int Id { get; set; }
public ICollection<BestellterArtikel> BestellteArtikel { get; set; }
My second entity looks like this:
public int Id { get; set; }
public int BestellungId { get; set; }
public Bestellung BestellteArtikel { get; set; }
Further more I included this line to my overwritten OnModelCreating-Method:
modelBuilder.Entity<Bestellung>().HasMany(e => e.BestellteArtikel).WithRequired(e => e.Bestellung);
What have I done wrong? Have I forgotten something important? And does it has to be so complex? Do I have to add a line in my overwritten method for each property?
Here is my solution :
Entity 1:
public virtual ICollection<BestellterArtikel> BestellteArtikel { get; set; }
Entity 2:
public virtual Bestellung BestellteArtikel { get; set; }
Edited:
also you have to revise your mapping:
modelBuilder.Entity<Bestellung>().HasMany(e => e.BestellteArtikel).WithRequired(e => e.BestellteArtikel );
Instead of referring to BestellteArtikel property, you referred to type!
What do you mean by "always null"?
If you are talking about null values when you try to read them from DB,
then remember that you need to eagerly load the navigation properties when you query the context,
or use EF lazy-loading.
Read this for more information.