We have a requirement that several tables need to be referred in a one table as shown in the diagram.
Please note that the diagram is NOT the correct DB model but just represent what our requirement. Can you suggest a proper way to implement above in SQL Server DB and Entity Framework 6?
Example: A sales order (in SalesOrder table) can have multiple files and those uploaded file details will be stored in UploadedFile table. Likewise OrderTable and Invoice too.
So we need to have a proper DB model with FK relationships between FileUpload table with each other related table.
Note: All tables PKs are auto-increment int values and we may need add more entities (tables) in future
In order to provide you an alternative, as you said you are using Entity Framework, here is a sample of Code First implementation done on C#. You can create and update the schema via Package Manager Console migrations, within Visual Studio. I have used the Fluent API in order to define the relationships, as this is recommended over the alternative.
public class SampleContext : DbContext
{
public SampleContext()
: base("name=YourConnection")
{
}
public DbSet<SalesOrder> SalesOrders { get; set; }
public DbSet<CreditOrder> CreditOrders { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<UploadedFile> UploadedFiles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<SalesOrder>()
.HasKey(so => so.Id);
modelBuilder.Entity<CreditOrder>()
.HasKey(co => co.Id);
modelBuilder.Entity<Invoice>()
.HasKey(i => i.Id);
modelBuilder.Entity<UploadedFile>()
.HasKey(u => u.Id);
modelBuilder.Entity<UploadedFile>()
.HasRequired(u => u.SalesOrder)
.WithMany(s => s.UploadedFiles)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UploadedFile>()
.HasRequired(u => u.CreditOrder)
.WithMany(c => c.UploadedFiles)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UploadedFile>()
.HasRequired(u => u.Invoice)
.WithMany(c => c.UploadedFiles)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UploadedFile>()
.Property(uf => uf.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<SalesOrder>()
.Property(so => so.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<CreditOrder>()
.Property(co => co.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Invoice>()
.Property(i => i.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
base.OnModelCreating(modelBuilder);
}
}
// Collections of navigation properties should be included in classes for a one-to-many relationship
public class SalesOrder
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class CreditOrder
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class UploadedFile
{
public int Id { get; set; }
public SalesOrder SalesOrder { get; set; }
public CreditOrder CreditOrder { get; set; }
public Invoice Invoice { get; set; }
public string FilePath { get; set; }
public string FileType { get; set; }
}
public class SalesOrder
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class CreditOrder
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public string MyColumn { get; set; }
public IList<UploadedFile> UploadedFiles { get; set; }
}
public class UploadedFile
{
public int Id { get; set; }
public SalesOrder SalesOrder { get; set; }
public CreditOrder CreditOrder { get; set; }
public Invoice Invoice { get; set; }
public string FilePath { get; set; }
public string FileType { get; set; }
}
Before Reading My Answer take notice in the below information:
A better answer can be found at this previous post at Foreign Key to multiple tables.
My Answer:
Refer here for more information from the Creating Tables documentation off of the MDSN page.
The design I went for gave each table there own id as the Primary Key. Then I used the UploadedFile table to add references to each of these tables in the shape of Foreign Keys.
I have created a few dummy scripts that might help you create these tables. Please let me know if this helps. Thank you!
SQL Scripts:
SalesOrder Table:
Create Table dbo.SalesOrder(
SalesOrderID int not null, identity primary key,
--enter whatever other columsn you have here
)
CreditOrder Table:
Create Table sbo.CreditOrder(
CreditOrderID int not null, identity primary key,
--enter whatever other columsn you have here
)
Invoice Table:
Create Table dbo.Invoice(
InvoiceID int not null, identity primary key,
--enter whatever other column you have here
)
UploadedFile Table:
Create table dbo.UploadedFile(
UploadFileID int not null identity primary key,
SalesOrderID int null Foreign Key References SalesOrder(SalesOrderID),
CreditOrderID int null Foreign Key References CreditOrder(CreditOrderID),
InvoiceID int null Foreign Key References CreditOrder(InvoiceID),
--enter whatever other columns you have here
)
Related
i made a many to many relationship and the table was created with Classes and Users for a gym web app,but when i try to insert values to that table i get SqlException: Cannot insert explicit value for identity column in table 'classes' when IDENTITY_INSERT is set to OFF.
My models are
Class
public class Class
{
[Key]
public int ClassID { get; set; }
...
public List<UserClass> userClasses { get; set; }
}
User:
public class User
{
[Key]
public int UserID{ get; set; }
...
public List<UserClass> userClasses { get; set; }
}
Where they merge:
public class UserClass
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long UserClassID { get; set; }
public int UserID { get; set; }
public int ClassID { get; set; }
public User user { get; set; }
public Class #class { get; set; }
}
my context class modelbiulder function:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserClass>()
.HasKey(x => new { x.UserID, x.ClassID });
//If you name your foreign keys correctly, then you don't need this.
modelBuilder.Entity<UserClass>()
.HasOne(uc => uc.user)
.WithMany(p => p.userClasses)
.HasForeignKey(uc => uc.UserID);
modelBuilder.Entity<UserClass>()
.HasOne(uc => uc.#class)
.WithMany(t => t.userClasses)
.HasForeignKey(uc => uc.ClassID);
}
and this function that i use with ajax
public IActionResult BookTheClass(int ClassID)
{
User user = _context.users.Find(_auth.User.UserID);
Class selectedClass = _context.classes.Find(ClassID);
selectedClass.userClasses = new List<UserClass>() {
new UserClass{
user=user,
#class=selectedClass
}
};
_context.classes.Add(selectedClass);
_context.SaveChanges();
return PartialView("~/Views/PartialViews/ClassBooked.cshtml");
}
The other similar questions had a model builder problem that i dont have.
An identity column will keep track of the next free number, and you do not need to generate the ClassId by yourself. The new id can be fetched by SELECT SCOPE_IDENTITY()
If you really want to insert value to an identity column(not recommended) then you would have to switch on the Identity Insert property of your table. See the below SQL to do the same
SET IDENTITY_INSERT classes ON
I have the following entity:
public class ShoppingCartItem
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ShoppingCartItemId { get; set; }
public virtual Product Product { get; set; }
public int Quantity { get; set; }
public string ShoppingCartId { get; set; }
public DateTime ItemAddedToCart { get; set; }
}
There is a one to one foreign key relationship from Product to ShoppingCartItem.
My question is, how can I make entity framework core skip deleting the ShoppingCartItem entity on Product cascade delete?
You can use something like the following in your builder object
builder.Entity<ShoppingCartItem>()
.HasOne(s => s.Product)
.WithOne(p => p.ShoppingCartItem)
.Metadata.DeleteBehavior = DeleteBehavior.Restrict;
I have existing tables including the mapper table. I have to set the Entity Framework annotations. I am confused about how to achieve that. there are three tables,
Model (ModelId, ModelName),
Department (DepartmentId, DepartmentName)
ModelDepartmentMapper (ModelDepartmentMapperId, ModelId, DepartmentId, ModelStatus)
I have created the classes as:
public class Model
{
public int ModelId { get; private set; }
public string ModelName { get; private set; }
public virtual ICollection<Department> Departments { get; private set; }
}
public class Department
{
public int DepartmentId { get; private set; }
public string DepartmentName { get; private set; }
public virtual ICollection<Model> Models { get; private set; }
}
public class JoinModelDepartment
{
public int JoinModelDepartmentId { get; private set; }
public Guid ModelId { get; private set; }
public Guid DepartmentId { get; private set; }
public int ModelStatus { get; private set; }
}
And in DBContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Model>()
.HasMany<Department>(s => s.Departments)
.WithMany(c => c.Models)
.Map(cs =>
{
cs.MapLeftKey("ModelGuid");
cs.MapRightKey("DepartmentGuid");
cs.ToTable("JoinModelDepartment");
});
}
Please guide me how to add ModelStatus in the mapper table. Do I need to create it manually or there is a way to do this?
What I would tend to do is move these kinds of mappings into their own class files. Theres a brilliant tutorial found here:
https://www.entityframeworktutorial.net/code-first/move-configurations-to-seperate-class-in-code-first.aspx
This lets you map out each of the tables individually and it will build the database off of them.
So in your case you would have a Model, Department and a ModelDepartmentMapper class file. From there you can set all the relevant columns and how they are linked to another table as well as column types IE VARCHAR etc
I have a class:
public class FormTemplate
{
public Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Code { get; set; }
public virtual string Shifr { get; set; }
public virtual FormType FormType { get; set; }
public virtual DateTime ActivationDate { get; set; }
}
and i need to split it into to tables in db. I have a fluent mapping wich do this for me:
en.Map(m =>
{
m.Properties(_ => new {_.Code, _.Name, _.Shifr});
m.ToTable("Table1");
})
.Map(m =>
{
m.Properties(_ => new {_.ActivationDate});
m.ToTable("Table2");
}).HasRequired(t => t.FormType).WithMany().Map(m =>
{
m.MapKey("FormType");
m.ToTable("Table2");
});
It works fine, but creates two tables with same primary key column "Id". Is it possible to map first table PK to column "Id" and second table PK to column "Form" ?
I currently have an employee model
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<StateLicenseType> Licenses { get; set; }
and a License Type Model
public class StateLicenseType
{
public int StateLicenseTypeId { get; set; }
public string State { get; set; }
public string LicenseName { get; set; }
public virtual Employee Employee { get; set; }
}
This relationship can be one to many, but I also need to add some information to the license when saved. I need to be able to store the employees unique license number and have not been able to find out how to do this while searching around. Is there a way to have Entity Framework add a column to a join table and then even if I have to, update it myself?
Is there a better/different way to model this relationship with EF?
In an old DB the table was created like this,
CREATE TABLE `nmlsstatelicenses` ( `peopleid` int(11) DEFAULT NULL, `statelicensetypeid` int(11) DEFAULT NULL, `licensenumber` varchar(25) DEFAULT NULL)
You need to create a third entity which will be a linking entity (like a linking table in many-to-many relationships in database. Here is an example: many-to-many relationships with additional information.
So you would have the following entities in your model:
public Employee
{
public string EmployeeId { get;set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<LicenseRegistration> RegisteredLicenses { get; set; }
}
public LicenseType
{
public int StateLicenseTypeId { get; set; }
public string State { get; set; }
public string LicenseName { get; set; }
public virtual ICollection<LicenseRegistration> RegisteredLicenses { get; set; }
}
public LicenseRegistration
{
//properties for the additional information go here
/////////////////////////////////////////////////////
public int EmployeeId {get;set;}
[ForeignKey("EmployeeId")]
public Employee Employee {get;set;}
public int LicenseTypeId {get;set;}
[ForeignKey("LicenseTypeId")]
public LicenseType {get;set;}
}
Then, in your DBContext file, you will need to define 1-to-many relationship between Employee and LicenseRegistration, and between LicenseType and LicenseRegistration.
Hope this helps!
UPDATE
Here is how you would set up the relationships:
modelbuilder.Entity<LicenseRegistration>()
.HasRequired(lr => lr.LicenseType)
.WithMany(lt => lt.RegisteredLicenses)
.HasForeignKey(lr => lr.LicenseTypeId);
modelbuilder.Entity<LicenseRegistration>()
.HasRequired(lr => lr.Employee)
.WithMany(e => e.RegisteredLicenses)
.HasForeignKey(lr => lr.EmployeeId);