My question is two fold and related to each other
How to define or write a property inside a class which represents a foreign key and how do you populate it using dapper
Tech i am working with :
Using dapper as an ORM
Database in MySQL
Using WPF/C# .netframework 4.7
For Ex : lets say there is are two entities customer and a product,
a customer has a Customer table and a product has a Product table
A customer can have zero or many products and a single product should only relate to one person, so this is a one to many relationship from customer to product
NOTE : I know there can be more customers for a single product and that a customer can buy more than one product, THIS is just a simple example where the problem is not the the database design but how to define those fields that are foreign keys, as properties in a class where the ORM being used is dapper and the DATABASE is MySQL
in Customer table there are 2 fields Customer ID and Customer name
in Product table there are 3 field , Product ID and Product Name and C_CustomerID, here C_CustomerID is the foreign key that references the Customer Table's Primary KEY
so you generally make two classes right a customer and a product Class with its properties
for Instance Customer table has
public int CustomerID { get; set; }
public string CustomerName { get; set; }
and Product table has
public int ProductID { get; set; }
public string ProductName { get; set; }
The problem is how to define the foreign key
is it like this
Option 1.
public int C_CustomerID { get; set; }
Option 2.
public List<CustomerID> C_CustomerID { get; set; }
Option 3.
public List<Customer> C_CustomerID { get; set; }
Option 4.
public List<Customer> Customer { get; set; }
and how do you populate these, do you use stored procedures or do you use functions from dapper or manual C# code
You should model your data after "real life", not after the database. I will skip your requirement that the product should know who bought it, that's not a simplification, but will only make it harder. A Customer can buy multiple products and should have a list of the products bought. Normally it would be done like this:
public class Customer
{
public int CustomerID { get; set; }
public string CustomerName
public List<Product> Products { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
}
The foreign key relationship is an implementation detail of your database, it does not belong in your data model. You should therefore create a link table CustomerProductLink containing a CustomerId and a ProductId, here you will add a row for every Product bought by any Customer. The link table is also just an implementation detail, you shouldn't model it.
When you load a Customer you tailor your SQL to only load the products that are bought by that customer, something like:
SELECT * FROM Customers AS C
INNER JOIN CustomerProductLink AS L ON C.CustomerID = L.CustomerID
INNER JOIN Products AS P ON P.ProductId = L.ProductID
WHERE C.CustomerID = #CustomerID;
You load the customer using Dapper multimapping.
This way your model describes the data domain as it is, and how the relationship is modelled gets hidden in your data layer.
After writing this, I found this tutorial describing pretty much the same thing.
In Product Model add
public int CustomerID { get; set; }
public List<Customer> Customers { get; set; }
Related
I've designed a database, paying a lot of attention to normalization.
Here's a piece of it:
First of all, If you notice any issues with this design, feel free to let me know.
The goal is to have companies, each of which have some departments.
Departments can be shared between companies.
As in:
Company 1 can have Department 1, 2 and 3.
Company 2 can have Department 1, 5, 8 and 9.
The BusinessUnits will have access to departments.
But it depends on the company to which a department is linked.
BusinessUnit 1 may have permission to access Department 1 of Company 1, but should not be able to access Department 1 of Company 2.
The CompanyDepartment config table is pretty obvious.
It links a Company to (possibly) multiple departments.
The CompanyDepartmentBusinessUnit config table is used to link BusinessUnits to Departments of a Company.
In this table, the CompanyId and DepartmentId form a composite Foreign Key to the primary key of CompanyDepartment (which is: CompanyId and DepartmentId as well).
I'm using a Database-First approach in Entity Framework.
For the simple junction tables, I've overwritten the OnModelCreating method in my DbContext.
An example of how I did this:
My question now is: how do I do this for the CompanyDepartmentBusinessUnit relation?
Say that my user chose to see the departments of Company 1.
I want to filter all the Departments that are linked to Company 1 but are also visible to the BusinessUnit in which the user resides (for instance Business Unit 2).
Thank you in advance and enjoy your holidays!
EF allows you to use implicit junction table only if (1) it has no additional columns and (2) if it's not referenced by other entity different than the two ends of the many-to-many relationships.
CompanyDepartment satisfies the condition (1), but not (2) because it's referenced from CompanyDepartmentBusinessUnit, hence you need to use explcit entity with two one-to-many relationships.
Once you do that, it can be seen that now CompanyDepartmentBusinessUnit satisfies both conditions, hence can be modelled with implicit junction table for BusinessUnit and CompanyDepartment.
With that being said, the final model would be something like this:
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<CompanyDepartment> DepartmentLinks { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<CompanyDepartment> CompanyLinks { get; set; }
}
public class BusinessUnit
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsPersonal { get; set; }
public ICollection<CompanyDepartment> CompanyDepartments { get; set; }
}
public class CompanyDepartment
{
public int CompanyId { get; set; }
public int DepartmentId { get; set; }
public Company Company { get; set; }
public Department Department { get; set; }
public ICollection<BusinessUnit> BusinessUnits { get; set; }
}
and taking into account the default EF conventions, with the following minimal fluent configuration:
modelBuilder.Entity<Company>().ToTable("Company");
modelBuilder.Entity<Department>().ToTable("Department");
modelBuilder.Entity<BusinessUnit>().ToTable("BusinessUnit");
modelBuilder.Entity<CompanyDepartment>().ToTable("CompanyDepartment");
modelBuilder.Entity<CompanyDepartment>()
.HasKey(e => new { e.CompanyId, e.DepartmentId });
modelBuilder.Entity<CompanyDepartment>()
.HasMany(e => e.BusinessUnits)
.WithMany(e => e.CompanyDepartments)
.Map(m => m
.MapLeftKey("CompanyId", "DepartmentId")
.MapRightKey("BusinessUnitId")
.ToTable("CompanyDepartmentBusinessUnit")
);
I am trying to make relationships in Entity Framework and my SQL Server with Fluent API. Here's how I insert:
CREATE TABLE Product (
id INT PRIMARY KEY NOT NULL IDENTITY(1,1),
name VARCHAR(255)
);
CREATE TABLE OrderLine (
id INT PRIMARY KEY NOT NULL IDENTITY(1,1),
productId INT NOT NULL,
FOREIGN KEY (productId) REFERENCES Product(id)
);
Now, product has many orderLines, but does not know them and does not need to know them. We don't need to pick a single product and count the amount of orderlines, for example. Here's how it looks:
public class Product {
public int id { get; set; }
public string name { get; set; }
public ICollection<OrderLine> orderLines { get; set; } //not needed but we'll have it anyway
}
public class OrderLine {
public int id { get; set; }
public int productId { get; set; }
public Product product { get; set; }
}
According to entityframeworktutorial.net this could should work:
modelBuilder.Entity<Product>()
.HasMany<OrderLine>(s => s.orderLines)
.WithRequired(s => s.product)
.HasForeignKey(s => s.productId);
However, product is simply null when I try to pull it out of the database. Is this not a one-to-many relationship? I'm not quite sure where I went wrong.
I'm not sure if this is the entire problem, as theres not that much information there, but in order to lazy load (which it looks like you are trying) the navigation properties must be marked as virtual like so...
public virtual Product product { get; set; }
otherwise EF's proxy can't wrap them and do its stuff.
If you have the lazyloading not enabled EF dont get the related entities by default.
Try include the entities that you need when you selecting the data:
var product = _db.OrderLine.Include(p=>p.Product)
Other solution is activate the lazyloading on modelBuilder, but be aware that to complex queries or large tables this can cause a big hit on application performance.
So we have this legacy DB to which we have no control and cannot modify in any way or form. Any of the tables in said DB have foreign keys and we are using EF 6 to access it. We created our model and, up until now, things were OK.
Now we are dealing with the following, very common, structure:
[Product] [ProductGroupDetails] [ProductGroup]
ProductID ProductID ProductGroupID
... ProductGroupID .....
Both Product and ProductGroup tables have additional fields which are not relevant right now. The two fields in the ProductGroupDetails conform its primary key.
After adding these three tables to the model I manually added the associations between them by right clicking on the diagram and selected the Add New -> Association option.
After running the tt file I get these entities:
public partial class ProductGroup
{
public ProductGroup()
{
this.ProductGroupDetails = new HashSet<ProductGroupDetail>();
}
public int ProdGroupID { get; set; }
public virtual ICollection<ProdGroupDetail> ProdGroupDetails { get; set; }
}
public partial class ProdGroupDetail
{
public int ProdGroupID { get; set; }
public string ProductID { get; set; }
public virtual Product Products { get; set; }
public virtual ProdGroupHead ProdGroupHead { get; set; }
}
public partial class Product
{
public Product()
{
this.ProductGroupDetails = new HashSet<ProductGroupDetail>();
}
public string ProductID { get; set; }
public virtual ICollection<ProductGroupDetail> ProductGroupDetails { get; set; }
}
I would have expected for the ProductGroupDetails entity not to exist, the Product one having a ProductGroups collection and the ProductGroup to have a Products collection.
Of course this makes it challenging when creating a new Product Group since to the new ProductGroup instance I would have to add an instance of ProductGroupDetail for each group but the ProdGroupID property would be empty since I do not know it yet. Normally I would just add instances of Product to the Products collection and upon save EF would work its magic.
Is there a way I can coerce the edmx so that the ProductGroupDetail entity is not created and the other ones end up having the expected collections?
I am building ASP.NET MVC 5 application. I am using Entity Framework 6.1, code first approach to generate a database. I have a many-to-many relationship between Product and Category.
public class Product
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
// navigation
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public long Id { get; set; }
public string Name { get; set; }
// navigation
public virtual ICollection<Product> Products { get; set; }
}
In the Dbcontext class I override OnModelCreating method to create table for many-to-many relationship as below:
modelBuilder.Entity<Product>().HasMany<Category>(s => s.Categories).WithMany(c => c.Products)
.Map(cs =>
{
cs.MapLeftKey("ProductId");
cs.MapRightKey("CategoryId");
cs.ToTable("ProductCategories");
});
The table comes out as joining the two foreign keys. How do I add an Id (as primary key) to this junction table?
ProductCategories
- Id // add id as primary key
- ProductId
- CategoryId
Let me expand #bubi's answer:
By default, when you define many-to-many relationship (using attributes or FluentAPI), EF creates it (add additional table to DB) and allows you to add many products to a category and many categories to a product. But it doesn't allow you to access the linking table rows as entities.
If you need such feature, for example you what to manage these links some way like mark them as "deleted" or set a "priority", you need to:
Create new Entity (ProductCategoryLink)
Add it to your Context as another DbSet
Update relations in Product and Category entities accordingly.
For you it could like:
Entities
public class Product
{
[Key]
public long ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[InverseProperty("Product")]
public ICollection<ProductCategoryLink> CategoriesLinks { get; set; }
}
public class Category
{
[Key]
public long CategoryId { get; set; }
public string Name { get; set; }
[InverseProperty("Category")]
public ICollection<ProductCategoryLink> ProductsLinks { get; set; }
}
public class ProductCategoryLink
{
[Key]
[Column(Order=0)]
[ForeignKey("Product")]
public long ProductId { get; set; }
public Product Product { get; set; }
[Key]
[Column(Order=1)]
[ForeignKey("Category")]
public long CategoryId { get; set; }
public Category Category { get; set; }
}
I used attribute-way to define relations as I prefer this approach more. But you can easily replace it by a FluentAPI with two one-to-many relations:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Product to Links one-to-many
modelBuilder.Entity<ProductCategoryLink>()
.HasRequired<Product>(pcl => pcl.Product)
.WithMany(s => s.CategoriesLinks)
.HasForeignKey(s => s.ProductId);
// Categories to Links one-to-many
modelBuilder.Entity<ProductCategoryLink>()
.HasRequired<Category>(pcl => pcl.Category)
.WithMany(s => s.ProductsLinks)
.HasForeignKey(s => s.CategoryId);
}
Context
It's not required but most likely you'll need to save links directly to context, so let's define a DbSet for them.
public class MyContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category > Categories{ get; set; }
public DbSet<ProductCategoryLink> ProductCategoriesLinks { get; set; }
}
Two ways of implementation
Another reason why I used attributes to define relations is that it shows (both marked with [Key] attribute (also pay attention to [Column(Order=X)] attribute])) that two FKs in ProductCategoriesLink entity become a composite PK so you don't need to define another property like "ProductCategoryLinkId" and mark it as a special PK field.
You always could find desired linking entity all you need is just both PK's:
using(var context = new MyContext)
{
var link = context.ProductCategoriesLinks.FirstOrDefault(pcl => pcl.ProductId == 1
&& pcl.CategoryId == 2);
}
Also this approach restricts any chance to save several links with the same Product and Category as they are complex key. If you prefer the way when Id is separated from FK's you'll need to add UNIQUE constraint manually.
Whichever way you choose you'll reach your aim to manipulate the links as you need and add additional properties to them if you need.
Note 1
As we defined many-to-many links as separate entity Product and Category don't have direct relation to each other anymore. So you'll need to update your code:
Instead of adding Product directly to Category or Category directly to Product now you need to define a ProductCategoryLink entity and save it using one of three ways depending on your logic's context:
public void AddProductToCategory(Product product, Company company)
{
using (var context = new MyContext())
{
// create link
var link = new ProductCategoryLink{
ProductId = product.ProductId, // you can leave one link
Product = product, // from these two
CategoryId = category.CategoryId, // and the same here
Category = category
};
// save it
// 1) Add to table directly - most general way, because you could
// have only Ids of product and category, but not the instances
context.ProductCategoriesList.Add(link);
// 2) Add link to Product - you'll need a product instance
product.CategoriesLinks.Add(link);
// 3) Add link to Category - you'll need a category instance
category.ProductLinks.Add(link);
context.SaveChanges();
}
}
Note 2
Also remember as your properties now navigate to ProductCategoryLinks (not to Products for categories and not for Categories for products) if you need to query the second linked entity you need to .Include() it:
public IEnumerable<Product> GetCategoryProducts(long categoryId)
{
using (var context = new MyContext())
{
var products = context.Categories
.Include(c => c.ProductsCategoriesLinks.Select(pcl => pcl.Product))
.FirstOrDefault(c => c.CategoryId == categoryId);
return products;
}
}
UPD:
There is a same question with detailed answer on SO:
Create code first, many to many, with additional fields in association table
If you need a model like you posted (a "clean" model) you need to disable automatic migration and create the table by yourself. EF will not handle Id so it has to be autonumbering.
If you need to handle and see Id inside your app, your model is different and the Junction table must have a class in the model.
I have a table called Products which obviously contains products.
However, I need to create related products. So what I've done is create a junction table called product_related which has two PKs. ProductID from Products table and RelatedID also from Products table.
I already use EF and have set up everything on other tables. How should I add this properly in order to create a relationship with products as such:
product.Products.Add(product object here). Of course here product represent a product object that I've fetched from the db using db.Products.FirstOr....
How should I do this properly ? A many to many to the same table?
Thanks.
In order to create a many-to-many relationship with Database-First approach you need to setup a database schema that follows certain rules:
Create a Products table with a column ProductID as primary key
Create a ProductRelations table with a column ProductID and a column RelatedID and mark both columns as primary key (composite key)
Don't add any other column to the ProductRelations table. The two key columns must be the only columns in the table to let EF recognize this table as a link table for a many-to-many relationship
Create two foreign key relationships between the two tables:
The first relationship has the Products table as primary-key-table with the ProductID as primary key and the ProductRelations table as foreign-key-table with only the ProductID as foreign key
The second relationship also has the Products table as primary-key-table with the ProductID as primary key and the ProductRelations table as foreign-key-table with only the RelatedID as foreign key
Enable cascading delete for the first of the two relationships. (You can't do it for both. SQL Server won't allow this because it would result in multiple cascading delete paths.)
If you generate an entity data model from those two tables now you will get only one entity, namely a Product entity (or maybe Products if you disable singularization). The link table ProductRelations won't be exposed as an entity.
The Product entity will have two navigation properties:
public EntityCollection<Product> Products { get { ... } set { ... } }
public EntityCollection<Product> Products1 { get { ... } set { ... } }
These navigation collections are the two endpoints of the same many-to-many relationship. (If you had two different tables you wanted to link by a many-to-many relationship, say table A and B, one navigation collection (Bs) would be in entity A and the other (As) would be in entity B. But because your relationship is "self-referencing" both navigation properties are in entity Product.)
The meaning of the two properties are: Products are the products related to the given product, Products1 are the products that refer to the given product. For example: If the relationship means that a product needs other products as parts to be manufactured and you have the products "Notebook", "Processor", "Silicon chips" then the "Processor" is made of "Silicon chips" ("Silicon chips" is an element in the Products collection of the Processor product entity) and is used by a "Notebook" ("Notebook" is an element in the Products1 collection of the Processor product entity). Instead of Products and Products1 the names MadeOf and UsedBy would be more appropriate then.
You can safely delete one of the collections from the generated model if you are only interested in one side of the relationship. Just delete for example Products1 in the model designer surface. You can also rename the properties. The relationship will still be many-to-many.
Edit
As asked in a comment the model and mapping with a Code-First approach would be:
Model:
public class Product
{
public int ProductID { get; set; }
public ICollection<Product> RelatedProducts { get; set; }
}
Mapping:
public class MyContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.HasMany(p => RelatedProducts)
.WithMany()
.Map(m =>
{
m.MapLeftKey("ProductID");
m.MapRightKey("RelatedID");
m.ToTable("product_related");
});
}
}
Lets take your Example:
Related table
Related_id PK
Related_name
Date
Product Table
Product_id PK
Related_id FK
Product_Name
Date
How to Represent it in EF
Related Model Class named as RelatedModel
[Key]
public int Related_id { get; set; }
public string Related_name {get;set}
public Datetime Date{get;set;}
Product Model Class named as ProductModel
[Key]
public int Product_id { get; set; }
public string Product_name {get;set}
public string Related_id {get;set}
public Datetime Date{get;set;}
[ForeignKey("Related_id ")] //We can also specify here Foreign key
public virtual RelatedModel Related { get; set; }
In this way we can Create Relations between Two table
Now In Case of Many to Many Relation I would like to take another Example here
Suppose I have a Model Class Enrollment.cs
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public decimal? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
Here CourseID and StudentId are the two foreign Keys
Now I Have another Class Course.cs where we will create Many to Many Relation.
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
Hope This will help!!!