I have declared two classes - Person and Vehicle as shown below
public class Person
{
public Person()
{
this.Vehicles = new HashSet<Vehicle>();
}
[Key]
public int PersonID { get; set; }
[Required, MaxLength(50)]
public string FirstName { get; set; }
[Required, MaxLength(50)]
public string MiddleName { get; set; }
[Required, MaxLength(50)]
public string LastName { get; set; }
[Required, MaxLength(10)]
public string MobileNo1 { get; set; }
[MaxLength(10)]
public string MobileNo2 { get; set; }
[MaxLength(50)]
public string Email1 { get; set; }
[MaxLength(50)]
public string Email2 { get; set; }
public virtual ICollection<Vehicle> Vehicles { get; set; }
}
public class Vehicle
{
[Key]
public int VehicleID { get; set; }
[MaxLength(20)]
public string VehicleNumber { get; set; }
[ForeignKey("VehicleOwner")]
public int? VehicleOwnerID { get; set; }
[ForeignKey("VehicleOwnerID")]
public virtual Person VehicleOwner { get; set; }
[ForeignKey("VehicleDriver")]
public int? VehicleDriverID { get; set; }
[ForeignKey("VehicleDriverID")]
public virtual Person VehicleDriver { get; set; }
[ForeignKey("Person")]
public int? PersonID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Person { get; set; }
}
This generates two foreign keys on Vehicles table as
.ForeignKey("dbo.Person", t => t.PersonID)
.ForeignKey("dbo.Person", t => t.Person_PersonID)
whereas what i expect is only
.ForeignKey("dbo.Person", t => t.PersonID)
Initially i thought it might be because i missed out declaring the entities as virtual but that was not the case. I am not able to detect the problem with this code.
Like Vehicles, i have another class - Documents with somewhat the same structure and relationship with Person. But for Documents the foreign key is generated as expected.
You've got 3 classes pointing at Person, so configure as:
public class Vehicle
{
[Key]
public int VehicleID { get; set; }
[MaxLength(20)]
public string VehicleNumber { get; set; }
public int? VehicleOwnerID { get; set; }
[ForeignKey("VehicleOwnerID")]
public virtual Person VehicleOwner { get; set; }
public int? VehicleDriverID { get; set; }
[ForeignKey("VehicleDriverID")]
public virtual Person VehicleDriver { get; set; }
public int? PersonID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Person { get; set; }
}
While that is incorrect syntax, the 2nd foreign key comes from the collection of vehicles on the person and EF not being able to resolve which FK it belongs to.
So in your Person class on your collection you need to point to the corresponding nav in the vehicle:
[InverseProperty("Person")]
public virtual ICollection<Vehicle> Vehicles { get; set; }
http://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in-code-first.aspx
As Steve has said, this is because you have references to Person from your vehicle class. So, Entity Framework creates 3 keys one for Person, VehicleOwner, and VehicleDriver. Yet, it creates another key because of the collection of vehicles from person. You must use the inverse property attribute here to tell Entity Framework that you mean to build a relationship between Vehicle and Person on the actual Vehicle.Person property. You can do this by
[InverseProperty("Person")]
public virtual ICollection<Vehicle> Vehicles { get; set; }
Also, you being redundant with:
[ForeignKey("Person")]
public int? PersonID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Person { get; set; }
Remove the ForeignKey from the int? PersonID in your Vehicle class.
Related
How can I map the following relationship using Entity Framework Code First?
Relationship - Author is the principal and ApplicationUser is the dependent:
ApplicationUser is a derived class from IdentityUser. Authors can be created first than users, so they are the principal on the relationship. Every user must be associated with an author, but an author may be associated with no ApplicationUser. I've tried to do this with fluent API and with annotations, but I'm not able to say that the primary key of ApplicationUser is a foreign key to Author. Thank you very much!
Models:
public class Author
{
[Key]
public string AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<string> AditionalEmails { get; set; }
public virtual ICollection<PublicationData> Publication { get; set; }
//User is optional.
public virtual ApplicationUser User { get; set; }
}
public partial class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Affiliation { get; set; }
public string Position { get; set; }
public DateTime CreatedOn { get; set; }
public bool Deleted { get; set; }
public int CountryCode { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<PublicationData> Publications { get; set; }
public virtual ICollection<Request> Requests { get; set; }
//Author is required
public virtual Author Author { get; set; }
}
I have three tables
EVENT - PERSON - COMPANY
I need to have a relation many-to-many using those tables. An event can have one or more "clients",which can be either person or company. Normally, using no ORM, using sql , it would be something like :
EVENT
----
id
name
CLIENTEVENT
-----------
id
clientid
clienttype -- person or company
PERSON
-----------
id
name
lastname
...
COMPANY
-------
id
name
How does this approach can be replicated using entity framework? I am pretty new using EF so I would appreciate all help you can give me.I am using repository pattern, following this approach http://www.codeproject.com/Articles/838097/CRUD-Operations-Using-the-Generic-Repository-Pat.
I advice you instead of clientid and clienttype make another two columns: personID and companyID. Models will look this way:
public class Event
{
public int ID { get; set; }
public string name { get; set; }
public virtual ICollection<CLIENTEVENT> links { get; set; }
}
public class Company
{
public int ID { get; set; }
public string name { get; set; }
public virtual ICollection<CLIENTEVENT> links { get; set; }
}
public class PERSON
{
public int ID { get; set; }
public string name { get; set; }
public string lastname { get; set; }
public virtual ICollection<CLIENTEVENT> links { get; set; }
}
public class CLIENTEVENT
{
public int ID { get; set; }
public virtual PERSON person { get; set; }
public int? personID { get; set; }
public virtual Company company { get; set; }
public int? companyID { get; set; }
public virtual Event event1 { get; set; }
public int? event1ID { get; set; }
}
With EF Code First, you don't have to deal with joining tables like ClientEvent. You can simply write:
public class Event
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Company> Companies { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
Try this code, you'll see EF creating two linking tables (EventPerson and EventCompany). Now, the client type is the collection you read (someEvent.Persons or someEvent.Companies), you can also easily get the events linked to a specific Person or a Company.
About many-to-many with EF.
I have class in my MVC project and I used Entity Framework 6. Every person has a Master (master_Id) and it references to the same table (primary key in Person table). My way does not work... what's the solution?
public class Person
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
[ForeignKey("Person")]
public int Master_Id { get; set; }
public virtual Person Master { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
public class Person
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int? MasterId { get; set; }
[ForeignKey("MasterId")]
public virtual Person Master { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
I have a two classes. A person class and a Title class.
public class Person
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int TitleTypeId { get; set; }
}
public class TitleType
{
public int Id { get; set; }
public string Name { get; set; }
}
The title class contains things like "Mr., Mrs., MS.,etc.."
What is the proper way to define these classes so that I can use Linq to recall the desired title "name" from the TitleType table?
i.e. person.Title.name
public class Person
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public TitleType Title { get; set; }
}
EF will create additional column to handle Foreign Key for you automatically.
yesterday I created database in Management Studio and now I want to create it in program using EF Code First.
Here is link to my database: http://s11.postimg.org/6sv6cucgj/1462037_646961388683482_1557326399_n.jpg
And what I did:
public class GameModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreationTime { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string TotalTime { get; set; }
public DateTime RouteStartTime { get; set; }
public DateTime RouteEndTime { get; set; }
public int MaxPlayersPerTeam { get; set; }
public int CityId { get; set; }
public int CreatorId { get; set; }
[InverseProperty("Id")]
[ForeignKey("CreatorId")]
//public int TeamId { get; set; }
//[ForeignKey("TeamId")]
public virtual UserModel Creator { get; set; }
public virtual CityModel City { get; set; }
//public virtual TeamModel WinnerTeam { get; set; }
}
public class RegionModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CityModel> Cities { get; set; }
}
public class CityModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RegionId { get; set; }
public virtual RegionModel Region { get; set; }
public virtual ICollection<UserModel> Users { get; set; }
public virtual ICollection<GameModel> Games { get; set; }
}
public class UserModel
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime RegistrationDate { get; set; }
public string FacebookId { get; set; }
public int CityId { get; set; }
public virtual CityModel City { get; set; }
public virtual IEnumerable<GameModel> Games { get; set; }
}
For now I wanted to create 4 tables but I have some problems... I want to make CreatorId in GameModel, but it doesn't work... When i wrote UserId instead of CreatorId it was working ( without [InverseProperty("Id")] and [ForeignKey("CreatorId")]).
This is what i get:
The view 'The property 'Id' cannot be configured as a navigation property. The property must be a valid entity type and the property should have a non-abstract getter and setter. For collection properties the type must implement ICollection where T is a valid entity type.' or its master was not found or no view engine supports the searched locations.
edit:
I changed it like this:
public int CityId { get; set; }
public int CreatorId { get; set; }
[ForeignKey("CityId")]
public virtual CityModel City { get; set; }
[ForeignKey("CreatorId")]
public virtual UserModel Creator { get; set; }
And there is another problem.
The view 'Introducing FOREIGN KEY constraint 'FK_dbo.UserModels_dbo.CityModels_CityId' on table 'UserModels' 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. See previous errors.' or its master was not found or no view engine supports the searched locations.
And I have no idea how to solve it.
The InversePropertyAttribute specifies, which navigation property should be used for that relation.
A navigation property must be of an entity type (the types declared in your model, GameModel for example) or some type implementing ICollection<T>, where T has to be an entity type. UserModel.Id is an int, which clearly doesn't satisfy that condition.
So, the inverse property of GameModel.Creator could be UserModel.Games if you changed the type to ICollection<GameModel>, or had to be left unspecified. If you don't specify an inverse property, EF will try to work everything out on its own (in this case it would properly recognize GameModel.Creator as a navigation property, but UserModel.Games would most likely throw an exception, as it is neither an entity type, nor does it implement ICollection<T> with T being an entity type, nor is it a primitive type from a database point of view). However, EF's work-everything-out-by-itself-magic doesn't cope too well with multiple relations between the same entity types, which is when the InversePropertyAttribute is needed.
A quick example that demonstrates the problem:
class SomePrettyImportantStuff {
[Key]
public int ID { get; set; }
public int OtherId1 { get; set; }
public int OtherId2 { get; set; }
[ForeignKey("OtherId1")]
public virtual OtherImportantStuff Nav1 { get; set; }
[ForeignKey("OtherId2")]
public virtual OtherImportantStuff Nav2 { get; set; }
}
class OtherImportantStuff {
[Key]
public int ID { get; set; }
public virtual ICollection<SomePrettyImportantStuff> SoldStuff { get; set; }
public virtual ICollection<SomePrettyImportantStuff> BoughtStuff { get; set; }
}
Here, EF knows that it has to generate 2 FKs from SomePrettyImportantStuff to OtherImportantStuff with the names Id1 and Id2, but it has no way to tell which of the IDs refers to the entity where it was sold from and which is the one it was bought from.
Edit: How to fix the cyclic reference problem
To fix that problem, your context class should override OnModelCreating and configure the foreign keys which shouldn't cascade on delete accordingly, like this:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<CityModel>().HasMany(c => c.Users).WithRequired(u => u.City)
.HasForeignKey(u => u.CityId).WillCascadeOnDelete(value: false);
// Add other non-cascading FK declarations here
base.OnModelCreating(builder);
}