In my .net 6.0 project I use the Entity Framework 6 to get data from my database.
My model contains a foreign key, which is used to resolve the data of the id.
I use the following call to resolve the foreign key:
// ...
_context.Tools.Include(t => t.User).ToListAsync();
// ...
My Tool Model looks like this:
[Table("MY_TOOLS")]
public class Tool
{
[Key]
[Column("ID")]
public int Id { get; set; }
[Column("UPDATED_BY")]
public int? UpdatedBy { get; set; }
[ForeignKey("UpdatedBy")]
public User? User { get; set; }
}
My User class looks like this:
[Table("MY_USERS")]
public class User
{
[Key]
[Column("ID")]
public int Id { get; set; }
[Column("EMAIL")]
public string? Email { get; set; }
}
When I leave the include like described above, the user is resolved correctly.
Is there a way to remove the user property from the loaded data, when I don't explicitly tell the Model to resolve the foreign key?
It shouldnt resolve it by default as it uses lazy loading. You would have to query it specifically for the user object to get it e.g. _context.My_Tools.include(uvar = uvar.User).FirstOrDefault();
So you just make a method called getToolEager() and one called getTool()
it would be a "waste" of a call to query for the user object only to throw it away in case you might not need it.
You have 2 options:
Lazy Loading Proxy
Use Lazy Loading as described here:
https://learn.microsoft.com/en-us/ef/core/querying/related-data/lazy
After just load data as _context.Tools.ToListAsync(); and users will load when you try access them.
Manually load related data
Modify Tool to explicitly store User FK:
[Table("MY_TOOLS")]
public class Tool
{
[Key]
[Column("ID")]
public int Id { get; set; }
[Column("UPDATED_BY")]
public int? UpdatedBy { get; set; }
[ForeignKey("UpdatedBy")]
public User? User { get; set; }
public int? UpdatedBy{ get; set; }
}
So when you load data as _context.Tools.ToListAsync(); fieldUser will be null but UpdatedBy will have User Id(if FK is not null in DB), so you can manually load them manually like tool.User = await _context.Users.FirstOrDefaultAsync(t => t.Id == tool.UpdatedBy);
Related
Suppose that I need to have two tables named Wagons and WagonTypes. Obviously enough, each row in the Wagons table should reference to the correspoding WagonTypes record via foreign key.
Am I doing this right?
public class Wagons
{
public Guid Id { get; set; }
[Required]
public WagonTypes Type { get; set; }
}
public class WagonTypes
{
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
}
Yeah, it works and all that but I don't unserstand why there's an additional field in the docs then:
public int BlogId { get; set; }
public Blog Blog { get; set; }
What's the point of having a BlogId field? Should I define it too?
You don't have to add the foreign key, but it's recommended because it makes your life easier.
Imagine you want to edit some row in table Wagons (for example property Name). You have to get the object, edit Name, then load property Type and then call SaveChanges. If you didn't load Type, EF would think you want to edit that Type too.
If you had public int TypeId{ get; set; } in your Wagons class, you could just get that object, directly edit Name and call SaveChanges without any further loading.
I have the following entities:
public class Seminar
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual List<Meeting> Meetings { get; set; }
}
and
public class Meeting
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int SeminarId { get; set; }
public string ThirdPartyId { get; set; }
public DateTime StartDate { get; set; }
public virtual Webinar Webinar { get; set; }
}
Meeting is related to Seminar by the SeminarId property (a foreign key).
The ThirdPartyId property (name changed to protect my interests) is populated after making a call to a third party API - it's the "link" to that system.
In order to make this call (in which we create the same object on that system), we need the Title and Description information from the seminar associated with the meeting.
So let's say I create a new meeting like so:
using (var db = new MyDbContext())
{
Meeting meeting = new Meeting
{
SeminarId = 5 // this would normally be loaded from elsewhere, obviously
};
// Load the meeting.Webinar property somehow here
m_thirdPartyApiClient.CreateMeeting(meeting); // Uses meeting.Webinar.Title and meeting.Webinar.Description
db.SaveChanges();
}
What I'd like to do is to load the foreign key property on meeting before calling the third party API or SaveChanges. Is this possible?
Simply call the database for the Seminar by its id and Assign it to the meeting like Meeting.Seminar = dbSeminar. To answer your comment, virtual just says that this method/property may be over written by a inheriting class and when overwritten to use the inheriting class's prop/method even when the ref is of type base class as long as the actual type is inheriting class.
Once you add the entity object to the corresponding DbSet, you can load any reference property by using Load method like this:
Meeting meeting = new Meeting
{
SeminarId = 5 // this would normally be loaded from elsewhere, obviously
};
db.Meetings.Add(meeting);
// meeting.Webinar is still null
db.Entry(meeting).Reference(m => m.Webinar).Load();
// meeting.Webinar is populated
I have the following many to many model between Project and UserProfile entities. As many to many relationships is not supported in Breeze yet, I am exposing the middle entity ProjectMember as well. So the server side code looks like the following:
public class Project
{
public int ProjectId { get; set; }
public String Name { get; set; }
public virtual List<ProjectMember> ProjectMembers { get; set; }
}
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public virtual List<ProjectMember> ProjectMembers { get; set; }
}
public class ProjectMember
{
public int ProjectId { get; set; }
[ForeignKey("ProjectId")]
public Project Project { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public UserProfile UserProfile { get; set; }
}
The metadata returned from the server seems to be the right one:
The navigation property seem to be properly sent out by the server.
When I request a project from the client by doing:
var query = entityQuery.from("Projects")
.where('projectId', '==', projectId)
.expand("projectMembers");
The returned JSon data is the one expected:
However, the Project.ProjectMembers navigation property is not properly constructed on the client side as you see from the screenshot below:
I went through the tutorials, the breeze documentation, the SO questions related to navigation properties and I still don't see what I am doing wrong.
Question:
Given the information above, why is Breeze not loading the ProjectMembers navigation property?
I would start by checking the EntityManager's metadataStore to make sure that the ProjectMember entityType can be found. You can do this after your first query via
var projectMemberType = myEntityManager.metadataStore.getEntityType("ProjectMember");
If the projectMemberType is not found then the problem has to do with the metadata not being brought down correctly.
Another possibility, have you defined a key for the ProjectMember type ( presumably a two part key)?
I'm in the process of converting a project from NHibernate to Entity Framework 6.
Given this simple model:
public class User
{
public int ID { get; set; }
public string FullName { get; set; }
public virtual Organization Organization { get; set; }
// [...]
}
public class Organization
{
public int ID { get; set; }
public string Name { get; set; }
public virtual List<User> Users { get; set; }
// [...]
}
Accessing the primary key (ID) through the Organization navigation property will cause the whole Organization entity to be loaded into the context:
foreach(var user in db.Users)
Console.WriteLine(user.Organization.ID);
Given that the OrganizationID foreign key is part of the User row, I should be able to access it without causing a Lazy Load of the whole entity (and indeed, NHibernate does this properly).
Short of adding properties for the foreign key IDs into all of my 100+ entities so I can access their values without loading the entities, is there anything to be done to avoid this behaviour?
EDIT: Furthermore, even doing a null check will cause a load of the Organization entity (not in NHibernate):
foreach(var user in db.Users)
Console.WriteLine(user.Organization != null);
I guess this is due to the fundamental differences in the way the entity proxy is implemented in these two frameworks. So I'll have to adapt all of my code to this new frustrating behaviour... Unless someone already had to go through this and could enlighten me?
Nope, you'll need to add them as property in your class (that is; if you want it strong typed) like this to access it directly.
public class User
{
public int ID { get; set; }
public string FullName { get; set; }
//added ID
public int OrganizationID { get; set; }
public virtual Organization Organization { get; set; }
// [...]
}
By accessing the int you'll prevent the lazy loading, EF will bind the ID through naming conventions. Having said that: 100+ classes... :|
UPDATE:
As I just realized; you might want to try:
db.Users
.Include("Organization.ID")
.Where(/*your stuff*/) //etc.;
I am not certain if it will fully load the nested property. If it doesn't, it might be a small performance gain.
Let's say I have 3 tables:
[Table("Comments")]
public class Comment {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Users")]
public int UserId { get; set; }
public virtual Users Users { get; set; }
public string Text { get; set; }
}
[Table("Users")]
public class Users {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
}
[Table("CommentAgree")]
public class CommentAgree {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int CommentId { get; set; }
public int UserId { get; set; }
}
Users post comments and other users can 'agree' with that comment, a bit like Facebook's 'like' system. I'm using lambda for my queries and I can do:
var query = db.Comments.Select(c => new {
c.Id,
c.Users.UserName,
c.Text
});
How can I create a join to CommentAgree on Comment.Id = CommentAgree.CommentId? I could write the join in Lambda but I need it to be a left join as nobody may agree with the comment but I still want it to display.
I want to do it the right way, so I'm open to suggestions whether to do it by foreign keys, lambda joins, navigation properties... or something else?
Is this possible?
Thanks
The best approach is probably to use the features of Entity Framework and create navigation properties rather than explicitly using LINQ to perform the joins just for related data.
If your types are shaped just for the purposes of data access, then adding navigation properties to both ends of the relationship is probably a good idea, along with the foreign key properties that you already have.
The collection navigation property on Comment should implement ICollection (for example List<CommentAgree>), and you would have a reference navigation property of type Comment on the CommentAgree type.
You would then have to define the relationships in your mappings, either using data annotations or (preferably) the fluent API.
To load the related data, you could either use lazy loading, or eager loading (using the Include extension method), or use explicit loading from the entry information for the entity.