EF core navigation property return null value even after using Incude - c#

This is not a duplicate question as I have looked up many questions including this, which is the closest to what I want but didn't solve the challenge.
I have my table models relation set up this way:
public class User
{
public long UserId { get; set; }
public string Name { get; set; }
public IList<Transaction> Transactions { get; set; }
}
public class Transaction
{
public long TransactionId { get; set; }
public User User { get; set; }
public User Patient { get; set; }
}
fluent api setup for the entity
//some other modelbuilder stuff
modelBuilder.Entity<User>(entity =>
{
entity.HasMany(e => e.Transactions).WithOne(e => e.User);
//wanted to add another entity.HasMany(e => e.User).WithOne(e => e.Patient) but efcore didn't allow me.
});
This generates a Transaction table with UserUserId and PatientUserId and takes the right values on save.
But when I do a get with a user Id
User user = dbcontext.Set<User>().Include(t => t.Transactions).FirstOrDefault(u => u.UserId == userId);
user.Transactions have a list of transaction all with null Transaction.Patient
What exactly is going on here and how do I get past it?
Thanks.

You are nesting navigations. So, you have to use ThenInclude like this to add Patient which is a navigation property of Transaction.
User user = dbcontext.Set<User>().Include(t => t.Transactions).ThenInclude(p => p.Patient).FirstOrDefault(u => u.UserId == userId);

Related

EF core tries to add a duplicated row to join table during entity update

I've researched a ton and couldn't find a solution that works for me.
The issue I have is the following:
I have a many to many relationship among my entities and I have a join table (without a "join model" in the code). And when trying to update an entity, EF Core tries to add a row to the join table, which already exists.
The thing is, that one of the tables is constant & is populated during migration and shouldn't be ever modified. The question is, how to tell EF Core, to not add any existing, valid rows to the second table & to the join table?
Here's the code - relationship configuration:
public class FirstModelsMap : IEntityTypeConfiguration<FirstModel>
{
public void Configure(EntityTypeBuilder<FirstModel> builder)
{
builder.HasKey(p => p.Id);
builder.HasIndex(nameof(FirstModel.InternalGuid));
builder.Property(p => p.Secret)
.HasMaxLength(128)
.IsRequired();
builder.Property(p => p.Name)
.IsRequired();
builder.Property(p => p.Url)
.IsRequired();
builder.Property(p => p.InternalGuid)
.IsRequired();
builder.HasMany(p => p.SecondModels)
.WithMany(p => p.FirstModels)
.UsingEntity(p => p.ToTable("FirstModelSecondModels"));
}
}
First entity:
public class FirstModel
{
public long Id { get; set; }
public Guid InternalGuid { get; set; }
public string Name { get; set; }
public Uri Url { get; set; }
public string Secret { get; set; }
public ICollection<SecondModel> SecondModels { get; set; }
}
Second entity:
public class SecondModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string InternalName { get; set; }
public ICollection<FirstModel> FirstModels { get; set; }
}
What I try to do in the repository:
public void Update(FirstModel firstModel)
{
firstModel.SecondModels = database.SecondModels.Where(e => firstModel.SecondModels.Select(e => e.Id).Contains(e.Id)).ToList();
// Tried adding this row, based on some StackOverflow answers
database.SecondModels.AttachRange(firstModel.SecondModels);
database.Update(firstModel);
}
What I get as a result when calling SaveChanges() in the service:
Microsoft.EntityFrameworkCore.DbUpdateException: 'An error occurred while saving the entity changes. See the inner exception for details.'
Inner Exception:SqlException: Violation of PRIMARY KEY constraint 'PK_FirstModelsSecondModels'. Cannot insert duplicate key in object 'dbo.FirstModelsSecondModels'. The duplicate key value is (1, 1).
My SecondModels table is being populated with migration & is not expected to be modified. The join table may be modified. However I want to let EF Core know, that some rows may already be in place. I want the join table to adjust based on the public ICollection<SecondModel> SecondModels { get; set; } value of the FirstModel. So that not relevant rows are deleted, existing rows are not added, new rows are added.
I'm sure I'm doing something wrong here (otherwise it would work). Can you please help?
I've ended up modifying the method the following way, which made everything work as expected, although I don't think this is the best solution by any means:
public async Task<bool> UpdateAsync(FirstModel firstModel)
{
var secondModelIds = firstModel.SecondModels.Select(e => e.Id);
var existingFirstModel = await database.FirstModel .Include(w => w.SecondModels).FirstOrDefaultAsync(w => firstModel.Id == w.Id);
if (existingFirstModel is null)
{
return false;
}
existingFirstModel.SecondModels= database.SecondModels.Where(e => secondModelIds .Contains(e.Id)).ToList();
existingFirstModel.InternalGuid = firstModel.InternalGuid;
existingFirstModel.Secret = firstModel.Secret;
existingFirstModel.Url = firstModel.Url;
existingFirstModel.Name = firstModel.Name;
// This is called in the service layer
await database.SaveChangesAsync()
return true;
}

How to write this C# statement in linq

I'm trying to get related tables Adress and PlzOrt into an object User.
The relationships are as follows:
User 1:1 Adress
Adress n:1 PlzOrt
Entities Scaffolded from the DB
public partial class User
{
//No hash sets for Adress Scaffolded (probably unnecessary since the is
//only ever one address per user
public int IdUser { get; set; }
public int? FidAdresse { get; set; }
public string Email { get; set; }
public string Vorname { get; set; }
public string Nachmname { get; set; }
[ForeignKey("FidAdresse")]
[InverseProperty("User")]
public virtual Adresse FidAdresseNavigation { get; set; }
}
public partial class Adresse
{
public Adresse()
{
User = new HashSet<User>();
}
public int IdAdresse { get; set; }
public int FidPlzOrt { get; set; }
public string Strasse { get; set; }
public string Hausnr { get; set; }
[ForeignKey("FidPlzOrt")]
[InverseProperty("Adresse")]
public virtual PlzOrt FidPlzOrtNavigation { get; set; }
[InverseProperty("FidAdresseNavigation")]
public virtual ICollection<User> User { get; set; }
}
public partial class PlzOrt
{
public PlzOrt()
{
Adresse = new HashSet<Adresse>();
}
public int IdPlzOrt { get; set; }
public string Plz { get; set; }
public string Ort { get; set; }
[InverseProperty("FidPlzOrtNavigation")]
public virtual ICollection<Adresse> Adresse { get; set; }
}
Here´s the linq that does not work.
return _context.User
.Include(u => u.FidPermissionNavigation)
.Include(c => c.FidAdresseNavigation)
.ThenInclude(c => c.FidPlzOrtNavigation)
.FirstOrDefault(c => c.IdUser == id);
The linq work when I don´t include the "ThenInclude(c => c.FidPlzOrtNavigation)" statement, but I want this information in my object.
Here´s the C# that gives me the expected results:
public User GetUser(int id)
{
foreach (User user in _context.User)
{
if (user.IdUser == id)
{
foreach (Adresse adresse in _context.Adresse)
{
if (adresse.IdAdresse == user.FidAdresse)
{
user.FidAdresseNavigation = adresse;
foreach (PlzOrt plzOrt in _context.PlzOrt)
{
if (plzOrt.IdPlzOrt == adresse.FidPlzOrt)
{
user.FidAdresseNavigation.FidPlzOrtNavigation = plzOrt;
break;
}
}
break;
}
}
return user;
}
}
return null;
}
Translating this linq statement would be of great help. Thanks in advance.
Generated db_context code in case you are interested or this helps
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(e => e.IdUser)
.HasName("PRIMARY");
entity.HasIndex(e => e.FidAdresse)
.HasName("fk_User_Adresse1_idx");
entity.HasOne(d => d.FidAdresseNavigation)
.WithMany(p => p.User)
.HasForeignKey(d => d.FidAdresse)
.HasConstraintName("fk_User_Adresse1");
});
modelBuilder.Entity<Adresse>(entity =>
{
entity.HasKey(e => e.IdAdresse)
.HasName("PRIMARY");
entity.HasIndex(e => e.FidPlzOrt)
.HasName("fk_Adresse_plz_ort1_idx");
entity.HasOne(d => d.FidPlzOrtNavigation)
.WithMany(p => p.Adresse)
.HasForeignKey(d => d.FidPlzOrt)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("fk_Adresse_plz_ort1");
});
modelBuilder.Entity<PlzOrt>(entity =>
{
entity.HasKey(e => e.IdPlzOrt)
.HasName("PRIMARY");
});
So you have an id, and you want the one and only user that has this id as primary key, together with his Address and his PlzOrt.
Whenever you query, use Select to fetch the data. Only use Include if you want to update the fetched data.
The reason for Select is that you have greater freedom of what you select. Besides you can limit the fetched data: if you query Schools with their Students, you know that every Student of School 10 will have a foreign key SchoolId equal to 10. So why fetch this foreign key for every of the Schools 1000 Students?
I'm not familiar with the possibilities of ef-core. Does it know that if you Select one of the virtual properties that a (group-)join is needed? In that case it is easier to use Select.
If you'll have to do your joins yourself:
var requestedUserWithAddresAndPlz = dbContext.Users.
// keep only the user with id
.Where(user => user.IdUser == id)
// every User has exactly one Address: use a normal join
.Join(dbContext.Addresses,
user => user.IdUser, // from every User take IdUser
address => addres.IdAddress, // from every Address take IdAddress
// result selector: take the user with its address to make one new
(user, address) => new
{
// Select only the User properties you plan to use
Id = user.IdUser,
Name = user.Name,
...
Address = new
{
// again: select only the properties you plan to use
// no need to select IdAddress, you know the value!
Street = address.Street,
HouseNumber = address.HouseNumber,
// to fetch the Plz: fetch the one-and-only PlzOrt with address.FidPlzOrt
Plz = dbContext.PlzOrts
.Where(plzOrt => plzOrt.PlzOrdIt == address.FidPlzOrt)
.FirstOrDefault(),
})
.FirstOrDefault();
Note: I used anonymous types to have greater freedom in selecting only the properties I actually plan to use. I can also give my properties the names that I want.
Disadvantage: you can't use anonymous types as return values. If you really need this outside your function, use create a class that contains your data, and use new SomeClass(). Advantage: if your database changes, SomeClass doesn't have to change, and thus your callers won't notice the change in your database.

Filter linq/entity query results by related data

I'm using MVC5 EF6 and Identity 2.1.
I have two classes:
public class Incident
{
public int IncidentId {get; set;}
...//Title, Description, etc
public virtual ICollection<FollowedIncident> FollowedIncidents { get; set; }
public virtual ApplicationUser User { get; set; }
}
public class FollowedIncident
{
public int FollowedIncidentId { get; set; }
public string UserId { get; set; }
public int IncidentId { get; set; }
public virtual Incident Incident { get; set; }
public virtual ApplicationUser User { get; set; }
}
So, the users will have the ability to follow an incident. (For starters, I'm not entirely sure if I need the ICollection and public virtual relationship references, but added them just in case for the time being.)
I'm trying to create the query that will show users the results of their followed incidents. In my controller, my query starts like this (I'm using Troy Goode's paging package... i.e. listUnpaged):
IQueryable<Incident> listUnpaged = db.Incidents.OrderByDescending(d => d.IncidentDate);
Then I want to filter by followed incidents. So, I want to show incidents where userId (parameter I pass to it) is equal to UserId in FollowedIncident. I've tried like this (error about conversion to bool from IEnumerable):
listUnpaged = listUnpaged.Where(s => s.FollowedIncidents.Where(t => t.UserId == userId));
And this (no error, but doesn't filter at all):
listUnpaged = listUnpaged.Where(s => s.FollowedIncidents.All(t => t.UserId == userId));
To me, it seems it should be as simple as this:
listUnpaged = listUnpaged.Where(s => s.FollowedIncidents.UserId == userId));
But, the linq extensions don't seem to like related data child properties? (I apologize for my programming terminology as I haven't quite pieced together all the names for everything yet.)
Anyone know how to accomplish this? It seems I may not even be thinking about it correct? (...since in the past, I've always used related data to supplement or add properties to a result. This will be the first time I want to narrow results by related data.)
Thank you.
Actually you're going about getting the Incidents the wrong way.. since Incident is a navigation property of FollowedIncident you should just use
IQueryable<Incident> listUnpaged = db.FollowedIncidents
.Where(a => a.UserId == userid)
.Select(a => a.Incident)
.OrderByDescending(d => d.IncidentDate);
Another option is to use Any()
IQueryable<Incident> listUnpaged = db.Incidents
.Where(a => a.FollowedIncidents.Any(b => b.UserId == userid)
.OrderByDescending(d => d.IncidentDate);
which would be like saying
Select *
From Incidents
Where Id IN (Select IncidentId
From FollowedIncident
Where UserId = #UserId)

query multi-level entity with filter at the lowest level

So I have 3 entity classes:
public partial class Event
{
public Event()
{
Recurrences = new HashSet<Recurrence>();
}
public int Id { get; set; }
public ICollection<Recurrence> Recurrences { get; set; }
}
public partial class Recurrence
{
public Recurrence()
{
AspNetUsers = new HashSet<AspNetUser>();
}
public int Id { get; set; }
public int EventId { get; set; }
public ICollection<AspNetUser> AspNetUsers { get; set; }
}
public partial class AspNetUser
{
public AspNetUser()
{
Recurrences = new HashSet<Recurrence>();
}
public string Id { get; set; }
public string UserName { get; set; }
public ICollection<Recurrence> Recurrences { get; set; }
}
I would like to get the event given the aspnetuser.id using line to entity. so far this is what I have but it's returning an error:
// GET: api/Events?userId={userId}
public IQueryable<Event> GetEvents(string userId)
{
return db.Events
.Include(e => e.Recurrences
.Select(u => u.AspNetUsers.Where(i => i.Id == userId)));
}
When I exclude the where clause it works fine. Please help.
Thanks in advance!
I don't think Include() means what you think it means. (https://msdn.microsoft.com/en-us/library/bb738708%28v=vs.110%29.aspx) What it does is tell the db set to be sure to bring in relationships for that object. By default (last I checked), the db context will auto pull in all relationships, so this isn't necessary. However, if you've turned off the lazy-loading (http://www.entityframeworktutorial.net/EntityFramework4.3/lazy-loading-with-dbcontext.aspx) then you'll need to .Include() all the relationships you want to have in the query.
This should solve your problem. I don't guarantee the SQL generated won't be silly, though.
If you have lazy-loading turned on:
db.Events.Include("Recurrences").Include("Recurrences.AspNetUsers")
.Where(e => e.Recurrences
.Any(r => r.AspNetUsers
.Any(u => u.Id ==userId)));
If you have lazy-loading turned off:
db.Events
.Where(e => e.Recurrences
.Any(r => r.AspNetUsers
.Any(u => u.Id ==userId)));
Also, if you have trouble seeing errors, you can .ToList() the query before returning so that it fails in your code and not deep inside the Web API stack. Personally, I like to do this so that I can try/catch the query and handle it properly.

Difficulty Concerning EF Code First Fluent API, TPH, and Foreign Keys

I have two tables in my database. One is called Users, and the other is called Widgets. The Widgets table represents 3 entities in my code model. One of the entities, Widget, is a parent class for the other two entities, WidgetTypeA and WidgetTypeB. Both WidgetTypeA and WidgetTypeB have navigation properties to the User entity, which is persisted to the Users table in the database. I'm having trouble getting Code First to use the same foreign key for both the WidgetTypeA and WidgetTypeB entities (UserId). Does anyone know how to do this? It seems like it should be a common problem with Table Per Hierarchy mapping.
My entity classes are as follows:
public class Widget
{
public int Id { get; set; }
public string Name { get; set; }
}
class WidgetMap : EntityTypeConfiguration<Widget>
{
public WidgetMap()
{
ToTable("Widgets");
HasKey(w => w.Id);
Property(w => w.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(w => w.Name)
.IsRequired()
.HasMaxLength(75)
.IsUnicode(true);
}
}
public class WidgetTypeA : Widget
{
public int UserId { get; set; }
public virtual User User { get; set; }
public string Color { get; set; }
public int DepthLevel { get; set; }
}
class WidgetTypeAMap : EntityTypeConfiguration<WidgetTypeA>
{
public WidgetTypeAMap()
{
Map(w => w.Requires("WidgetTypeId").HasValue(1));
HasRequired(w => w.User)
.WithMany(u => u.WidgetTypeAs)
.HasForeignKey(w => w.UserId)
.WillCascadeOnDelete(false);
Property(w => w.Color)
.IsOptional()
.IsUnicode(true)
.HasMaxLength(75);
Property(w => w.DepthLevel)
.IsOptional();
}
}
public class WidgetTypeB : Widget
{
public int UserId { get; set; }
public virtual User User { get; set; }
}
class WidgetTypeBMap : EntityTypeConfiguration<WidgetTypeB>
{
public WidgetTypeBMap()
{
Map(w => w.Requires("WidgetTypeId").HasValue(2));
HasRequired(w => w.User)
.WithMany(u => u.WidgetTypeBs)
.HasForeignKey(w => w.UserId)
.WillCascadeOnDelete(false);
}
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public int Age { get; set; }
public virtual ICollection<WidgetTypeA> WidgetTypeAs { get; set; }
public virtual ICollection<WidgetTypeB> WidgetTypeBs { get; set; }
}
class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
ToTable("Users");
HasKey(u => u.Id);
Property(u => u.Username)
.IsRequired()
.HasMaxLength(75)
.IsUnicode(true);
Property(u => u.Age)
.IsRequired();
}
}
At any rate, I keep getting the error
Invalid column name 'UserId1'
when I try to perform the following operations:
using (var entities = new MyEntities())
{
User u = new User
{
Username = "Frank",
Age = 14
};
entities.Users.Add(u);
entities.SaveChanges();
WidgetTypeA wa1 = new WidgetTypeA
{
Name = "0SDF81",
UserId = u.Id,
DepthLevel = 6
};
entities.WidgetTypeAs.Add(wa1);
entities.SaveChanges();
}
Not sure if this can be fixed or not. I can always specify a second UserId foreign key for the Widgets table, but that seems pointless. Perhaps there's a way to do this using Fluent API?
You cannot map properties defined in different derived entities to the same column. That is limitation in EF. If your WidgetTypeA has UserId property and your WidgetTypeB has UserId property they must be different columns in the database. It should work if you move both UserId and User properties from derived types to the parent Widget type.
I know its a long way late, but hopefully may help other readers.
Although Ladislav was correct that using a mapped Foreign Key is not supported in EF6, I did find a useful workaround.
It is possible to define a computed column specification whose expression simply refers to the original column. Userid in the description above. This can be used as the discriminator for the TPH mapping. With this approach, the column need not be persisted, but can be used for TPH, with the original column being available for use as a foreign key.

Categories