I'm basically trying to enforce this in Entity Framework: Require Only One Of Multiple Columns Be Not Null
My database has several 1:m relationships where the child entity belongs to one of several parent entities. For example, let's say I have tables for Teachers, Students, and Guardians. Each of those can have many PhoneNumbers and EmailAddresses. I am using EF Code First, and my models look something like:
public class Teacher {
public int Id { get; set; }
public string Name { get; set; }
public List<PhoneNumber> PhoneNumbers { get; set; }
public List<EmailAddress> EmailAddresses { get; set; }
}
public class Student {
public int Id { get; set; }
public string Name { get; set; }
public List<PhoneNumber> PhoneNumbers { get; set; }
public List<EmailAddress> EmailAddresses { get; set; }
}
public class Guardian {
public int Id { get; set; }
public string Name { get; set; }
public List<PhoneNumber> PhoneNumbers { get; set; }
public List<EmailAddress> EmailAddresses { get; set; }
}
public class PhoneNumber {
public int Id { get; set; }
public string Number { get; set; }
}
public class EmailAddress {
public int Id { get; set; }
public string Email { get; set; }
}
When I run the migration, this creates the database with the tables/columns I would expect. The PhoneNumbers and EmailAddresses tables each have columns Teacher_Id, Student_Id, and Guardian_Id, which are foreign keys to their respective parent entity. However, there are no constraints on how many parent entities can be set on the child. For example, I can create a PhoneNumber that has all three parent IDs set to null, or I can set both a Teacher_Id and a Guardian_Id.
I tried adding a required attribute to the parents like so:
public class Teacher { // Also Student/Guardian
public int Id { get; set; }
public string Name { get; set; }
[Required]
public List<PhoneNumber> PhoneNumbers { get; set; }
[Required]
public List<EmailAddress> EmailAddresses { get; set; }
}
That does not seem to have any effect.
I think there is no way to do this on entities. Instead, create a migration and try to alter the table in migration class like this:
public partial class YourMigrationName: Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("ALTER TABLE [dbo].[PhoneNumber]
WITH CHECK ADD CONSTRAINT [CK_PhoneNumer_Teacher_Student_Guardian] CHECK (Teacher_Id
is not null or Student_Id is not null or Guardian_Id is not null)
GO
ALTER TABLE [dbo].[EmailAddress] WITH CHECK ADD CONSTRAINT
[CK_EmailAddress_Teacher_Student_Guardian] CHECK (Teacher_Id is not null or
Student_Id is not null or Guardian_Id is not null)
");
}
}
Try this:
public class PhoneNumber
{
public int Id { get; set; }
public string Number { get; set; }
//Added this code:
[Required]
public Teacher Teacher { get; set;}
}
public class EmailAddress {
public int Id { get; set; }
public string Email { get; set; }
//Added this code:
[Required]
public Teacher Teacher { get; set;}
}
That would make sure you cannot create PhoneNumber without Teacher. You can also do this:
public class PhoneNumber
{
public int Id { get; set; }
public string Number { get; set; }
public int TeacherId
public Teacher Teacher { get; set;}
}
That would also detect that you want to add constraint on your PhoneNumber. Both ways work fine.
Related
I am trying add the cart to database but don't know how to add related Entities
My related tables are ( carts , products , productoptions , options)
How Can I add or Update those tables at the same time? And how can I set Foreign keys to related Tables
thanks...
You will create models for each of your tables (see below)
Then if you add just a child you will have to add it with the foreig nkey property populated
var test = childObj { parentPropertyId = parentPropertyIdValue}
If you add parent and child together you can just add and entity mework will take care of that. For example:
var test = new parentObj {
someProperty = someValue,
childProperty = new childObj{
//here will not have to populate the parentPropertyId
}
}
See sample models for your above tables
Use the [Key] attribute to specify your primary key
Use the [ForeignKey] attribute above your related entity property to specify which property to use as foreign key
Use an ICollection to access the children of an object
public class Carts
{
[Key]
public int Id { get; set; }
public int Userid { get; set; }
public DateTime CreatedDate { get; set; }
public ICollection<Products> Products { get; set; }
}
public class Products
{
[Key]
public int Id { get; set; }
public string SerialNumber { get; set; }
public string StockCode { get; set; }
public int CartId { get; set; }
public int Quantity { get; set; }
[ForeignKey(nameof(CartId))]
public Carts Cart { get; set; }
}
public class ProductOptions
{
[Key]
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
[ForeignKey(nameof(ProductId))]
public Products Products { get; set; }
[ForeignKey(nameof(OptionId))]
public Options Options { get; set; }
}
public class Options
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public int ParentId { get; set; }
public int GrandParentId { get; set; }
}
I have two model classes Category and Recipe and their relationship in one to many. I want to Edit the Recipe and also change the category that the recipe belongs to.Thanks in advance.
public class CookContext : DbContext
{
public CookContext(): base("cookContext")
{
}
public DbSet<Recipe> Recipes { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Recipe> Recipes { get; set; }
}
public class Recipe
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Ingridients { get; set; }
public string Image { get; set; }
public Category category { get; set; }
}
[HttpPost]
[ValidateInput(false)]
public ActionResult EditRecipe(Recipe recipe, int? categoryName)
{
var category = context.Categories.Where(c => c.Id ==
(int)categoryName).FirstOrDefault();
context.Entry(recipe).State = EntityState.Modified;
recipe.category = category;
context.SaveChanges();
}
The error messages i get are:
1.
[DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types.
2.
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
Try by adding the field CategoryId to your Recipe class, also, the category property should begin with a capital "C".
The collection property should be marked virtual if you want the data to be lazy loaded (only loaded when needed) otherwise, you may load all recipes for a given category every time you make a query:
public class Recipe
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Ingridients { get; set; }
public string Image { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
If it doesn't work try by setting up the ForeignKey attribute:
public class Recipe
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Ingredients { get; set; }
public string Image { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
I have 3 tables
User > Employee > Operator
There is a one to one relation on each other's end
public class User
{
[Key]
public int Id { get; set; }
public string Username { get; set; }
public DateTime? LoginDate { get; set; }
public bool IsActive{ get; set; }
public ICollection<Role> Roles { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
[Key]
[ForeignKey("User")]
public int Id { get; set; }
public string Name{ get; set; }
public string Email { get; set; }
public int UserId { get;set; }
public virtual User User { get; set; }
public virtual Operator Operator{ get; set; }
}
public class Operator
{
[Key]
[ForeignKey("Employee")]
public int Id { get; set; }
public int EmployeeId {get;set;}
public EmployeeEmployee{ get; set; }
}
However this does create the one to one relation when i create a diagram in Sql Server MS to check the relations.
Problem is, when I'm just trying to insert data directly graphically from Sql it expects me to insert the primary key on Employee and Operator tables. Why aren't they automatic like the User one?
First, you don't need specify two variables to store same informations.
public int UserId { get;set; }
public int EmployeeId {get;set;}
already stored in Id. Not need to duplicate it.
Foreign keys doesn't use identity (doesn't generate values). So you need create User and then create an Employe where set the "User" property with user created before. (Main idea is that you need initialise reference to foreign key manually)
User user = new User{...};
Employee employe = new Employee{User = user, ...};
context.Add(employee);
context.SaveChanges();
Or maybe you will use hierarchy. (I don`t checked on 3 level, but on 2 this works perfectly).
public class User
{
[Key]
public int Id { get; set; }
public string Username { get; set; }
public DateTime? LoginDate { get; set; }
public bool IsActive{ get; set; }
public ICollection<Role> Roles { get; set; }
}
public class Employee : User
{
public string Name{ get; set; }
public string Email { get; set; }
}
public class Operator : Employee
{
}
I am developing a C# MVC application. I am using Code First approach to model my database.
My project had the following requirements:
Company Can Have Many Products
Product Can have many Advertisement
Types
Here are model classes (code first solution) to the above mentioned problem.
public class Company
{
public Company()
{
this.Employees = new HashSet<ApplicationUser>();
}
public int ID { get; set; }
[Required]
public string Name { get; set; }
public string Logo { get; set; }
[Display(Name="Company Description")]
public string CompanyDescription { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public virtual ICollection<ApplicationUser> Employees { get; set; }
public virtual ICollection<Client> Clients { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string ProductName { get; set; }
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<AdvertisementType> AdvertisementTypes { get; set; }
}
public class AdvertisementType
{
public int AdvertisementTypeID { get; set; }
public int ProductID { get; set; }
[Display(Name = "Advertisement Name")]
public string AdvertisementTypeName { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public virtual Product Product { get; set; }
}
When I try to update the database, after creating the migrations i get the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.AdvertisementTypes_dbo.Products_ProductID' on table 'AdvertisementTypes' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
I have been trying solve this problem but unable to find any solution. I don't find any problem with the model classes, nor i think there are any issues with the relationship between the models.
Any suggestions or help will be useful.
EDIT
Here is screenshot of Tables and their relations
in your dbContext you need to turn cascade delete to false if you want to avoid that.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
You can initialize your make a List() in Product like you did with Company class...
public class Product
{
public int ProductID { get; set; }
public Product()
{
this.AdvertisementTypes = new List<AdvertisementType>();
}
I want to set the relationship of some tables and make some columns in the table can be null.
[Table("tbl_useraccount")]
public class AccountViewModels
{
[Key]
public string AccountId { get; set; }
public string Email { get; set; }
public string HashPassword { get; set; }
}
and here is the table which I want to make relationship:
[Table("tbl_userprofile")]
public class UserProfileViewModels
{
[Key]
public int Id { get; set; }
public string AccountId { get; set; }
public string Address { get; set; }
public int PhoneNumber { get; set; }
}
My question is: How to set AccountId (in the table tbl_useraccount) is the primary key and as the foreign key in the table tbl_userprofile from Model?
And my sub-question is: Is it necessary to set NULL or NOT NULL for per column name? If it is necessary, how can I do that?
p/s: I'm using MVC 5 and SQL Server.
First remark I have is that you don't use viewmodels for your database generation. ViewModels are only used for your views.
To create a relationship, you should add the AccountModel to the UserProfile, I added virtual to enable lazy loading. Also add the ForeignKey data annotation to the extra property you want to map your key to (optional)
[Table("tbl_useraccount")]
public class AccountModel
{
[Key]
public string AccountId { get; set; }
public string Email { get; set; }
public string HashPassword { get; set; }
}
[Table("tbl_userprofile")]
public class UserProfileModels
{
[Key]
public int Id { get; set; }
public string AccountId { get; set; }
public string Address { get; set; }
[ForeignKey("AccountModel")]
public int PhoneNumber { get; set; }
public virtual AccountModel AccountModel { get; set;}
}
If you want a field not to be null, if this is even possible. Use the [Required] data annotation. "Public int PhoneNumber" can not be null, you'll have to write it as following: "Public int? PhoneNumber".