I am not sure how to get the results from joining the tables in controllers.
There're 3 tables 'Groups' 'Users' 'GroupUser' (bridge table).
public class Group
{
[Key]
public int GroupID { get; set; }
public string Group_Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
[Key]
public int UserID { get; set; }
public string User_Name { get; set; }
public ICollection<Group> Groups { get; set; }
}
I also have this EFContext class
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Group>()
.HasMany(g => g.Users)
.WithMany(u => u.Groups)
.Map(m =>
{
m.MapLeftKey("UserID");
m.MapRightKey("GroupID");
m.ToTable("GroupUSer");
});
Do I also need to build a GroupUser class (to represent the GroupUser bridge table)?
Then how do I get the results when joining the 3 tables to get list of groups and users?
GroupViewModel model = new GroupViewModel
{
Groups = .... // this should be a linq statement that get results
that contains all groups and users
};
The equal sql statemen would be
select *
from Group g
join GroupUser gu on g.GroupID=gu.GroupID
join User u on u.UserID=gu.UserID
No, intermediate class is not needed.
The main point of an ORM (Object-Relational Mapper, which is what Entity Framework is) is to abstract away the database and let you work in a pure object-oriented way. Intermediate tables are definitely a database term and are not needed here.
The only reason I can think of that may lead you to create an intermediate class is when you need a "payload" (an extra meta-data) on the association. For example:
public class User
{
public int Id { get; set; }
public int Email { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
public class Account
{
public int Id { get; set; }
public virtual ICollection<User> Users { get; set; }
}
Now, if you want the user-to-account association to define whether the association is of "Own the account" type (Administrator), you can do something like:
public class User
{
public int Id { get; set; }
public int Email { get; set; }
public virtual ICollection<AccountUserAssociation> Accounts { get; set; }
}
public class Account
{
public int Id { get; set; }
public virtual ICollection<AccountUserAssociation> Users { get; set; }
}
public class AccountUserAssociation
{
public virtual User User { get; set; }
public virtual Account Account { get; set; }
public AssociationType AssociationType { get; set; }
}
public enum AssociationType { Regular, Administrator }
Related
I created a post with an issue and another issue.
These can be looked at for references but i consider them as handled.
My question arising from these issues and the action i (need or not need) to apply bothers me because i don't quite understand EF its behavior and expectations.
I have a Product, PurchasePrice and SalesPrice entity where my initial thought was that 1 Product can have multiple PurchasePrices but that 1 PurchasePrice only can exist in 1 Product (same for SalesPrice).
Therefore these relations:
// NOTE that BaseEntity just has a "int ID" prop and datetimes/stamps
public class Product : BaseEntity
{
public ICollection<PurchasePrice> PurchasePrices { get; set; }
public ICollection<PurchasePrice> SalesPrices { get; set; }
}
public class PurchasePrice:BaseEntity
{
public Product Product { get; set; }
}
public class SalesPrice:BaseEntity
{
public Product Product { get; set; }
}
Now, lets add a Supplier Entity to it because that is why i seperate Sales & Purchase apart and don't create an Enum out of it, because 1 Product (in database) can have multiple suppliers, each having their own Sales/Purchase prices AND another Productnumber value.
So above becomes:
public class Product : BaseEntity
{
public ICollection<PurchasePrice> PurchasePrices { get; set; }
public ICollection<PurchasePrice> SalesPrices { get; set; }
// added
public ICollection<Supplier> Suppliers { get; set; }
}
public class PurchasePrice:BaseEntity
{
public Product Product { get; set; }
// added
public Supplier Supplier { get; set; }
}
public class SalesPrice:BaseEntity
{
public Product Product { get; set; }
// added
public Supplier Supplier { get; set; }
}
// added Entity Supplier into the party
public class Supplier : BaseEntity
{
public ICollection<Product> Products { get; set; }
public ICollection<PurchasePrice> PurchasePrices { get; set; }
public ICollection<SalesPrice> SalesPrices { get; set; }
}
Lets continue a little furhter because it doesn't stop there, i want to keep track of these Product-Supplier-Prices relations so i created a Entity called 'ProductSupplierForContract' which would have the following structure:
public class ProductSupplierForContract:BaseEntity
{
public string ProductnumberValue { get; set; }
public int Product_Id { get; set; }
public int Supplier_Id { get; set; }
public int? Contract_Id { get; set; }
public virtual Product Product { get; set; }
public virtual Supplier Supplier { get; set; }
public virtual Contract Contract { get; set; }
}
Finally i have a Contract Entity which has the following structure:
public class Contract:BaseEntity
{
[Required]
public ICollection<Product> Products { get; set; }
public ICollection<ProductSupplierForContract> ProductSupplierForContracts { get; set; }
}
So Product becomes:
public class Product : BaseEntity
{
public ICollection<PurchasePrice> PurchasePrices { get; set; }
public ICollection<PurchasePrice> SalesPrices { get; set; }
public ICollection<Supplier> Suppliers { get; set; }
// added
public ICollection<Contract> Contracts { get; set; }
}
Custom Seeding (inherits from DropCreateDatabaseAlways):
protected override void Seed(ApplicationDbContext context)
{
PurchasePrice purchaseprice = new PurchasePrice((decimal)17.70);
ctx.PurchasePrices.Add(purchaseprice);
Product product1 = new Product("test product 1",purchaseprice);
ctx.Products.Add(product1);
base.Seed(ctx);
}
I also have mappings defined in Fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// setting the Product FK relation required + related entity
modelBuilder.Entity<Entity.ProductSupplierForContract>().HasRequired(psfc => psfc.Product)
.WithMany(p => p.ProductSupplierForContracts)
.HasForeignKey(psfc => psfc.Product_Id);
// setting the Supplier FK relation required + related entity
modelBuilder.Entity<Entity.ProductSupplierForContract>().HasRequired(psfc => psfc.Supplier)
.WithMany(s => s.ProductSupplierForContracts)
.HasForeignKey(psfc => psfc.Supplier_Id);
// setting the Contract FK relation required + related entity
modelBuilder.Entity<Entity.ProductSupplierForContract>().HasOptional(psfc => psfc.Contract)
.WithMany(c => c.ProductSupplierForContracts)
.HasForeignKey(psfc => psfc.Contract_Id);
}
Now, initially i didn't had any issues and i really really don't understand what has brought up this sudden change that i now got duplicates Products when i seed my database. I can strip it down to just adding a simple PurchasePrice with a value and a Product having a reference to this PurchasePrice and there is my duplicate.
Changing the relation inside the PurchasePrice class of the Entity Product, to a ICollection doesn't create a duplicate but i don't want this collection because it is not a Many to Many relation ...
I have tried enormous amounts of things but nothing that "resolved" this (if this is a problem to start with, for me yes but maybe not for EF) like removing inhertance BaseEntity, changinge Mapping (Fluent AND annotations), changed the way i seeded and initialized everthing, defining ID's myself, you name it ...
Mind that the purpose is not to optimize the way i seed in anyway but to have a decent working Model AND to understand what EF does and what it wants.
My questions:
Why is this duplicate occuring/appearing ?
If i want to be able to have 1 instance holding the relation of
Price-Supplier-Product-Contract, how should i do this? Answer is here
I fixed my problem by redesigning the model. I have added a additional Entity ProductForSupplier which holds the relation of a Product & Supplier and a Productnumber.
public class ProductForSupplier:BaseEntity
{
public string ProductnumberValue { get; set; }
[Required]
public Product Product { get; set; }
[Required]
public Supplier Supplier { get; set; }
}
Added a Entity ProductsForContract which will hold the amount of a Product-Supplier relation for 1 contract:
public class ProductsForContract
{
public int ProductsForContractId { get; set; }
public int Amount { get; set; }
public ProductForSupplier ProductForSupplier { get; set; }
public Contract Contract { get; set; }
}
And the Existing Entity ProductSupplierForContract becomes:
public class ProductSupplierForContract:BaseEntity
{
public ICollection<ProductsForContract> ProductsForContract { get; set; }
[Required]
public Contract Contract { get; set; }
}
This gives me the flexibility to keep relations of any kind between the entities and also has taken care of the duplicate (which i still don't know the cause of).
This is done using MVC .net framework and entity framework "database first" approach. There is a many to many relationship between two tables. They are connected through third table that has combined key as id from first table and id from second table.
public class ManyToManyTable
{
[Required]
[Key, Column(Order=0)]
public int firsttableid { get; set; }
[Required]
[Key, Column(Order=1)]
public int secondtableid { get; set; }
public int something { get; set; }
[ForeignKey("firsttableid")]
public virtual FirstTable firstTable { get; set; }
[ForeignKey("secondtableid")]
public virtual SecondTable secondTable { get; set; }
}
First and Second table have some id which is primary key.
I want to create View and Controller method that enables master detail entry form for this ManyToManyTable. that would have FirstTable in Master and SecondTAble in details, and all to be saved in ManyToManyTable when button Save is pressed.
Of course, both First and Second Table have this property:
public virtual ICollection<ManyToManyTable> ManyToManyTables { get; set; }
What is the easiest way to implement cases like this one?
Thank you!
EF has a default conventions for many-to-many relationships. No need to create specific
mapping class. You have to include navigation properties in both "FirstTable" and "SecondTable" Class as shown below.
public class FirstTable
{
public FirstTable()
{
secondTableProperties = new HashSet<SecondTable>();
}
public int Id { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public virtual ICollection<SecondTable> secondTableProperties { get; set; }
}
public class SecondTable
{
public SecondTable()
{
FirstTableProperties = new HashSet<FirstTable>();
}
public int Id { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public virtual ICollection<FirstTable> FirstTableProperties { get; set; }
}
Remove mapping class from DBContext , only include above two classes. Build and run the application , EF will automatically create a Mapping table in SQL server. Usually the Mapping table contains only the primary keys of other two tables.
You can use Fluent API to take some control on the created mapping table
modelBuilder.Entity<FirstTable>()
.HasMany<SecondTable>(s => s.FirstTableProperties)
.WithMany(c => c.secondTableProperties)
.Map(cs =>
{
cs.MapLeftKey("FirstTableId");
cs.MapRightKey("SecondTableId");
cs.ToTable("ManyToManyTable");
});
If you want to work with a join table with additional properties, above mentioned many-to-many relationship won't work . In that case you will have to create two one-to-many relationships as shown below.
public class FirstTable
{
public int Id { get; set; }
public int MyProperty2 { get; set; }
public virtual ICollection<ManyToManyTable> manytomany { get; set; }
}
public class SecondTable
{
public int Id { get; set; }
public int MyProperty2 { get; set; }
public virtual ICollection<ManyToManyTable> manytomany { get; set; }
}
public ManyToManyTable
{
[Required]
[Key, Column(Order=0)]
public int firsttableid { get; set; }
[Required]
[Key, Column(Order=1)]
public int secondtableid { get; set; }
public int AdditionalProperty { get; set; }
public virtual FirstTable first { get; set; }
public virtual SecondTable Second { get; set; }
}
i am trying to define a database model in code-first to see and display which user is assigned as a specialist for the record data.
I have a very simple model for the user:
public class User
{
public int ID { get; set; }
public string userName { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
....
}
Next I have defined two (simple) models which define the data that can be edited by the user and the specialist should be assigned to using a dropdownlist:
public class Order
{
public int ID { get; set; }
public string orderNumber { get; set; }
public int specialistID { get; set; }
public virtual User specialist{ get; set; }
}
public class Part
{
public int ID { get; set; }
public string partNumber { get; set; }
public string description { get; set; }
public int specialistID { get; set; }
public virtual User specialist{ get; set; }
}
What kind of relation between the models can be used without having a navigation property for each table in the User model?
Do I need to use additional tables to define the relationship: User.Id-Order.specialistID and the relationship: User.Id-Part.specialistID ?
Is there a smarter way out-of-the-box by Entity Framework?
Many thanks for your answers.
Pascal
By default when you add forign-key constraint to the many-to-one table the Entity Framework add virtual property to the entity class and virtual ICollection to the User.
I am new to Entity Framework and Asp.NET, and therefore, struggling with creating database relationships within the Entity Framework.
I have two SQLite tables (Ticket and User) and have setup my entity models as follows:
public class Users
{
[ForeignKey("id")]
public int id { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public virtual ICollection<Tickets> Tickets { get; set; }
}
public class Tickets
{
public int id { get; set; }
public string summary { get; set; }
public string description { get; set; }
public string c_location { get; set; }
public string c_store_device { get; set; }
public string category { get; set; }
public DateTime? created_at { get; set; }
public DateTime? closed_at { get; set; }
public int priority { get; set; }
public int? assigned_to { get; set; }
public DateTime? due_at { get; set; }
public DateTime? updated_at { get; set; }
public string status { get; set; }
public virtual Users Users { get; set; }
}
I am trying to use Entity Framework 7 to export an IEnumerable<Tickets> that includes the User assigned to each Ticket.
I have tried to create my model relationship in MyDBContext as a single User can have multiple Tickets, and also has a foreign key associated in my Sqlite database (Tickets.assigned_to = User.id):
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Users - > many Tickets
modelBuilder.Entity<Users>()
.HasMany(p => p.Tickets)
.WithOne(e => e.Users)
.HasForeignKey(p => p.assigned_to);
}
My result ends up with Ticket data being exported, but against every ticket I see a null value for User:
[{"id":10002,...,"Users":null}]
When I use .Include() within my Repository to include each User like this:
public IEnumerable<Tickets> GetAll()
{
return _db.Tickets.Include(t => t.Users).ToList();
}
It results in the error
HTTP Error 502.3 - Bad Gateway
The specified CGI application encountered an error and the server terminated the process.
What I'm trying to retrieve is data that looks like:
{"Ticket";[{"id":10002,..."status":"closed"}],"Users":[{"id":"1"..."email":"johndoe#someplace.com"}]}
I know it probably has something to do with my relationship model, but I cannot work out what I am doing wrong.
First you should really derive your Users from IdentityUser. It helps when trying to wire up the relationship, but I will give you the answer based on your current models. Your ForeignKey property should be on the child entity. By naming conventions, which is what EF uses by default, your public Users Users works better if you put a public int UsersId. Then essentially what EF will do is from your public Users Users it will go to the Users table. Then it looks for the ForeignKey which is set to Id, so now we are in the Users Table looking at the id property. Then it looks for the naming convention UsersId and if it sees it, it will set that property to the value that it saw from the Users Table Id column.
Try using this
public class Users
{
public int id { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public virtual ICollection<Tickets> Tickets { get; set; }
}
public class Tickets
{
public int id { get; set; }
public string summary { get; set; }
public string description { get; set; }
public string c_location { get; set; }
public string c_store_device { get; set; }
public string category { get; set; }
public DateTime? created_at { get; set; }
public DateTime? closed_at { get; set; }
public int priority { get; set; }
public DateTime? due_at { get; set; }
public DateTime? updated_at { get; set; }
public string status { get; set; }
[ForeignKey("Id")]
public int UsersId { get; set; }
public virtual Users Users { get; set; }
}
and for your Fluent API configuring
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Users - > many Tickets
modelBuilder.Entity<Users>()
.HasMany(p => p.Tickets)
.WithOne();
}
Now all that does is create the relationship. In order to view the specific items you want to view, use a ViewModel. So, pull the two lists you want from where you want. Then use logic to separate the list how you want them to display.
public class UsersViewModel()
{
public UsersViewModel(Users user, List<Tickets> tickets)
{
this.first_name = user.first_name;
this.last_name = user.last_name;
this.email = user.email;
this.Tickets = new List<Tickets>();
foreach(var ticket in tickets)
{
if(ticket.UserId == user.Id)
{
this.Tickets.Add(ticket)
}
}
}
public string first_name { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public List<Tickets> Tickets { get; set;}
}
then in your controller make your list
public IActionResult Index()
{
var usersList = _repository.Users.ToList();
var ticketsList = _repository.Tickets.ToList();
var model = new List<UsersViewModel>();
foreach(var user in usersList)
{
var listItem = new UsersViewModel(user, ticketsList);
model.Add(listItem);
}
return View(model);
}
or use a Linq query
public IActionResult Index()
{
var usersList = _repository.Users.ToList();
var model = new List<UsersViewModel>();
foreach(var user in usersList)
{
var ticketsList = from x in _repository.Tickets where x.UserId.Equals(user.Id) select x;
var listItem = new UsersViewModel(user, ticketsList);
model.Add(listItem);
}
return View(model);
}
then at the top of your view you should have
#model IEnumerable<UsersViewModel>
I have those 2 Models
public class BranchEmployees
{
public int ID { get; set; }
[Required, Column(Order = 0), Key]
public string ApplicationUserID { get; set; }
[Required, Column(Order = 1), Key]
public int BranchID { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
public virtual ICollection<Branch> Branch { get; set; }
}
public class Branch
{
public int ID { get; set; }
public string BranchName { get; set; }
[Required]
public string ApplicationUserID { get; set; }
public ApplicationUser User { get; set; }
public virtual ICollection<BranchEmployees> BranchEmployees { get; set; }
}
public class ApplicationUser
{
//rest of the code
}
UPDATE
I have everything set up but what I want is the query that gets me the Employees whose IDs are in the branch employees table
, I'm using entity framework code first with MVC 5 , how do I do it ?
Assuming that your ApplicationUser class will have a navigational property called BranchEmployees, here is the query that gets me the Employees whose IDs are in the branch employees table
List<ApplicationUsers> employeeNames =
dbContext
.ApplicationUsers
.Where(au => au.BranchEmployees
.Count() > 0).ToList();
Also, can you provide whole model including ApplicationUser? I also wonder why you do not prefer BranchEmployees to inherit from ApplicationUser.
You don't need a class that indicates a many-to-many relation between two tables when you do code-first. The key here is to create virtual properties of those classes. Lets say you have a class Student and class Course. Students can be in many Courses and Courses can have many Students. To generate a database using these models the classes should look like this:
public class Student
{
private ICollection<Course> _courses;
public Student()
{
this._courses = new HashSet<Course>();
}
[Key]
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Course> Courses
{
get { return this._courses; }
set { this._courses = value; }
}
}
And for Course:
public class Course
{
private ICollection<Student> _students;
public Course()
{
this._students = new HashSet<Student>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students
{
get { return this._students; }
set { this._students = value; }
}
}
I hope that this can help you solve your issue.