I have following entities:
public class Product {
[Key]
public int Id{get;set;}
//other properties
}
public Coupon {
[Key]
public int Id {get;set;}
//other properties
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<CouponCode> CouponCodes { get; set; }
}
I am configuring DbModelBuilder as follows:
var builder = new DbModelBuilder();
builder.Entity<Product>().HasKey(p => p.Id);
builder.Entity<Coupon>().HasKey(a => a.Id);
//other properties
builder.Entity<Coupon>().HasMany(x => x.CouponCodes);
builder.Entity<Coupon>().HasMany(x => x.Products);
This scheme is creating Coupon_Id in Products table. Actually I want to register all the product codes for which the given Coupun is valid. The way EF is interpreting it is obviously wrong as there an be multiple coupons valid for one Product entity.
Kindly help me find what I'm doing wrong.
If you want to have many-to-many relation you must instruct EF to create it.
builder.Entity<Coupon>().HasMany(x => x.Products).WithMany();
Related
I'm trying to develop a project that includes "online friends" feature using EF Code First. I have to store all the users' friends in the database. I have "User" POCO class for the users and "FriendRelationship" POCO class for holding the friends of a user. These POCO classes are:
[Table("UserTable")]
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
//other properties
public int? FriendListID {get; set;}
public virtual FriendRelationship FriendList { get; set; }
}
[Table("FriendRelationshipTable")]
public class FriendRelationship
{
public FriendRelationship()
{
Friends = new List<User>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public virtual List<User> Friends { get; set; }
}
And my Fluent API is:
modelBuilder.Entity<User>()
.HasOptional(o => o.FriendList)
.WithMany(m => m.Friends)
.HasForeignKey(fk => fk.FriendListID)
.WillCascadeOnDelete(false);
I'm creating the Friend object in the User object at User object's first creation time(this User object added to database at controller.):
public class CreateAccountModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
HttpRequestBase request = controllerContext.HttpContext.Request;
//other actions
User _user = new User();
_user.FriendList = new FriendRelationship();
//other actions and return
}
}
And lastly, my friend addition attempt:
static void AddFriendByManually()
{
using (var context = _liveCampusContext)
{
User Admin2 = context.Users.SingleOrDefault(w => w.Username == "Admin2");
User Admin = context.Users.SingleOrDefault(w => w.Username == "Admin");
User AdminVader= context.Users.SingleOrDefault(w => w.Username == "AdminVader");
Admin.FriendList.Friends.Add(Admin2);
Admin.FriendList.Friends.Add(AdminVader);
Admin2.FriendList.Friends.Add(Admin);
Admin2.FriendList.Friends.Add(AdminVader);
AdminVader.FriendList.Friends.Add(Admin2);
AdminVader.FriendList.Friends.Add(Admin);
context.SaveChanges();
}
}
When context.SaveChanges() line executes, an exception is thrown:
Multiplicity constraint violated. The role 'User_FriendList_Target' of the relationship 'TeachLearnWeb.Data.DbContextFolder.User_FriendList' has multiplicity 1 or 0..1.
What am I missing? Thanks.
Judging by the usage example you have shown, this relationship should actually be many to many:
Any User can have many friends.
The same friend can show up for many users.
Also, the friend relationship is bidirectional (I strongly suspect).
So after Admin.FriendList.Friends.Add(AdminVader); the FriendList of AdminVader will already contain Admin and we can skip the line AdminVader.FriendList.Friends.Add(Admin);.
You only need the FriendRelationship class if you want to store additional data on the relationship itself (e.g. FriendSince timestamp).
Else change your model to
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
//other properties
public virtual ICollection<User> Friends { get; set; }
}
And change the config of the relationship as follows:
ModelBuilder.Entity<User>()
.HasMany(u => u.Friends)
.WithMany()
.Map(m => m.ToTable("FriendRelationshipTable"));
As per the example given in your code:
Admin.FriendList.Friends.Add(Admin2);
Admin.FriendList.Friends.Add(AdminVader);
*In the above code you are adding Admin2 and AdminVader.
Admin2.FriendList.Friends.Add(Admin);
Admin2.FriendList.Friends.Add(AdminVader);
* In the above 2 lines you are adding Admin and AdminVader. So AdminVader is being added twice which leads to Multiplicity constraint.
You might need to make changes to your table.
IN the UserTable, you can store all users.
[Table("UserTable")]
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name {get; set;}
}
In the FriendRelationshipTable, store the userID (user who will have set of friends) and FriendsUserId(this will store the id of the friend the user has)
[Table("FriendRelationshipTable")]
public class FriendRelationship
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public ID
public int FriendUserID { get; set; }
public int UserID {get; set;}
}
I have not added other properties.
Check if this table configuration will work.
I'm new to ASP.NET and currently working on a simple app to show a list of people and their hobbies. I have the following classes:
public class Hobby
{
public int HobbyID {get;set;}
public string Name {get;set;}
public string Type {get;set;}
public ICollection<PersonHobby> PersonHobbies {get;set;}
}
public class Person
{
public int PersonID {get;set;}
public int Age {get;set;}
public string Name {get;set;}
public ICollection<PersonHobby> PersonHobbies {get;set;}
}
public class PersonHobby
{
public int PersonHobbyID {get;set;}
public int PersonID {get;set;}
public int HobbyID {get;set;}
}
When viewing a person's Details page, I also need to display their hobbies. I did some research and found that ViewModels are a good way to accomplish this. So I created one, but I'm not sure if I did it correctly:
public class PersonHobbiesViewModel
{
public Person Person {get;set;}
public IEnumerable<PersonHobby> PersonHobbies {get;set;}
public IEnumerable<Hobby> Hobbies {get;set;}
}
And at this point I know that I need to create a viewmodel object in my controller's Details method and populate it with data, but I don't know how to navigate through the different tables. I have this:
public ActionResult Details(int? id)
{
var viewModel = new PersonHobbiesViewModel();
viewModel.Person = db.Person.find(id);
viewModel.Hobbies = ???
return View(viewModel);
}
On the other hand, if I'm going in the completely wrong direction, let me know! Thanks in advance.
Firstly what you might like to do, is change your entity models ever so slightly and let EF6 deal with the many to many complexity for you.
Your new model might look like this:
public class Hobby
{
public int HobbyID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public Person Person { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public int Age { get; set; }
public string Name { get; set; }
public ICollection<Hobby> Hobbies { get; set; }
}
Your context might be like:
public class MyContext : DbContext
{
public DbSet<Hobby> Hobbies { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Hobby>()
.HasRequired<Person>(s => s.Person)
.WithMany(s => s.Hobbies)
.HasForeignKey(s => s.PersonId);
}
}
Then when you are creating your model you can simply query it like:
var person = db.People.Include(c => c.Hobbies).SingleOrDefault(x => x.PersonID == id);
var viewModel = new PersonHobbiesViewModel();
viewModel.Person = person;
viewModel.Hobbies = person.Hobbies;
return View(viewModel);
Have a read through the following tutorials about complex entity models and reading from them, they cover the same content.
Ps. I have not tested any of this.
#shenku's answer has correct way to map entities. But it is unnecessary if you don't consider about naming conventions.
Additionally Entity Framework has much of c#'s object oriented programming basics. That means if you pass the person object from your controller to view, you could access to your entities on view like below.
//Controller
var person = db.People.Include(c => c.Hobbies).SingleOrDefault(x => x.PersonID == id);
return View(person);
#*View Page*#
#using Project.Models
#* prints Person Name and Age *#
#Model.Name #Model.age
#* prints Hobby Names of Person
#foreach (var item in Model)
{
#item.name
}
You've answered your own problem correct as "circular reference" on your comment.
So you don't need PersonHobby class, Entity framework will automatically creates this table for you. This magic happens because you've defined hobby and person as a collection on their own classes.
Also you don't need a viewModel for your situation. Just pass the person or hobby object. Do not pass the Icollection because it's already loaded when you wrote db.People.Include(c => c.Hobbies). Also you can define classes as 'virtual' so it will load entities without include method.(Lazy-loading)
I have two models, One ApplicationUser which holds all users in the system and I have a Quotation model which will hold all Quotations made. now I want to store two mappings to ApplicationUser inside Quotations. So that I can map to created User as well as cancelled User. My model looks like this
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int ApplicationUserID { get; set; }
public virtual ApplicationUser CreatedUser { get; set; }
[ForeignKey("ApplicationUserID")]
public ApplicationUser CancelledUser { get; set; }
}
But this throws an error
Quotation_CancelledUser_Target_Quotation_CancelledUser_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'ApplicationUserID' on entity 'Quotation' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'Quotation_CancelledUser'.
So I guess , The approach I am taking is wrong. Can anyone point out the correct way to achieve this?
The problem you are observing is called "Multiple Cascade Path". A Multiple Cascade Path happens when a cascade path goes from column col1 in table A to table B and also from column col2 in table A to table B.
The exception is caused by SQL Server when code first attempted to add table that has columns appearing more than once of another table.
In SQL Server, a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement. For example, the tree of cascading referential actions must only have one path to a particular table on the cascading referential actions tree.
You will need to use FluentAPI to configure the relationship. I am using EF5 currently and do not know if this can be accomplished in EF6/7.
So modifying your code sample, it would look like:
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int CreatedUserID { get; set; }
// Navigation property
public virtual ApplicationUser CreatedUser { get; set; }
public int CancelledUserID { get; set; }
// Navigation property
public virtual ApplicationUser CancelledUser { get; set; }
}
// Created a simple class for example
public class ApplicationUser
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
Now in you context class you can write:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Disable the default PluralizingTableNameConvention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// Add configuration here
modelBuilder.Entity<Quotation>()
.HasKey(e => e.QuotationID);
modelBuilder.Entity<ApplicationUser>()
.HasKey(e => e.Id);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CreatedUser)
.WithMany()
.HasForeignKey(u => u.CreatedUserID);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CancelledUser)
.WithMany()
.HasForeignKey(u => u.CancelledUserID);
}
For more information with example refer this link.
I have a legacy table I need to connect my app to. I am using a code-first, POCO model. I have the following classes:
public class Equipment
{
[Key]
public string EquipmentId { get; set; }
public string OriginatorId { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
[Key]
[Column("employee_id")]
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
[ForeignKey("OriginatorEmployeeId")]
public virtual Equipment Equipment { get; set; }
}
I need to map EmployeeId in the Employee class to to OriginatorEmployeeId in the Equipment class.
Also, the legacy table is represented by the Employee class. The table is actually named employee (lower case) and the EmployeeId column is named "employee_id". I want to keep naming of my classes and properties consistent with the rest of the app, hence Employee and EmployeeId.
Here is what I have tried using fluent API:
modelBuilder.Entity<Employee>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("employee");
});
modelBuilder.Entity<Equipment>()
.HasOptional<Employee>(u => u.Employee)
.WithOptionalDependent(c => c.Equipment).Map(p => p.MapKey("OriginatorEmployeeId"));
I am probably mixing things I don't need to. The error I am getting right now is:
Multiplicity is not valid in Role 'Equipment_Employee_Source' in relationship 'Equipment_Employee'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
Any help is appreciated.
Can an employee record be associated to more than one equipment record? If they can then your Employee POCO should contain a collection property representing a one-to-many relationship between Employee and Equipment.
public virtual ICollection<Equipment> Equipments {get;set;}
You configuration should then be adjusted accordingly to show this relationship:
modelBuilder.Entity<Employee>()
.HasMany<Equipment>(u => u.Equipments)
.WithRequired(c => c.Employee).HasForeignKey(p => p.OriginatorId);
It also looks like you will need to setup a configuration for your column name mappings as well. Therefore, I would recommend that you create a separate configuration file for each of your POCOs to make it easier to manage the configurations, then just add those configurations to the modelbuilder.Configurations collection in your OnModelCreating event of your DBContext
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelbuilder.Configurations.Add(new EmployeeConfiguration());
}
I have a class with a relationship to another table.
public class MyClass
{
[Key]
public Guid Id {get; set; }
public virtual OtherClass OtherClass { get; set; }
}
I hook this up to a controller and create views for CRUD - all works fine.
In the DB a OtherClass_OtherClassId column is created, but this is not in the model.
How can I put a reference in this Id column during the controller's Create method?
How can I force this relationship to be [Required] without having to create a brand new OtherClass each time?
Annotated class with some description:
public class MyClass
{
// [Key] - Don't actually need this attribute
// EF Code First has a number of conventions.
// Columns called "Id" are assumed to be the Key.
public Guid Id {get; set; }
// This reference creates an 'Independent Association'. The Database
// foreign key is created by convention and hidden away in the code.
[Required]
public virtual OtherClass OtherClass { get; set; }
// This setup explicitly declares the foreign key property.
// Again, by convention, EF assumes that "FooId" will be the key for
// a reference to object "Foo"
// This will still be required and a cascade-on-delete property
// like above - an int? would make the association optional.
public int OtherClass2Id { get; set; }
// Leave the navigation property as this - no [Required]
public virtual OtherClass2 { get; set; }
}
So which is better? Independent associations or declaring the foriegn key?
Independent associations match object programming closer. With OOP, one object doesn't really care much about the Id of a member. ORM's try to cover these relationships up, with varying degrees of success.
Declaring the foreign key puts database concerns into your model, but there are scenarios where this makes dealing with EF much easier.
Example - when updating an object with a required independent association, EF will want to have the entire object graph in place.
public class MyClass
{
public int Id {get; set; }
public string Name { get; set; }
[Required] // Note the required. An optional won't have issues below.
public virtual OtherClass OtherClass { get; set; }
}
var c = db.MyClasses.Find(1);
c.Name = "Bruce Wayne";
// Validation error on c.OtherClass.
// EF expects required associations to be loaded.
db.SaveChanges();
If all you want to do is update the name, you'll either have to pull OtherClass from the database as well since it's required for entity validation or attach a stubbed entity (assuming you know the id). If you explicitly declare foreign key, then you won't run into this scenario.
Now with foreign keys, you run into a different issue:
public class MyClass
{
public Guid Id {get; set; }
public string Name { get; set; }
public int OtherClassId { get; set }
public virtual OtherClass OtherClass { get; set; }
}
var c = db.MyClasses.Find(1);
// Stepping through dubugger, here, c.OtherClassId = old id
c.OtherClass = somethingElse;
// c.OtherClassId = old id - Object and id not synced!
db.SaveChanges();
// c.OtherClassId = new id, association persists correctly though.
In summary -
Independent associations
Good: Match OOP and POCO's better
Bad: Often requires a full object graph, even if you're only updating one or two properties. More EF headaches.
Foreign Keys
Good: Easier to work with sometimes - less EF headaches.
Bad: Can be out of sync with their object
Bad: Database concerns in your POCO's
EF generally require handholding with the model configuration. This should get you started. However doing a good tutorial on EF Code First and DB first would be greatly beneficial.
Following has:
Order with multiple OrderItems
single User
and single OrderType made by keeping the identity OrderTypeId and the actual OrderType ref object.
public class Order
{
public Order()
{
OrderItems = new OrderItemCollection();
}
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public string OrderName { get; set; }
public int UserId { get; set; }
public virtual User OrderUser { get; set; }
public virtual OrderItemCollection OrderItems { get; set; }
public int? OrderTypeId { get; set; }
public OrderType OrderType { get; set; }
public override int GetHashCode() { return OrderID.GetHashCode();}
}
public class OrderConfiguration : EntityTypeConfiguration
{
public OrderConfiguration()
{
this.ToTable("ORDERS");
this.HasKey(p => p.OrderID);
this.Property(x => x.OrderID).HasColumnName("ORDER_ID");
this.Property(x => x.OrderName).HasMaxLength(200);
this.HasMany(x => x.OrderItems).WithOptional().HasForeignKey(x => x.OrderID).WillCascadeOnDelete(true);
this.HasRequired(u => u.OrderUser).WithMany().HasForeignKey(u => u.UserId);
this.Property(x => x.OrderTypeId).HasColumnName("ORDER_TYPE_ID");
this.HasOptional(u => u.OrderType).WithMany().HasForeignKey(u => u.OrderTypeId);
}
}
public class OrderContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new OrderConfiguration());
}
}
'