I have these tables i have made in c# using code first approach.
Employee class:
public int id { get; set; }
public string name { get; set; }
Department class:
public int id { get; set; }
public string deptName { get; set; }
public IQueryable<Employee> { get; set; }
This generates a DepartmentID in my Employee table in my sql database. I cannot however access this field in c# as DepartmentID is not a field in the employee class/model.
My question is how do i access this variable. I wish to do some various joins etc but am struggling with this.
You can certainly expose the foreign key, but it is not necessarily needed. The beauty of EF is you don't need joins.
First I would clean up your classes:
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
// Exposed FK. By convention, EF know this is a FK.
// EF will add one if you omit it.
public int DepartmentID { get; set; }
// Navigation properties are how you access the related (joined) data
public virtual Department Department { get; set; }
}
public class Department
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
Now you can query your data easily:
var employeeWithDepartment = context.Employee
.Include(e => e.Department)
.FirstOrDefault(e => e.ID = 123);
var employeeName = employeeWithDepartment.Name;
var departmentName = employeeWithDepartment.Department.Name;
... etc.
var departmentWithListOfEmployees = context.Departments
.Include(d => d.Employees)
.Where(d => d.Name == "Accounting")
.ToList();
... build table or something
foreach (var employee in departmentWithListOfEmployees.Employees)
{
<tr><td>#employee.ID</td><td>#employee.Name</td>
}
... close table
Related
I have two tables that I need to join and filter. Orders and Customers. I have generated these classes using EF Code First from DB.
Generated classes for the tables -
Orders
[Table("Orders")]
public partial class Orders
{
[Key]
[StringLength(17)]
public string OrderID { get; set; }
public int ShipToCustomerID { get; set; }
//Navigation Property
public Customer Customer { get; set; }
}
Customers
[Table("Customer")]
public partial class Customer
{
public int CustomerID { get; set; }
public string AccountNumber { get; set; }
//Navigation prop
public int ShipToCustomerID { get; set; } (not a part of the table, just attempting to get the navigation work)
public Orders Order { get; set; }
}
Method 1:
LINQ Joins
using (var context = new OrderDetailsGeneral1())
{
var data = (from p in context.Orders
join q in context.Customers
on p.ShipToCustomerID equals q.CustomerID
where p.OrderID == "7150615"
select new
{
OrderID = p.OrderID,
CustomerID = q.AccountNumber
}
);
var orders = data.ToList();
return Json(orders);
}
This works well and I get the following output -
[
{
"OrderID": "7150615",
"CustomerID": "23320347 "
}
]
Method 2:
I read that it's better to use navigation properties than using joins and that's why I was trying to do so, as per that I added the navigation properties to the classes above.
I tried a bunch of ways to link them together. One of them is the way mentioned here and I came across a bunch of errors.
It would try to map Customers.CustomerID to Orders.OrderID instead of Orders.ShipToCustomerID.
What's the best way to achieve this? I am having a hard time figuring out linking this foreign key (Customers.CustomerID) to a non primary/alternate key (Orders.ShipToCustomerID)
you have to fix your classes
[Table("Orders")]
public partial class Order
{
[Key]
[StringLength(17)]
public string OrderID { get; set; }
public int ShipToCustomerID { get; set; }
[ForeignKey(nameof(ShipToCustomerID))]
[InverseProperty("Orders")]
public virtual Customer Customer { get; set; }
}
[Table("Customer")]
public partial class Customer
{
[Key]
public int CustomerID { get; set; }
public string AccountNumber { get; set; }
[InverseProperty(nameof(Order.Customer))]
public virtual ICollection<Order> Orders { get; set; }
}
I have the following models in my application:
public class Employee
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Benefits { get; set; }
}
public class Department
{
public int DeptID { get; set; }
public string DeptName { get; set; }
}
public class DeptEmp
{
public int PersonID { get; set; }
public int DeptID { get; set; }
}
I want to create a query, using Entity Framework, to select all columns from employee with a condition that it retrieves only those employees that PersonId has a relation with DeptId in the DeptEmp class and DepartId from Department has a relation with DeptId in the DeptEmp.
I have written the following LINQ statement:
var selectEmployees = from e in Employee
join d in DeptEmp on e.PersonId equals d.PersonId
join dd in Depatment on d.DeptId equals dd.DeptId
select new
{
e.FirstName,
e.LastName,
e.Benefits
};
but it is not working. Am I missing anything?
Entity framework works on a "use standards or else" basis. It is fairly easy if you use standards, if not you have to provide lots of information about your deviations.
For instance, entity framework expects a primary key of Employee as Id or EmployeeId. If you decide to use a different primary key (PersonId), you'll have to tell entity framework that this is your primary key.
The same is with your many-to-many relationship. If you use the defaults it is fairly easy, otherwise you'll need attributes or fluent API to inform about the deviations from the defaults.
Default many-to-many in your Employee / Department model would be:
See also Entity Framework Tutorial Configure many-to-many
public class Employee
{
public int EmployeeId{ get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Benefits { get; set; }
// an employee has many departments:
public virtual ICollection<Department> Departments { get; set; }
}
public class Department
{
public int DeptartmentId { get; set; }
public string DeptName { get; set; }
// an department has many employees
public virtual ICollection<Employee> Employees{ get; set; }
}
public MyDbContext : DbContext
{
public DbSet<Employee> Employees {get; set;}
public DbSet<Department> Departments {get; set;}
}
If you make a simple console application with these classes you'll see that it creates also a many-to-many table. You'll seldom need this table, but if you really need it, you could add it to the DbContext.
I want ... to select all columns from employee with the condition that it retrieves only those employees that PersonId has a relation with DeptId
I assume that this means that given a DeptId you want all properties from all employees working in this DeptId:
using (var dbContext = new MyDbContext(...))
{
var myDepartment = dbContext.Departments
.Where(department => department.DepartmentId == DeptId)
.SingleOrDefault();
// I know there is at utmost one, because it is a primary key
if (myDepartment == null) ShowDepartmentMissing(...);
var employeesOfDepartment = myDepartment.Employees
.Select(employee => new
{
FirstName = employee.FirstName,
LastName = employee.LastName,
Benefits = employee.Benefits,
});
I cant figure out how to relate a column with another table using the include method.
i have the following models
public class Fleet
{
[Key]
public Guid OwnerId { get; set; }
public String ownerName { get; set; }
public virtual ICollection<UserAccount> UserAccount { get; set; }
}
public class UserAccount
{
[Key]
[Display(Name="UserID")]
public Guid UserID { get; set; }
public String UserName { get; set; }
public Guid SelectedFleet { get; set; }
public Guid? PrimaryFleet { get; set; }
public String Password { get; set; }
public virtual Fleet Fleet { get; set; }
}
In the Model UserAccount i have 2 foreignKeys where selectedFleet and PrimaryKey are realated to my fleet table
With this code i do get my UserAccount row back so that i can complete my login, i state this so you are aware that there is actually info to get bakc from the table with the data provided
var v = dc.UserAccounts.Where(a => a.UserName == model.UserName).SingleOrDefault();
The problem is when i try to include the fleet my record returns null, i know i am doing something wrong since i don't even know how to tell the include what column is supposed to be related to which table i tried d => d.Fleet.OwnerId == d.SelectedFleet but i got an error telling me thats not the use of inlcude
var v = dc.UserAccounts.Where(a => a.UserName == model.UserName).Include(d => d.Fleet).SingleOrDefault();
I am facing a problem with EF7 inverse property. There are two entities that are connected like this.
public class Employee
{
public int Id { get; set; }
}
public class Review
{
public int Id { get; set; }
[Required]
public virtual Employee Employee { get; set; }
[Required]
public Employee Manager { get; set; }
}
I want to access a list of the reviews when I start to query my employees, so I tried to do this:
public class Employee
{
public Employee()
{
Reviews = new List<Review>();
}
public int Id { get; set; }
[InverseProperty("Employee")]
public virtual ICollection<Review> Reviews { get; set; }
}
With this, the query is not well made and return this error:
Invalid column name 'EmployeeId1'.
This is the part of the query where is the error:
SELECT [ua].[Id], [r].[EmployeeId], [r].[EmployeeId1], [r1].[EmployeeId], [r1].[EmployeeId1]
FROM [UserAssessment] AS [ua]
LEFT JOIN [Review] AS [r] ON [ua].[ReviewId] = [r].[Id]
LEFT JOIN [Review] AS [r1] ON [ua].[ReviewId] = [r1].[Id]
Anyone know what can I do?
UPDATE
This statement is generating the query:
return this.DbSet
.Include(ua => ua.Employee).ThenInclude(t => t.Role)
.Include(ua => ua.Review).ThenInclude(rt => rt.ReviewType)
.Include(ua => ua.Review).ThenInclude(rt => rt.Manager).ThenInclude(r => r.Role)
I have to access with those same includes because lazy loading is not available on EF7 yet.
You need the InverseProperty on both the Employee and Review
public class Review
{
public int Id { get; set; }
[Required]
[InverseProperty("Reviews")]
public Employee Employee { get; set; }
[Required]
public Employee Manager { get; set; }
}
public class Employee
{
public int Id { get; set; }
[InverseProperty("Employee")]
public ICollection<Review> Reviews { get; set; }
}
Should work. I have a similar setup where it creates the navigation without creating any new fields. If this doesn't work let me know and I'll spin up a test project.
Also note, that EF7 currently ignores virtual and this does not have meaning like it did in EF6.
I am working on app and struggling to really understand how to update data in tables where we have intermediate join table (i.e. table to break many-to-many relationship). for example, from following diagram. if I say I want to add record for new student with list of three courses i.e. math, English and computing. how I do that where I have
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
another questions; another scenario if I have courses already and of-course I don't want duplication of math, English and computing course title, how I add new instance of student record there??
Student
public partial class Student
{
public Student()
{
this.StudentCourses = new HashSet<StudentCourse>();
}
public int StudentID { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
}
Course
public partial class Course
{
public Course()
{
this.StudentCourses = new HashSet<StudentCourse>();
}
public int CourseID { get; set; }
public string Title { get; set; }
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
}
Intermediate model
public partial class StudentCourse
{
public int StudentCourseID { get; set; }
public int StudentID { get; set; }
public int CourseID { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
My third question is, do I have to use Virtual key word in above code.
First, if you do not have an additional field in StudentCourse (such semester of the registration), then you do not need to have StudentCourse class.
If you want to keep this mode, you can do this:
StudentCourse registration = new StudentCourse();
registration.StudentID = 4;
registration.CourseID = 6;
context.StudentCourses.Add(registration);
context.SaveChanges();
These resources may give you further explanation:
https://practiceaspnet.wordpress.com/2015/10/22/code-first-many-to-many-mm-relationships-using-conventions-and-data-annotations/
https://practiceaspnet.wordpress.com/2015/10/30/managing-data-in-many-to-many-relationships-with-entity-framework-code-first/
If you want to check if there is no duplicate, you can simple do these:
if(context.Courses.Where(c => c.Title == 'Math').FirstOrDefault() == null)
{
//add the course
}else {
//already existing
}