I have a simple model which when I run the website in the debugger the entity framework does not correctly populate the model.
The model is simple:
public class Team
{
/// <summary>
/// Constructor required for the EntityFramework to create the object.
/// </summary>
private Team()
{
}
public Team(string name, ApplicationUser owner)
{
Name = name;
Owner = owner;
}
[Required]
public int Id { get; private set; }
[Required]
public string Name { get; private set; }
[Required]
public ApplicationUser Owner { get; private set; }
[Required]
public List<TeamMembership> Members { get; set; }
}
public class TeamMembership
{
/// <summary>
/// Constructor for the EntityFramework
/// </summary>
private TeamMembership()
{
}
public TeamMembership(ApplicationUser user, MembershipStatus status)
{
User = user;
Status = status;
}
[Required]
public ApplicationUser User { get; private set; }
[Required]
public MembershipStatus Status { get; set; }
[Required]
public int Id { get; private set; }
}
Where ApplicationUser is the default class generated by the ASP MVC 5 membership infrastructure.
When I run my tests (Specflow), which create a new LocalDb database with a unique id for each test and runs the migrations on that db, then the entity framework correctly populates my Team and the Owner.
However when I run the website and try and interact with the application then my Team is not fully populated, because the Owner is null and Members is not populated. But the owner ID is set correctly in the database and the query seems ok. The queries executed during the test and the running of the app seem the same.
Why might this be and how might I start to debug the issue?
I feel like I am missing something simple.
You probably need to add .Include() to your query, which isn't shown in your question, but should look something like this:
var query = context.Teams.Include(x => x.Owner).Include(x => x.Members).Where( ... );
EF will only load the top-level entities, but not the entities referenced by it until they're needed (lazy loading). If you haven't disposed of the context, simply attempting to access those navigation properties should cause them to load from the database, but this won't happen if the context has been disposed of.
.Include() will inform EF that those navigation properties need to be eagerly loaded along with the referencing entity.
See this MSDN page for more detail about lazy/eager loading and how to control it.
Related
I have a simple model for the purposes of this post.
Two entities Role and Person.
public class Role : Entity
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> PeopleWithThisRole { get; set; }
}
public class Person : Entity
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Guid? RoleId { get; set; }
}
If I get the Roles from the EF context, then the PeopleWithThisRole collection is empty (unless I .Include them. As expected.
However if I get the Roles and I then get the People as below
var roles = _context.Roles.ToList();
var people = _context.People.ToList();
Then roles.PeopleWithThisRole collection is fully populated with the people without having to .Include it.
Is this the expected behaviour or should I raise this as a bug?
Thanks
UPDATE
With many thanks to #hvd below, I have decided to keep the entities clean and not use [JsonIgnore] attribute and instead map to DTOS (which exclude those properties I don't need in the JSON) - which is probably the correct way!
It's expected and also how earlier versions of EF worked.
Your _context keeps track of entities loaded inside that context, to allow for saving changes. Inside that context, Person objects have been loaded (at your request) and their RoleId values are known. Inside that same context, Role objects with those same Id values have been loaded (also at your request). EF links the objects in memory based on those IDs. If you trace the SQL queries sent to the server, you should find that no queries have been sent other than those that you requested.
I generated Entity Model from my database in my MVC5 App.
When I try to add [DispalyName] to some properties it works fine, but after some time app refreshes this class by itself and removes all my custom code
public partial class Patient
{
public Patient()
{
this.PatientDetails = new HashSet<PatientDetail>();
}
public int PatientID { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
public string FirstName { get; set; }
public virtual ICollection<PatientDetail> PatientDetails { get; set; }
}
Why MVC does it and how to disable that?
I believe since you're using Database first, the entities will get completely re-created every time you refresh, thus you lose your custom attributes.
Also, to go off of Joe's comment, you should make a view model and put your [Display] attributes there and not directly on the entity.
I am trying to figure out a way to develop a database model using Entity Framework that does not do updates or deletes. The business requirements want the complete history of all changes that are made to each record in the system, for analysis reasons. So instead I want to always modify by inserting a new record to the database.
Is there a clean way to get Entity Framework to do that? Or am I going to be jumping through a lot hoops to get this sort of behavior. The basic model is pretty simple, some stuff, like constructors, removed since they don't add much to the discussion:
public class Container
{
public Guid Id { get; private set; }
public ICollection<Container> RelatedContainers { get; private set; }
public ICollection<Item> Items { get; private set; }
}
public class Item
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public string Value { get; private set; }
}
Basically you need to override SaveChanges() method in DbContext. In your method get all the objects that have the EntityState Deleted or Modified and set the status UnChanged.
public class YourDbContext:DbContext{
public override int SaveChanges(){
foreach ( var ent in this.ChangeTracker
.Entries()
.Where(p =>p.State == System.Data.EntityState.Deleted
p.State == System.Data.EntityState.Modified))
{
ent.State =System.Data.EntityState.Unchanged;
}
}
}
I am working with EF 4.3 code first and using WCF Data services (5.0 just released) to expose the data over HTTP
I find that if I call a service operation from a browser I am able to get related entities back, but when I consume the service operation in a client app I am not getting back the related entities.
I have been researching this isuse it seems the EF enables lazy loading whn using the virtual key word when referencing a an ICollection, this some how prevents WCF data services from returing realted entities - is this true
If i browse locally and put a break point on my getUsersByName method I can see the related group entity but when it comes over the wire to a client app the gropup entity is missing.
Is there a configuration to enable this.
Thanks
eg
public partial class Group
{
public Group()
{
this.Users = new HashSet<User>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int GroupID { get; set; }
[Required(ErrorMessage = "Please enter a group name")]
[StringLength(50, ErrorMessage = "The group name is too long")]
public string GroupName { get; set; }
[Required]
public System.DateTime CreatedDate { get; set; }
[Required]
public bool Admin { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public partial class User
{
public User()
{
this.Groups = new HashSet<Group>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Required(ErrorMessage="Please enter a username")]
[StringLength(50,ErrorMessage="Username is too long")]
public string UserName { get; set; }
[Required(ErrorMessage="Please enter an email address")]
[RegularExpression(".+\\#.+\\..+",ErrorMessage="Please enter a valid email address")]
public string Email { get; set; }
[Required]
public System.DateTime CreatedDate { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
public partial class TestContext : DbContext
{
public Test()
: base("name=TestEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Tell Code First to ignore PluralizingTableName convention
// If you keep this convention then the generated tables will have pluralized names.
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<Group> Groups { get; set; }
public DbSet<User> Users { get; set; }
}
[ServiceBehavior(IncludeExceptionDetailInFaults=true)]
[JSONPSupportBehavior]
public class TestSVC : DataService<TestContext>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
config.SetServiceActionAccessRule("*", ServiceActionRights.Invoke);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
config.UseVerboseErrors = true;
}
[WebGet]
public User GetUserByName(string userName)
{
var user = (from u in this.CurrentDataSource.Users
where u.UserName == userName
select u).FirstOrDefault();
return user;
}
Actually regardless of what you do on the server side with the entity WCF DS will not return expanded entities by default. It's a feature. The reason is size of the message on the wire.
The client can request this behavior though (and that should work without making any modifications to the EF entities on the server side).
Let's assume you have a service operation GetProducts. You can issue a query like ~/GetProducts?$expand=Category which will include the Category entities in the result.
You also noted that the client doesn't get to see these, but you do see them in the browser. So are you already using the $expand? If that's the case the problem is solely on the client. Make sure that you request the results using the $expand on the client as well (depends on what code you're using, there's an Expand method in the LINQ on the client for this). And then you can use Fiddler to see if the client actually gets the results back the way you want. If that's the case and you still don't get the results in your client code, it might be due to MergeOptions. Try setting DataServiceContext.MergeOption to OverwriteChanges and try again (but be sure that you're aware of what this setting does).
Try removing the initialization of the navigation properties in the constructor because it will create problems with the proxy objects.
It's almost impossible to use lazy loading with serialization because when the serializer access the navigation properties the related entities will be loaded. This will lead to load whole database. So you need to disable lazy loading and use Include to load whatever you want or you can use some DTO with lazy loading enabled .
I'm learning EF Code First and am having trouble when updating existing records. I've boiled it down to this simple example:
This works:
using(var db = new DataContext()){
var p = db.People.Find(1);
p.Name="New Name";
Console.WriteLine(p.Gender.Name); //<--Unnecessary property access
db.SaveChanges(); //Success
}
...but this fails (when the WriteLine is removed):
using(var db = new DataContext()){
var p = db.People.Find(1);
p.Name="New Name";
db.SaveChanges(); //DbValidationError "Gender field is required."
}
Why do I have to access/load the Gender propery if I'm not using it and the data is already correctly stored in the database? I just want to change the Name on an existing record. In this example, Gender is a one-to-many association stored as Gender_Id in the People table. The classes are defined like this:
public class Person
{
[Key]
public int PersonId { get; set; }
[Required, MaxLength(50)]
public string Name { get; set; }
[Required, Column("Gender")]
virtual public GenderCode Gender { get; set; }
}
public class GenderCode
{
[Key]
public int Id { get; set; }
[Required, MaxLength(10)]
public string Name { get; set; }
}
public class DataContext:DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<GenderCode> GenderCodes { get; set; }
}
Of course, the fully defined classes are to have many more fields. I'd rather not have to access every dependant property every time I want to modify an unrelated value.
Is there a way to load an object, change a field, and save it without loading all related objects first?
Yes, this is necessary because of some horrible design mistakes in EF.
Check out my similar question, EF: Validation failing on update when using lazy-loaded, required properties
One trick is declaring FK properties along with the OO relations:
[ForeignKey("GenderId"), Column("Gender")]
virtual public GenderCode Gender { get; set; }
[Required]
public int GenderId { get; set; }
It is because you are using data annotations and Required attribute has also meaning for validation. Once you set navigation property as Required by data annotation it must be filled / loaded when you are going to persist entity to the database.