I have following three tables
Employee(empid, name)
Role(roleid, name)
EmpRoleMap(mapid, empid,roleid)
In the index view I show the link of employees with edit link with each employee. When user clicks on edit it opens the edit screeen for the selected employee.
For this page to render I am making two calls to database. First is to get the details about selected employee. Seconds is to query the EmpRoleMap table to find out what all roles that employee belongs to.
I want to do the same stuff in single database call, i.e single call would give me the details about the employee and the roleID from EmpRoleMap & RoleName list for the given Employee.
I am using Code First approach with Scaffolding.
How can I do this using EntityFramework? Can we use "Include" clause with LINQ to get the additional data from other tables (here EmpRoleMap,Role).
You have to do a good Entity Model design. I'm going to try to show you an example of what I'm saying, but remember that at this moment I cannot check the code.
public class EmployeeModel
{
[Key]
[ForeignKey("EmployeeRoles")]
public decimal empid { get; set; }
public string name { get; set; }
public virtual List<EmpRoleModel> EmployeeRoles { get; set; }
}
public class RoleModel
{
[Key]
public decimal roleid { get; set; }
public string name { get; set; }
}
public class EmpRoleModel
{
[Key]
[Column(Order = 0)]
[ForeignKey("Employee")]
public decimal empid { get; set; }
[Key]
[Column(Order = 1)]
[ForeignKey("Role")]
public decimal roleid { get; set; }
public virtual EmployeeModel Employee { get; set; }
public virtual RoleModel Role { get; set; }
}
Now you can navigate to employee roles by the EmployeeRoles property.
Please try this code and give me feedback.
Related
I'm having trouble getting only products purchased by the logged in user, that is, the user who made the purchase.
I have a Users table that contains general registration data, and a Customers table.
The relationship between the tables is as follows:
User: userId, name, email, password
Customer: customerId, status, section_id, user_id
Product: productId, name, price, purchase_date, customer_id
Well, the Products table is related to the Customers table and not to the Users table. And I can't change that because there are other applications consulted in the database.
I try to get the products this way, but the id value of Customer is null.
public IActionResult View(int id)
{
User user = _loginUser.GetUser();
Product product = _pedidoRepository.GetProduct(id);
if (product.customer_id != user.Customer.customerId)
{
return new ContentResult() { Content = "Access denied" };
}
return View(product);
}
I get the error:
Project.Models.users.Customer.get returned null
How can I make the Client id accessible for comparison purposes?
My GetUser method looks like this:
public user GetUser()
{
// Deserializar
if( _session.Exists(Key))
{
string user JSONString = _session.Consult(Key);
return JsonConvert.DeserializeObject<user>(userJSONString);
}
else
{
return null;
}
}
It is important to say that the relationship between "User" and "Customer" is one by one. When registering "User", "Customer" is registered in the table with a fk for "User"
Class User.cs
public class User
{
/* PK */
[Key]
public int userId { get; set; }
public string name { get; set; }
public string email{ get; set; }
public string passwor{ get; set; }
[ForeignKey("user_id")]
[JsonIgnore]
public Customer Customer { get; set; }
}
Class Customer.cs
public class Customer
{
/* PK */
[Key]
public int customerId{ get; set; }
public string Status { get; set; }
[Column("user_id")]
[ForeignKey("User")]
public int? user_id { get; set; }
public virtual User User { get; set; }
.
.
}
Class Product.cs
public class Product
{
/* PK */
[Key]
public int productId{ get; set; }
public string name { get; set; }
public decimal price { get; set; }
public DateTime purchase_date { get; set; }
[Column("customer_id")]
[ForeignKey("Customer")]
public int? customer_id{ get; set; }
public virtual Customer Customer { get; set; }
}
I appreciate any comments. I'm starting with language. If I need to, I can send some more codes.
Well I would assume that your session holds a JSON representation of user, queried at some point from the DB. When this happens, user is queried without the Customer navigational property (so it's null). When you access later User.Customer, User entity is not attached to DbContext anymore, therefore Customer is not loaded from the DB.
If my assumtion is true, I'd propose two options:
When filling the session object, query user with loaded customer, like context.Users.Include(c => c.Customer).GetById(x)
In your method query Customer from the DB using userId. I.e. you get User from session, but matching Customer from the DB.
The choice depends on whether Cutomer is relevant for most of your calls or just for this single one.
Let's say in our project we use C# and MsSQL and we have one Products table with two columns (ID,Name)
One day we decided to save product information given by Company1, so we created a new table ProductInfoFromCompany1 because it has custom columns (ProductID, Price, CurrentScore)
The next day, we agreed with Company2 and now we need to save their data as well. So, new table -> ProductInfoFromCompany2 with different columns (ProductID, Year, Rating)
Another day, we agreed with Company3 and so on...
So, we have no idea how the data given by new companies will look like. That's why we need to create a new table because if we use one Details table, it will be too wide with numerous null columns
In Entity Framework Core we have these models:
public class ProductInfoFromCompany1
{
public int Id { get; set; }
public int ProductId { get; set; }
public decimal Price { get; set; }
public double CurrentScore { get; set; }
public Product Product { get; set; }
}
public class ProductInfoFromCompany2
{
public int Id { get; set; }
public int ProductId { get; set; }
public int Year { get; set; }
public double Rating { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
//Do we need these navigation properties in this class?
//public ProductInfoFromCompany1 ProductInfoFromCompany1 { get; set; }
//public ProductInfoFromCompany2 ProductInfoFromCompany2 { get; set; }
}
You can see my question is commented in the Product class.
Do we need to add navigation properties in the Product class?
The reason why I'm asking is that in all books or documentation which I've read, people use navigation property, but in this case, it violates open-closed principle because whenever we add new company, we need to modify Product class as well.
P.S. if we want to query ProductInfoFromCompany1 data and we have product Id, we can simply start querying from ProductInfoFromCompany1, like this
var info = _db.ProductInfoesFromCompany1.Where(c=>c.ProductId == productId);
Do we need to add navigation properties in the Product class?
You are the only one who can answer the question if you need something or not.
If the question is does EF Core require navigation properties, the answer is no. Reference: Relationships - Single Navigation Property EF Core documentation topic:
Including just one navigation property (no inverse navigation, and no foreign key property) is enough to have a relationship defined by convention.
In fact EF Core fluent API and shadow properties allow defining relationship without any navigation or FK property. How useful it would be is another story. The main point (which is the question as I read it) is that none of them is mandatory.
Of course the lack of a navigation property imposes some limitations on the type of LINQ queries you can create - like you said, you can't start a query from Product and apply filter on associated ProductInfoFromCompany1, or eager/explicit/lazy load it.
But if you don't need all that, e.g. as you said, you can build your queries starting from ProductInfoFromCompany1, then omitting the navigation property in Product is perfectly fine.
As I mentioned in my comment a design change is required to achieve what you want.
Here is my suggestion:
Since your issue is with the structure of the product table because you don't know what each company wants to store as info for their product you can do it this way : (I ll explain later).
public class Company
{
[Key]
public int Id { get; set; }
[Display(Name = "Name")]
[Required]
public string Name { get; set; }
[Display(Name = "Description")]
public string Description { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
public virtual ICollection<Product> Prodcuts { get; set; }
}
public class Product
{
[Key]
public int Id { get; set; }
[Display(Name="Name")]
[Required]
public string Name { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
[Required]
[ForeignKey("Company")]
[Display(Name = "Company")]
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<ProductField> Fields { get; set; }
}
public class ProductField
{
[Key]
public int Id { get; set; }
[Display(Name = "Value")]
[Required]
public string Value { get; set; }
[Required]
[ForeignKey("Product")]
[Display(Name = "Product")]
public int ProductID { get; set; }
public virtual Product Product { get; set; }
[Required]
[ForeignKey("Field")]
[Display(Name = "Field")]
public int FieldID { get; set; }
public virtual Field Field { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
}
public class Field
{
[Key]
public int ID { get; set; }
[MaxLength(100)]
[Index("ActiveAndUnique", 1, IsUnique = true)]
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "Description")]
public string Description { get; set; }
[Required]
[Display(Name = "Created date")]
[DataType(DataType.DateTime)]
public DateTime CreatedDate { get; set; }
}
Explanation of the code:
This approach gives you more control over your data without having to create a table for each product info.
Company:
I started by creating a company table with a navigation property that will lazy load all the products related to it.(if lazy loading is enabled)
Then In the product table I added a FK to reference the company.
Field:
Since you mentioned that you don't know what a company will have as product info , you can create a new field and link it to a product using the ProductField table .
ProductField:
This table will act as a "Many to Many" between your product, and field as a result you can add as many field to a new product without having to modify the structure of your product table or create a new one . You can also reuse the same field if company number 3 needs it.
USAGE:
Given we have a company named MyCompany.
MyCompany has a product named Car and the info required to be added to the car is Make, and Color.
We create two new fields called Make, and Color, then in the ProductField Table we add two new entries:
The first one will have:
The ID of the field "Make", The value "BMW", and a reference to the product with its id which is Car.
We do the same thing for color by referencing the the field "Color" and the product "Car".
Querying:
Now querying is simpler than having a table for each company product info.
Example:
var myProducts = _db.Products.Where(p=>p.CompanyID== "1").Include(p=>p.Fields).Tolist()
Again that's my take on it. Hope it helps.
In an MVC5 project, there are several master/detail entities as Group / Student shown below:
public class Group
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
//Navigation Properties
public virtual ICollection<GroupStudent> GroupStudents { get; set; }
}
public class GroupStudent
{
[Key]
public int Id { get; set; }
//Foreign key for Group
public int GroupId { get; set; }
//Foreign key for Student
public int StudentId { get; set; }
//Navigation Properties
public virtual Group Group { get; set; }
public virtual Student Student { get; set; }
}
public class Student
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
//Navigation Properties
public virtual ICollection<GroupStudent> GroupStudents { get; set; }
}
I created a new Controller for both: Group and Student and defined List, Create, Details and Edit methods ind View in the related Controllers. On the other hand, I need to make master / detail operations i.e. assigning the Students to the Groups. At this step I am not sure about a good approach regarding to this issue due to the following situations below:
Should I create a new Controller (i.e. GroupStudentController) in order to these master / detail operations? Or should I create the necessary methods in StudentController or GroupController? What is the true convention regarding to such a kind of master/detail operations?
No need to create an extra controller, just create a method in any of the existing controllers and choose the controller according to the business need of the required method.
Ex: If you have a method that should assign a student to a group and this action will be done from the students list page, then make it in the Students controller, if you have a page that will popup from the groups list to manage the students for the selected group, then make it in the groups controller.
Make your business drive.
I am creating a quiz creator module. There are 5 subjects. Each subject has 2000 questions.
Say, it's called "Create Full model test", and it will contain 100 questions, 20 questions from 5 subjects. So, 20*5=100
In the UI, creator will first select "Create full model test" from the drop down. Suppose if he select the "Full model test". He will have to select one subject among 5 subjects, then he will have to select 20 questions per subject. and then will have to save them into that "Full model test" segment.
If he selects English, he will have to select 20 questions, then he will have to select another subject, for example, physics, and will have to select another 20 questions for physics, then he will have to select maths and then will have to select another 20 questions and so on for maths. Finally 100 questions will be submitted by the form.
Now, my question is, how can I auto save the selected questions into that "Full model test" segment, so that he can continue saving 5 subject's questions before he submit the form.
Here is my Department model:
namespace MvcBCS.Models
{
public class Department
{
[Key]
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public string DepartmentCode { get; set; }
public virtual ICollection<Subject> Subject { get; set; }
public virtual ICollection<Section> Section { get; set; }
public virtual ICollection<Subsection> Subsection { get; set; }
}
}
Here is my Subject Model:
namespace MvcBCS.Models
{
public class Subject
{
[Key]
public int SubjectId { get; set; }
public string SubjectName { get; set; }
public string SubjectCode { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
public virtual ICollection<Section> Section { get; set; }
}
}
Here is the Section Model:
namespace MvcBCS.Models
{
public class Section
{
[ForeignKey("Department")]
public int? DepartmentId { get; set; }
public virtual Department Department { get; set; }
[ForeignKey("Subject")]
public int? SubjectId { get; set; }
public virtual Subject Subject { get; set; }
[Key]
public int SectionId { get; set; }
public string SectionName { get; set; }
}
}
Here is the subjection model:
namespace MvcBCS.Models
{
public class Subsection
{
[ForeignKey("Department")]
public int? DepartmentId { get; set; }
public virtual Department Department { get; set; }
[ForeignKey("Subject")]
public int? SubjectId { get; set; }
public virtual Subject Subject { get; set; }
[ForeignKey("Section")]
public int? SectionId { get; set; }
public virtual Section Section { get; set; }
[Key]
public int SubsectionId { get; set; }
public string SubsectionName { get; set; }
public string SubsectionCode { get; set; }
}
}
All that you need is just to store all temporary data on client side.
And when you get complete pasts or even full set of data - send it to server using POST/JSON/XML/etc.
I think it such case it will be better for you to use some kind of JavaScript frameworks, for example KnockoutJS, which with only few click's and couple lines of code will allows you to store all your data on client side in object-based model.
Anyway if you will use any framework or not, you should use JavaScript to store all of your data, including question, answers and subjects. JavaScript will allows you to store,validate and send all data that you need.
To make your work with JavaScript easier and faster - you can use jQuery library which contains functions for interaction with both DOM structure and server side.
To make learning of KnockoutJS easier just use tutorial: http://learn.knockoutjs.com
This is how I am going to attempt it.
business rules:
- full model test will be completed if all the subjects questions are submitted by certain user.
Assumption: we knew in advance that we have five subjects. Therefore
public enum Subject
{
English,
Physics,
...
}
Test Poco entity structure:
public class Test
{
public int Id{get;set;}
public User CreatedBy{get;set;}
public Subject Subject{get;set;}
public bool IsFullTestCompleted{get;set;}
public string Question1{get;set;}
public string Question2{get;set;}
...
}
Assumptions:
You can use partial views/ajax/Jquery/Angularjs/Knockout whatever suits you best in your problem context to implement it to auto save the changes
You will add necessary view model with appropriate validation annotations/attributes
you will add validation where necessary.
you will add/update the Test entity. like for first subject you will add data into Test entity, and subsequently you will update it.
Hope it will be helpful
I'm using C#.NET, Entity Framework 4.1 and the code first approach. From that i have a bunch of entities and one entity is related to another entity. They have a primary key/foreign key relation.
I'm also using ViewModels to specify which attributes may be scaffolded and i was hoping that the two related entities would create a dropdown list. But that's not happening.
This is just an example of how my entity relation looks, to illustrate my problem.
UserType entity:
public class UserType
{
public int UserTypeID { get; set; }
public string Type { get; set; }
public virtual ICollection<User> Users { get; set; }
}
User entity:
public class User
{
public int UserID { get; set; }
public int UserTypeID
public string Username { get; set; }
public bool IsAdmin { get; set; }
public virtual UserType UserType { get; set; }
}
My ViewModel:
[JsonObject(MemberSerialization = MemberSerialization.OptOut, IsReference = false)]
[DataContract]
public class UserViewModel : BaseViewModel
{
[Key]
public int UserID { get; set; }
public int UserTypeID { get; set; }
[required]
public string Username { get; set; }
}
So the UserViewModel is scaffolded. And i want it to create a dropdown list for the UserType. But right now it only creates an input field for UserTypeID. How can i make it show a dropdown list with the values of UserTypes?
The scaffolder has no idea that UserTypeId in your view model relates to an actual UserType class, and therefore has no way of knwoing where to get the static list of user types from.
If you look at the example you referenced, it also includes a navigation property to the foreign key class.