I am building a reservation system. I have users in roles('admin', 'client', 'employee', 'student').
Each reservation must be associated with a user of role client, it might be assigned to user of role employee and might also be assigned to user of role student.
So in my reservation class I have properties of type User and I have marked them with [ForeignKey("AnytypeId")] attribute to hint EF for relations.
I have seen code like this at http://blog.stevensanderson.com/2011/01/28/mvcscaffolding-one-to-many-relationships/
public class Reservation
{
public int ReservationID
{
get;
set;
}
[Required(ErrorMessage="Please provide a valid date")]
public DateTime ReservationDate
{
get;
set;
}
public DateTime ReservationEnd { get; set; }
public DateTime EntryDate
{
get;
set;
}
public DateTime UpdatedOn
{
get;
set;
}
public decimal Ammount
{
get;
set;
}
public decimal? Discount { get; set; }
[DataType(DataType.MultilineText)]
public string ServiceDetails { get; set; }
[DataType(DataType.MultilineText)]
public string Remarks { get; set; }
public String PaymentMethod { get; set; }
public string VoucherNumber { get; set; }
public int ServiceID
{
get;
set;
}
public virtual Service Service
{
get;
set;
}
public string EmployeeID { get; set; }
[ForeignKey("EmployeeID")]
public virtual User Employee { get; set; }
public string ClientID { get; set; }
[ForeignKey("ClientID")]
public virtual User Client { get; set; }
public string StudentID { get; set; }
[ForeignKey("StudentID")]
public virtual User Student { get; set; }
}
public class ReservationMap : EntityTypeConfiguration<Reservation>
{
public ReservationMap()
{
this.HasOptional(r => r.Client).WithMany().WillCascadeOnDelete(true);
this.HasOptional(r => r.Employee).WithMany().WillCascadeOnDelete(false);
this.HasOptional(r=>r.Student).WithMany().WillCascadeOnDelete(false);
}
}
Now as I run my mvc3 EF code first app database created for me on the fly with following ERD and edmx model.
Now few problems that I am having:
1. When I am listing all the users of role clients in view their reservation property is showing always 0 even if their are reservations available in database. I don't know why this collection property marked with virtual is not loading??
Please I am stuck with this help me out here this is the last thing remaining.
There are couple of problems in your model. You have configured the one to many relationship in such a way that the many end(Reservations property) is excluded from the mapping. Hence the Reservations will not be loaded by EF.
The other problem is if you are going to map the Reservations property as the many end of the relationship, what will be the navigational property? is it Employee, Client, Student? Because only one of these properties can participate in the relationship with Reservations property.
It is not clear as to how these relationships should be modeled by your description. One way would be to have 3 collection properties.
public class ReservationMap : EntityTypeConfiguration<Reservation>
{
public ReservationMap()
{
HasOptional(r => r.Client).WithMany(u => u.ClientReservations).WillCascadeOnDelete(true);
HasOptional(r => r.Employee).WithMany(u => u.EmployeeReservations).WillCascadeOnDelete(false);
HasOptional(r=>r.Student).WithMany(u => u.StudentReservations).WillCascadeOnDelete(false);
}
}
Related
I'm trying to create 1-to-1 relationship between two classes. 1 user has 1 profile picture and 1 profile picture belongs to one user.
the code is as follows.
public class UserImage
{
[Key, ForeignKey("User")]
public int ImageId { get; set; }
public byte [] ImageContentBytes { get; set; }
public string FileName { get; set; }
public string UserId { get; set; }
public string ImagePath { get; set; }
[InverseProperty("UserImage")]
public virtual ApplicationUser User { get; set; }
}
public class ApplicationUser : IdentityUser
{
public string FullName { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public string RoleId { get; set; }
public IdentityRole Role { get; set; }
public int CityId { get; set; }
public ICollection<User_Has_Jobs_Posted> UserJobs { get; set; }
public City City { get; set; } // Adding relationship to the user.
public IList<JobPost> jobPosts { get; set; }
[InverseProperty("User")]
public virtual UserImage UserImage { get; set; }
}
The error is saying:
Unable to determine the principal end of an association between the types 'FinalWorkFinder.Models.UserImage' and 'FinalWorkFinder.Models.ApplicationUser'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
In a one-to-one relationship one entry must depend on another, rather then both entries depending on each other.
So in your case an ApplicationUser entry would be valid on its own but a UserImage cannot.
You can fix this by using the Required attribute on the FK like so:
[Required]
public virtual ApplicationUser User { get; set; }
Or you could use fluent api, and do something along the lines of:
modelBuilder.Entity<ApplicationUser>()
.HasOptional(f => f.UserImage)
.WithRequired(s => s.User);
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).
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 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 }
I am working with the EF Code First library trying to work on an appointment scheduling app.
The model's I have are going to be a Client, Appointment, and AppointmentType...
Basically each Client can have a set of Appointments, and each Appointment can have a single AppointmentType...
The code is as follows:
public class Client
{
[ScaffoldColumn(false)]
public int ClientID { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[EmailAddress]
[Required]
public string Email { get; set; }
[DataType("DateTime")]
public DateTime Birthday { get; set; }
[Required]
public string CellPhone { get; set; }
public string HomePhone { get; set; }
public string Notes { get; set; }
public virtual ICollection<Appointment> Appointments{ get; set; }
public string Name {
get{
return FirstName + " " + LastName;
}
}
public class Appointment
{
[ScaffoldColumn(false)]
public int AppointmentID { get; set; }
[ScaffoldColumn(false)]
public int ClientID { get; set; }
[ScaffoldColumn(false)]
public int AppointmentTypeID { get; set; }
[Required]
public DateTime AppointmentDate { get; set; }
public string Notes { get; set; }
public virtual AppointmentType AppointmentType { get; set; }
public virtual Client Client { get; set; }
}
public class AppointmentType
{
[ScaffoldColumn(false)]
public int AppointmentTypeID { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public virtual Appointment Appointment { get; set; }
}
Everything works well when I create an appointment type, and a client, but when I create an appointment I get the following error...
InnerException {"The INSERT statement conflicted with the FOREIGN KEY constraint \"Appointment_Client\". The conflict occurred in database \"Salon.Models.SalonContext\", table \"dbo.Clients\", column 'ClientID'.\r\nThe statement has been terminated."} System.Exception {System.Data.SqlClient.SqlException}
If more details are needed, please let me know...I am just trying to figure out if I am missing anything in the setup.
This is what happens when I debug on the post to create the Appointment...All the ID's are as 0 which is correct, but should the other fields not be null?? Or does it matter...Just not very familiar with how things should look this being my first EF Code First project...
According to your setup, one AppointmentType can only have one Appointment. This is a one-to-one mapping. In this case, you better move the AppointmentType into the Appointment entity. Otherwise, what I believe is more logical, an AppoitmentType can have many Appointments but one Appointment can have only one AppointmentType. Accordingly, you should have a virtual ICollection inside your AppointmentType entity.
public class AppointmentType
{
[ScaffoldColumn(false)]
public int AppointmentTypeID { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Appointment> Appointments { get; set; }
}
I am not sure this is what's causing the problem but it could be. Sometimes mapping faults cause some weird exceptions to be thrown. Give it a try and let me know if your problem gets resolved.
By your constraints AppointmentType and Client cannot be null in Appointment. You can delete constraints or set correct objects in object properties. For example create Client and AppointmentType and then create Appointment for created Client with created AppointmentType