Entity framework not updating self-referenced key - sql-server

I've got my entity class (which is part of my Entity Framework Code First DbContext) like this:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Person Boss { get; set; }
public decimal Piotrus { get; set; }
}
Now I'm trying to update persons boss like this:
using(var db = new DataContext())
{
var employee = db.Persons.Single(p => p.Name = "Kowalski");
employee.Boss = db.Persons.Single(p => p.Name = "Malysz");
db.Entry(employee).State = State.Modified;
db.SaveChanges();
}
While debugging entity seems to be ok. It have right Boss assigned. Even when I tried to get data from current DbContext it looks fine, but in Database property Boss_Id stays still unchanged, while other properties (if were changed) has been modified.
Also everything works okay when I'm creating a person. Assigned boss is stored correctly.
What can cause this problem?

Try commenting out db.Entry(employee).State = State.Modified;

Related

Entity Framework Core one to many related data navigation property is always empty

I feel like I'm missing something obvious here; I'm using .Net 5 with Entity Framework Core. The problem is that the foreign key is correct, but the associated navigation property is always empty and has no data. Do I have to do something with the fluent framework, or do something special with my includes?
I have 3 simplified entities and a database context method in this example, the project is much too large to include entirely. In the method, CalendarEvents is a DbSet:
public class CalendarEvent: IJsonSerializable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
/// <summary>
/// Gets the personnel associated with this event
/// </summary>
public virtual List<SchedulePerson> SchedulePeople { get; set; } = new List<SchedulePerson>();
}
public class SchedulePerson : IJsonSerializable, ICloneable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
public virtual CalendarEvent AssociatedCalendarEvent { get; set; }
}
public class Employee : IJsonSerializable
{
[Key]
public int Id { get; set; }
public virtual List<SchedulePerson> AssociatedSchedulePeople { get; set; } = new List<SchedulePerson>();
}
public class DbContext : DbContext
{
public DbSet<CalendarEvent> CalendarEvents { get; set; }
public DbSet<SchedulePerson> SchedulePeople { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbContext(DbContextOptions<VibrationContext> options): base(options)
{
}
public CalendarEvent GetEvent(int calendarEventId)
{
var currentCalEvent = this.CalendarEvents.Where(x => x.Id == calendarEventId);
var dummy1 = currentCalEvent.FirstOrDefault();
var dummy2 = currentCalEvent.Include(calEvent => calEvent.SchedulePeople).ToList();
var dummy3 = currentCalEvent.Include(calEvent => calEvent.SchedulePeople).ThenInclude(people => people.Employee).ToList();
return currentCalEvent.FirstOrDefault();
}
}
In this case Employee is the associated navigation property, and the associated key EmployeeId, is correct. For the moment I've added extra logic that will populate each employee per schedule person separately, manually based on the foreign key EmployeeId, when I get the event, but I'd rather not have to add logic like that each time. If that's something unavoidable then that's fine, but I'd like to do things properly and let Entity Framework Core handle as much as possible.
For additional context:
SchedulePerson -> CalendarEvent is a many-to-one relationship
Employee -> SchedulePerson is a many-to-one relationship
In other words a calendar event can contain many schedule persons, but a schedule person can only be associated with one calendar event.
Employee can be associated with many schedule persons, but each schedule person is only associated with one employee.
There should only be one employee for each real person, but there can be multiple SchedulePersons for each real person.
Thank you for your help and let me know if there is any more information I can provide.
Also if anything else looks bad or wrong in these code snippets please let me know.
Edit, this is what I have to do if I want to get the employees in my request:
private void UpdateEmployeeContents(CalendarEvent calendarEvent)
{
foreach (SchedulePerson person in calendarEvent.SchedulePeople)
{
person.Employee = this.Employees.Where(x => x.Id == person.EmployeeId).FirstOrDefault();
}
}

Cannot Insert new Data in the Database in .NET Core API error Database operation

I am working on an API and when I started adding new data. I received this error. It was working when I manually add the ID every input but now I got this error and after adding some solutions from here its still not working.
Error:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s).
Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
Code for insert:
public bool Insert(string UserName, SendInventoryModel sendInventoryModel)
{
using (DatabaseContext context = new DatabaseContext())
{
bool flag = false;
// Create new
InventoryEntity inventoryEntity = new InventoryEntity
{
UserName = sendInventoryModel.UserName,
Item = sendInventoryModel.Item ,
};
context.Table.Add(inventoryEntity);
context.SaveChanges();
// Check
var model = CheckUserNameID(UserName, sendInventoryModel.Item);
var data = context.Table.Find(model.Id);
if (null != data)
{
flag = true;
}
return flag;
}
}
SendInventoryModel:
public class SendSiteMailModel
{
[Required]
public string UserName { get; set; }
[Required]
public string Item{ get; set; }
}
InventoryController:
[HttpPost("{username}")]
[Authorize]
public JObject Post([Required] string UserName, [FromBody] SendInventoryModel sendInventoryModel)
{
ResponseModel x = new ResponseModel();
try
{
InventoryRepository InventoryRepository = new InventoryRepository();
bool isSuccess = InventoryRepository.Insert(UserName, sendInventoryModel);
}
catch (Exception error)
{
// if not successful
}
return Json(x);
}
I already added [DatabaseGenerated(DatabaseGeneratedOption.Identity)] in my InventoryEntity and InventoryModel.
InventoryEntity:
[Key]
DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
InventoryModel:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
I also added the below code in my DBContext.cs:
public virtual DbSet<OtherTableEntity> Table{ get; set; }
public virtual DbSet<InventoryEntity> Table{ get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OtherTableEntity>();
modelBuilder.Entity<InventoryEntity>().Property(x => x.Id).ValueGeneratedOnAdd();
base.OnModelCreating(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
Add finally my table design: Inventory ID:
(Is Identity) = Yes
Identity Increment = 1
Identity Seed = 1
Note that there is no Primary Key in the Inventory table. And its an old table with existing data. The current database was migrated from membership to identity.
After all the things that I have added the context.SaveChanges(); in the insert method still does not work. Any ideas or suggestion on how to fix this problem?
Note: I've changed the table entity names and models per Asherguru suggestion since its kinda confusing and generic.
Are your TableEntity and Table in database same table names?
Why different names - TableEntity and Table?
Try to add [Table("YourTableNameInDatabase")] in TableEntity class. Then EF can find actual table in database and insert into this table.
[Table("YourTableNameInDatabase")]
public partial class TableEntity
{
public int Id { get; set; }
}
It would be less confusing if you show actual table names with some necessary screenshots.

EF Core wrong join statement

I have the following model classes in EF Core 2.2
public class User
{
[Key]
public long Id { get; set; }
[ForeignKey("Post")]
public long? PostId { get; set; }
public virtual Post Post { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
[Key]
public long Id { get; set; }
[ForeignKey("User")]
public long UserId { get; set; }
public virtual User User { get; set; }
}
I have checked the relations with SSMS and they are fine.
But when I use
dbContext.Posts.Include(p => p.User);
EF Core generates the following join statement
FROM Posts [p]
LEFT JOIN Users [p.Users] ON [p].[Id] = [p.Users].[PostId]
I'm including User from Post and expect it to be as below
FROM Posts [p]
LEFT JOIN Users [p.Users] ON [p].[UserId] = [p.Users].[Id]
What is wrong with models?
Assume that I want to save last PostId in User model.
Is there an attribute to tell ef core which property to use when joining models?
From the discussion on the other response it looks like you want a User to contain Posts, but then also have the User track a reference to the Latest post. EF can map this, however you will probably need to be a bit explicit about the relationships.
For instance:
[Table("Users")]
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
public virtual Post LatestPost { get; set; }
}
[Table("Posts")]
public class Post
{
[Key]
public int PostId { get; set; }
public string PostText { get; set; }
public DateTime PostedAt { get; set; }
public virtual User User { get; set; }
}
then a Configuration to ensure EF wires up the relationship between user and posts correctly:
// EF6
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasMany(x => x.Posts)
.WithRequired(x => x.User)
.Map(x=>x.MapKey("UserId"));
HasOptional(x => x.LatestPost)
.WithMany()
.Map(x=>x.MapKey("LatestPostId"));
}
}
// EFCore
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasMany(x => x.Posts)
.WithOne(x => x.User)
.HasForeignKey("UserId");
HasOne(x => x.LatestPost)
.WithMany()
.IsRequired(false)
.HasForeignKey("LatestPostId");
}
}
You can accomplish this in the OnModelCreating event with the modelBuilder reference as well. Note here I am not declaring FK properties in my entities. This too is an option, but I generally recommend not declaring FKs to avoid reference vs. FK update issues. I've named the LatestPost FK as LatestPostId just to reveal a bit more accurately what it is for. It could be mapped to a "PostId" if you so choose.
Now lets say I go to add a new post and I want to associate it to the user, and assign it as the LatestPost for that user:
using (var context = new SomethingDbContext())
{
var user = context.Users.Include(x => x.Posts).Include(x => x.LatestPost)
.Single(x => x.UserId == 1);
var newPost = new Post { PostText = "Test", User = user };
user.Posts.Add(newPost);
user.LatestPost = newPost;
context.SaveChanges();
}
You can update the "latest" post reference by loading the user and setting the LatestPost reference to the desired post.
There is a risk with this structure however that you should consider. The issue is that there is no way to reliably enforce (at a data level) that the LatestPost reference in a User actually references a post associated to that user. For instance, if I have a latest post pointing to a particular post, then I delete that post reference from the user's Posts collection, that can result in FK constraint errors, or simply disassociate the post from the user, but the user latest post still points at that record. I can also assign another user's post to this user's latest post reference. I.e.
using (var context = new SomethingDbContext())
{
var user1 = context.Users.Include(x => x.Posts).Include(x => x.LatestPost)
.Single(x => x.UserId == 1);
var user1 = context.Users.Include(x => x.Posts).Include(x => x.LatestPost)
.Single(x => x.UserId == 2);
var newPost = new Post { PostText = "Test", User = user1 };
user1.Posts.Add(newPost);
user1.LatestPost = newPost;
user2.LatestPost = newPost;
context.SaveChanges();
}
And that would be perfectly fine. User 2's "LatestPostId" would be set to this new post, even though this post's UserId only refers to User1.
A better solution when dealing with something like a Latest post is to not denormalize the schema to accommodate it. Instead, use unmapped properties in the entity for the latest post, or better, rely on projection to retrieve this data when it's needed. In both cases you would remove the LatestPostId from the User table
Unmapped property:
[Table("Users")]
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
[NotMapped]
public Post LatestPost
{
get { return Posts.OrderByDescending(x => x.PostedAt).FirstOrDefault(); }
}
}
The caveat of the unmapped property approach is that you need to remember to eager-load Posts on the User if you want to access this property, otherwise you will trip a lazy load. You also cannot use this property in Linq expressions that get sent to EF (EF6) though they may work with EFCore, but risk performance issues if the expression gets translated to in-memory early. EF will not be able to translated LatestPost to SQL since there would be no key in the schema.
Projection:
[Table("Users")]
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}
Then if you want to retrieve a user and it's latest post:
var userAndPost = context.Users.Where(x => x.UserId == userId)
.Select(x => new { User = x, LatestPost = x.Posts.OrderByDescending(PostedAt).FirstOrDefault()} ).Single();
Projection with Select can retrieve entities of interest, or better, simply return the fields from those entities into a flattened view model or DTO to send to UI or such. This results in more efficient queries against the database. Using Select to retrieve the details you don't need to worry about eager-loading via Include, and when done correctly, will avoid pitfalls with lazy loading.
The relationship between User and Post is wrong. This should be your model:
public class User
{
[Key]
public long Id { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
[Key]
public long Id { get; set; }
[ForeignKey("User")]
public long UserId { get; set; }
public virtual User User { get; set; }
}
That is one-to-many relationship: one user can have many posts and a post can have just one user.
You had enough of answers on this question to how to create the model , I will just highlight what's wrong in your class
You have craete postid in user model as FK and therefore it will always consider it as relationship between user and post , you have to remove that and design it something similar to what is said by steve

Null data returned using a 1:1 model

I have two SQL tables, User and UserType joined with UserType as a foreign key, with their respective models in ASP. To my understanding, this should be a 1:1 relationship (correct me if I'm wrong). One unique user, set as a type of user (being admin, super admin, user etc).
When I try and retrieve a list of users, it returns a null on the property UserType.
I used Google to get this far, but I'm struggling to get this particular issue fixed.
At one point I got an error stating: "Unable to determine the principal end of an association". To get around that, I included a Required annotation (didn't work) and a ForeignKey annotation (didn't work either) in both models, both simultaneously and separately.
This is what I have so far.
[Table("Users", Schema = "dbo")]
public class Users
{
[Key, ForeignKey("UserType")]
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ContactNumber { get; set; }
public UserType UserType { get; set; }
public string IsActive { get; set; }
}
[Table("UserType", Schema = "dbo")]
public class UserType
{
[Key]
public Guid Id { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public string IsActive { get; set; }
public Users Users { get; set; }
}
I'm using the below LINQ method to retrieve the data:
public PagedTables<Users> GetAllUsers(Pagination pagination)
{
using (var db = new DbContext())
{
var user = new PagedTables<Users>()
{
Data = db.Users.OrderBy(U => U.Id).Skip(pagination.Page).Take(pagination.Limit).ToList(),
Count = db.Users.Count()
};
return user;
}
}
A break point on the users var shows that the property UserType returns null. I would expect the assigned user type to be joined onto the user.
Any help would be appreciated. Thanks
My EF background is database-first but if you are eager loading (i.e. not lazy loading) then are you missing an Include to tell LINQ to go and get the UserType? Something like;
Data = db.Users.OrderBy(U => U.Id).Skip(pagination.Page).Take(pagination.Limit).Include(U => U.UserType).ToList(),

Entity Framework auto assign FK to reference Entity during insertion?

Could you please explain for me Why and How EF auto assign FK to reference entity when i insert entities into Database? I got these simple Entities like this:
First one is Catalogue
public class Catalogue
{
public int CatalogueId { get; set; }
public string Name { get; set; }
public ICollection<Page> Pages { get; set; }
}
Second one is Page which reference to Catalogue.
public class Page
{
public int PageId { get; set; }
public string Name { get; set; }
public int CatalogueId { get; set; }
public Catalogue Catalogue { get; set; }
}
The relationship in this case is one to many. So in the code i am using this:
using (var context = new MyDbContext())
{
var catalogue = new Catalogue
{
Name = "catalogue 1"
};
var page = new Page
{
Name = "page 1",
CatalogueId = 0
};
context.Catalogues.Add(catalogue);
context.Pages.Add(page);
context.SaveChanges();
}
The MyDbContext is simple nothing special.
When i run this code i am expecting it will generate an error because CatalogueId = 0 is not valid, but it working fine,.
It is interesting me and hopefully someone can clarify that :).
Thanks in advance
This is how EF work under the hood. The context will go and execute the INSERT and generate the update for the FK value in the table. Later, will populate the tracked entity with the real key value.
You can experiment with unattached entities and will notice that no FK value is updated.

Resources