I have two POCO classes, each containing a collection of the other. I am attempting to configure a many-to-many relationship between these classes. However, when I load the context, EF does not populate any of the collections of each object. How can this be done automatically?
public class POCO1
{
public int ID { get; set; }
public virtual ICollection<POCO2> POCO2s { get; set; }
}
public class PCOO2
{
public int ID { get; set; }
public virtual ICollection<POCO1> POCO1s { get; set; }
}
public class POCOContext : DbContext
{
public DbSet<POCO1> POCO1s { get; set; }
public DbSet<POCO2> POCO2s { get; set; }
modelBuilder.Entity<POCO1>()
.HasKey(p => p.ID);
modelBuilder.Entity<POCO1>()
.HasMany(p => p.POCO2s).WithMany(p => p.POCO1s);
modelBuilder.Entity<POCO2>()
.HasKey(p => p.ID);
modelBuilder.Entity<POCO2>()
.HasMany(p => p.POCO1s).WithMany(p => p.POCO2s);
}
public class Test
{
static void Main(string[] args)
{
using (var context = new POCOContext())
{
context.Database.Initialize(true);
var p1 = context.POCO1s.Create();
var p2 = context.POCO2s.Create();
p1.POCO2s.Add(p2);
p2.POCO1s.Add(p1);
context.saveChanges();
}
// reload context from db
using (var context = new POCOContext())
{
context.POCO1s.ToList()[0].POCO2s.Count(); // == 0, but should be 1
context.POCO2s.ToList()[0].POCO1s.Count(); // == 0, but should be 1
}
}
}
Edit to include more info: from what I have read it should not be necessary to include annotations or even specify keys using Fluent API, and using these conventions EF 5.0 should configure the relations correctly. Indeed this appears to be the case, because the database tables are constructed correctly using code first (there is a POCO1 table, a POCO2 table, and an intersection table, all of which are populated correctly during testing). The problem is when reading the data back out, it does not seem to populate the many to many relationships. Other posts on SO and various tutorials and MSDN documentation suggest that using the proper conventions, lazy loading, and virtual collections should work, but that is not the behavior I'm seeing.
In my case the problem was due to disabled proxy creation on the context (ie context.Configuration.ProxyCreationEnabled = false;) in my actual code. The "automatic collection population" feature described in the original question is known as "lazy loading" and it requires proxy creation to be enabled. This MSDN article has more information.
Also, as mentioned in the original question, this feature works by convention without specifying keys or relationships (using annotations or fluent API).
Related
I have some models like those below:
public class Mutant
{
public long Id { get; set; }
...
// Relations
public long OriginalCodeId { get; set; }
public virtual OriginalCode OriginalCode { get; set; }
public int DifficultyLevelId { get; set; }
public virtual DifficultyLevel DifficultyLevel { get; set; }
}
and
public class OriginalCode
{
public long Id { get; set; }
...
// Relations
public virtual List<Mutant> Mutants { get; set; }
public virtual List<OriginalCodeInputParameter> OriginalCodeInputParameters { get; set; }
}
and in the OnModelCreating of DBContext I made the relations like these:
modelBuilder.Entity<Mutant>()
.HasOne(m => m.OriginalCode)
.WithMany(oc => oc.Mutants)
.HasForeignKey(m => m.OriginalCodeId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
modelBuilder.Entity<Mutant>()
.HasOne(m => m.DifficultyLevel)
.WithMany(dl => dl.Mutants)
.HasForeignKey(m => m.DifficultyLevelId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
now when I request for Mutants, the OriginalCode is null:
but as soon as I request for OriginalCodes like below:
then the OriginalCode field of the mutants will be not null:
What is the reason and how could I fix it?
The reason is explained in the Loading Related Data section of the EF Core documentation.
The first behavior is because EF Core currently does not support lazy loading, so normally you'll get null for navigation properties until you specifically load them via eager or explicit loading. However, the Eager loading section contains the following:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
which explains why the navigation property is not null in the second case.
Now, I'm not sure which of the two behaviors do you want to fix, so will try to address both.
The first behavior can be "fixed" by using one of the currently available methods for loading related data, for instance eager loading:
var mutants = db.Mutants.Include(m => m.OriginalCode).ToList();
The second behavior is "by design" and cannot be controlled. If you want to avoid it, make sure to use fresh new DbContext instance just for executing a single query to retrieve the data needed, or use no tracking query.
Update: Starting with v2.1, EF Core supports Lazy Loading. However it's not enabled by default, so in order to utilize it one should mark all navigation properties virtual, install Microsoft.EntityFrameworkCore.Proxies and enable it via UseLazyLoadingProxies call, or utilize Lazy-loading without proxies - both explained with examples in the EF Core documentation.
Using Package Manager Console install Microsoft.EntityFrameworkCore.Proxies
install-package Microsoft.EntityFrameworkCore.Proxies
And then in your Context class add .UseLazyLoadingProxies():
namespace SomeAPI.EFModels
{
public partial class SomeContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(connectionString);
}
}
}
}
I have two PostgreSQL tables, "stock_appreciation_rights" and "sar_vest_schedule" which are mapped to the classes "StockAppreciationRights" and "SarVestingUnit". The relationship is this: One SarVestingUnit is associated with one StockAppreciationRights, and one StockAppreciationRight is associated with many SarVestingUnit. Here is the SarVestingUnit class:
public class SarVestingUnit
{
public string UbsId { get; set; }
public short Units { get; set; }
public DateTime VestDate { get; set; }
public virtual StockAppreciationRights Sar { get; set; }
}
Here is the StockAppreciationRights class:
public class StockAppreciationRights
{
public StockAppreciationRights()
{
SarVestingUnits = new HashSet<SarVestingUnit>();
}
public string UbsId { get; set; }
public DateTime GrantDate { get; set; }
public DateTime ExpirationDate { get; set; }
public short UnitsGranted { get; set; }
public decimal GrantPrice { get; set; }
public short UnitsExercised { get; set; }
public virtual ICollection<SarVestingUnit> SarVestingUnits { get; set; }
}
And a snippet from my dbContext:
modelBuilder.Entity<SarVestingUnit>(entity =>
{
entity.HasKey(e => new { e.UbsId, e.VestDate })
.HasName("sar_vest_schedule_pkey");
entity.ToTable("sar_vest_schedule");
entity.Property(e => e.UbsId)
.HasColumnName("ubs_id")
.HasColumnType("character varying");
entity.Property(e => e.VestDate)
.HasColumnName("vest_date")
.HasColumnType("date");
entity.Property(e => e.Units).HasColumnName("units");
entity.HasOne(d => d.Sar)
.WithMany(p => p.SarVestingUnits)
.HasForeignKey(d => d.UbsId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("sar_vest_schedule_ubs_id_fkey")
.IsRequired();
});
Here is the behavior I don't understand. If I get just the StockAppreciationRights from my dbcontext like this:
var x = new personalfinanceContext();
//var test = x.SarVestingUnit.ToList();
var pgSARS = x.StockAppreciationRights.ToList();
The SarVestingUnits collection is empty in my StockAppreciationRights objects.
Likewise, if I just get the SarVestingUnits and not the StockAppreciationRights like this:
var x = new personalfinanceContext();
var test = x.SarVestingUnit.ToList();
//var pgSARS = x.StockAppreciationRights.ToList();
The Sar property in my SarVestingUnit objects is null.
However, if I get them both like this:
var x = new personalfinanceContext();
var test = x.SarVestingUnit.ToList();
var pgSARS = x.StockAppreciationRights.ToList();
Then everything is populated as it should be. Can someone explain this behavior? Obviously I'm new to entity framework and PostgreSQL.
From your described behaviour it seems that lazy loading is either disabled or not available (I.e. EF Core <=2.1)
Lazy loading would assign proxies for related references that it hasn't loaded so that if those are later accessed, a DB query against the DbContext would be made to retrieve them. This allows data to be retrieved "as required" but can result in a significant performance penalty.
Alternatively you can eager-load related data. For example:
var pgSARS = context.StockAppreciationRights.Include(x => x.SarVestingUnits).ToList();
would tell EF to load the StockAppreciation rights and pre-fetch any vesting units for each of those entries. You can use Include and ThenInclude to drill down and pre-fetch any number of dependencies.
The reason your final example appears to work is that EF will auto-populate relations that the context knows about. When you load the first set, it won't resolve any of the related entities, however, loading the second set and EF already knows about the related other entities and will auto-set those references for you.
The real power of EF though is not dealing with entities like a 1-to-1 mapping to tables (outside of editing/inserting) but rather leveraging projection to pull relational data to populate what you need. Leveraging Select or Automapper's ProjectTo methods mean you can extract whatever data you want through the mapped EF relationships and EF can build you an efficient query to fetch it rather than worrying about lazy or eager loading. For instance if you want to get a list of vesting units with their associated right details:
var sars = context.StockAppreciationRights.Select(x => new SARSummary
{
UbsId = x.UbsId,
Units = x.Units,
VestDate = x.VestDate,
GrantDate = x.Sar.GrantDate,
ExpirationDate = x.Sar.ExpirationDate,
UnitsGranted = x.Sar.UnitsGranted,
GrantPrice = x.Sar.GrantPrice
}).ToList();
From within the Select statement you can access any of the related details to flatten into a view model that serves your immediate needs, or even compose a hierarchy of view models simplified for what the view/consumer needs.
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
We have an application with a fairly complex entity model where high-performance and low-latency are essential but we have no need for horizontal scalability. The application has a number of event sources in addition to a self-hosted ASP.NET Web API 2. We use Entity Framework 6 to map from POCO classes to the database (we use the excellent Reverse POCO Generator to generate our classes).
Whenever an event arrives the application has to make some adjustment to the entity model and persist this delta adjustment to the database via EF. At the same time read or update requests may arrive via the Web API.
Because the model involves many tables and FK relationships, and reacting to an event usually requires all relationships under the subject entity to be loaded, we have elected to maintain the entire set of data in an in-memory cache rather than load the entire object graph for each event. The image below shows a simplified version of our model:-
At program start-up we load all the interesting ClassA instances (and their associated dependency graph) via a temporary DbContext and insert into a Dictionary (ie our cache). When an event arrives we find the ClassA instance in our cache and attach it to a per-event DbContext via DbSet.Attach(). The program is written using the await-async pattern throughout and multiple events can be processed at the same time. We protect the cached objects from being accessed concurrently by the use of locks so we guarantee that a cached ClassA can be loaded into a DbContext only one-at-a-time. So far so good, the performance is excellent and we are happy with the mechanism. But there is a problem. Although the entity graph is fairly self-contained under ClassA, there are some POCO classes representing what we consider to be read-only static-data (shaded in orange in the image). We have found that EF sometimes complains
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
when we attempt to Attach() two different instances of ClassA at the same time (even though we are attaching to different Dbcontexts) because they share a reference to the same ClassAType. This is demonstrated by the code snippet below:-
ConcurrentDictionary<int,ClassA> theCache = null;
using(var ctx = new MyDbContext())
{
var classAs = ctx.ClassAs
.Include(a => a.ClassAType)
.ToList();
theCache = new ConcurrentDictionary<int,ClassA>(classAs.ToDictionary(a => a.ID));
}
// take 2 different instances of ClassA that refer to the same ClassAType
// and load them into separate DbContexts
var ctx1 = new MyDbContext();
ctx1.ClassAs.Attach(theCache[1]);
var ctx2 = new MyDbContext();
ctx2.ClassAs.Attach(theCache[2]); // exception thrown here
Is there any way to inform EF that ClassAType is read-only/static and we don't want it to ensure that each instance can be loaded into only one DbContext? So far the only way around the problem I've found is to modify the POCO generator to ignore these FK relationships, so they are not part of the entity model. But this complicates the programming because there are processing methods in ClassA which need access to the static data.
I think the key to this question is what exactly the exception means:-
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
It occurred to me that perhaps this exception is Entity Framework complaining that an instance of an object has been changed in multiple DbContexts rather than simply being referenced by objects in multiple DbContexts. My theory was based on the fact that the generated POCO classes have reverse FK navigation properties, and that Entity Framework would naturally attempt to fix-up these reverse navigation properties as part of the process of attaching the entity graph to the DbContext (see a description of the fix-up process)
To test out this theory I created a simple test project where I could enable and disable the reverse navigation properties. To my great joy I discovered that the theory was correct, and that EF is quite happy for the objects to be referenced multiple times so long as the objects themselves don't change - and this includes navigation properties being changed by the fix-up process.
So the answer to the question is simply follow 2 rules:-
Ensure the static data objects are never changed (ideally they should have no public setter properties) and
Do not include any FK reverse navigation properties pointing back to the referring classes. For users of the Reverse POCO Generator I have made a suggestion to Simon Hughes (the author) to add an enhancement making this a configuration option.
I've included the test classes below:-
class Program
{
static void Main(string[] args)
{
ConcurrentDictionary<int,ClassA> theCache = null;
try
{
using(var ctx = new MyDbContext())
{
var classAs = ctx.ClassAs
.Include(a => a.ClassAType)
.ToList();
theCache = new ConcurrentDictionary<int,ClassA>(classAs.ToDictionary(a => a.ID));
}
// take 2 instances of ClassA that refer to the same ClassAType
// and load them into separate DbContexts
var classA1 = theCache[1];
var classA2 = theCache[2];
var ctx1 = new MyDbContext();
ctx1.ClassAs.Attach(classA1);
var ctx2 = new MyDbContext();
ctx2.ClassAs.Attach(classA2);
// When ClassAType has a reverse FK navigation property to
// ClassA we will not reach this line!
WriteDetails(classA1);
WriteDetails(classA2);
classA1.Name = "Updated";
classA2.Name = "Updated";
WriteDetails(classA1);
WriteDetails(classA2);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
System.Console.WriteLine("End of test");
}
static void WriteDetails(ClassA classA)
{
Console.WriteLine(String.Format("ID={0} Name={1} TypeName={2}",
classA.ID, classA.Name, classA.ClassAType.Name));
}
}
public class ClassA
{
public int ID { get; set; }
public string ClassATypeCode { get; set; }
public string Name { get; set; }
//Navigation properties
public virtual ClassAType ClassAType { get; set; }
}
public class ClassAConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<ClassA>
{
public ClassAConfiguration()
: this("dbo")
{
}
public ClassAConfiguration(string schema)
{
ToTable("TEST_ClassA", schema);
HasKey(x => x.ID);
Property(x => x.ID).HasColumnName(#"ID").IsRequired().HasColumnType("int").HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.Name).HasColumnName(#"Name").IsRequired().HasColumnType("varchar").HasMaxLength(50);
Property(x => x.ClassATypeCode).HasColumnName(#"ClassATypeCode").IsRequired().HasColumnType("varchar").HasMaxLength(50);
//HasRequired(a => a.ClassAType).WithMany(b => b.ClassAs).HasForeignKey(c => c.ClassATypeCode);
HasRequired(a => a.ClassAType).WithMany().HasForeignKey(b=>b.ClassATypeCode);
}
}
public class ClassAType
{
public string Code { get; private set; }
public string Name { get; private set; }
public int Flags { get; private set; }
// Reverse navigation
//public virtual System.Collections.Generic.ICollection<ClassA> ClassAs { get; set; }
}
public class ClassATypeConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<ClassAType>
{
public ClassATypeConfiguration()
: this("dbo")
{
}
public ClassATypeConfiguration(string schema)
{
ToTable("TEST_ClassAType", schema);
HasKey(x => x.Code);
Property(x => x.Code).HasColumnName(#"Code").IsRequired().HasColumnType("varchar").HasMaxLength(12);
Property(x => x.Name).HasColumnName(#"Name").IsRequired().HasColumnType("varchar").HasMaxLength(50);
Property(x => x.Flags).HasColumnName(#"Flags").IsRequired().HasColumnType("int");
}
}
public class MyDbContext : System.Data.Entity.DbContext
{
public System.Data.Entity.DbSet<ClassA> ClassAs { get; set; }
public System.Data.Entity.DbSet<ClassAType> ClassATypes { get; set; }
static MyDbContext()
{
System.Data.Entity.Database.SetInitializer<MyDbContext>(null);
}
const string connectionString = #"Server=TESTDB; Database=TEST; Integrated Security=True;";
public MyDbContext()
: base(connectionString)
{
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ClassAConfiguration());
modelBuilder.Configurations.Add(new ClassATypeConfiguration());
}
}
I think this might work: try using AsNoTracking in those entities DbSets when selecting them at program start-up:
dbContext.ClassEType.AsNoTracking();
This will disable the change tracking for them, so EF will not try to persist them.
Also, the POCO class for those entities should only have read-only properties (with no set method).
I have a exceedingly simplistic data model (below). I am having trouble figuring out how I am to get my navigational properties to load from the database. I have no trouble getting them in, but the navigational property does not get set by EF it appears. I have seen several related questions, but they are slightly different or rather involved. I am looking for information on how navigational properties are treated by EF 4.2 (POCO). In the reading I've done, I got the impression that I would be able to access objects with foreign keys using navigational properties. Instead, my properties are coming back as either null or empty depending on if I instantiate my collection in the constructor.
public class AnimalDb : DbContext
{
public static AnimalDb Create(string fileName)
{
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
return new AnimalDb(fileName);
}
private AnimalDb(string fileName) : base(fileName) { }
public DbSet<Animal> Animals { get; set; }
}
public class Animal
{
public Animal()
{
Id = Guid.NewGuid();
Traits = new ObservableCollection<Trait>();
}
public Guid Id { get; set; }
public string Species { get; set; }
public string Name { get; set; }
public ObservableCollection<Trait> Traits { get; set; }
}
public class Trait
{
public Trait()
{
Id = Guid.NewGuid();
}
public Guid Id { get; set; }
public string Name { get; set; }
}
And here is some (simple) code that uses it:
foreach (var animal in db.Animals)
{
foreach (var trait in animal.Traits)
{
//animal.Traits count is 0, so this does not run.
//However there are traits in the database, as my populate
//function is working fine.
Console.WriteLine("{0} is {1}", animal.Name, trait.Name);
}
}
----Edit Answer Summary----
Using the article and information provided in the answers below, I was able to discover I could either eagerly load using db.Animals.Include() or enable lazy loading. There is a trick to enabling lazy loading and being able to use it though. First to enable lazy loading I added:
db.Configuration.LazyLoadingEnabled = true;
Next I changed my Traits collection in the following manner:
public virtual ObservableCollection<Trait> Traits { get; set; }
Making it virtual allows the automatically generated proxy to lazily load Traits. That's it! IMHO I think the MSDN docs should shout this load and clear in the POCO EF 4.2 coding conventions. Again thanks for the help.
There are a few reasons that your wire-up methods may appear to have no data. To load related data you need to :
explicity load the data
meet the lazy loading requirements, or
use eager loading using Include()
My guess is that you turned off the virtual proxies. There is more on the requirements here:
http://msdn.microsoft.com/en-us/library/dd456855.aspx
If you don't use lazy loading you have to explicitly tell EF to load the relation with the Include method:
foreach (var animal in db.Animals.Include(a => a.Traits))
{
foreach (var trait in animal.Traits)
{
//...
}
}
You can read more about eager loading in this article.