I have the following two classes:
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
}
public class Trip
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<Person> Persons { get; set; }
}
As you can see, a Trip can have 1 or more Persons...
I tried to use the EntityConfiguration to build the database properly but I cannot manage to make it work... I am quite confused on its usage:
public class TripConfiguration : EntityTypeConfiguration<Trip>
{
internal TripConfiguration()
{
// ???
}
}
What do I need to write to have the application to behave properly:
I need at least one person.
I might have more that one person
A person cannot be in the SAME trip twice
A person can be in more than one trip
Try this:
this.HasRequired(x => x.Person)
.WithMany(x => x.Trips)
.HasForeignKey(x => x.PersonId);
Your classes:
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Trip> Trips { get; set;}
}
public class Trip
{
public int Id { get; set; }
public string Name { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
And as far that I know, EF doesn't support unique FK (or correct me if I'm wrong..). So you have to check it yourself.
This is not a One-To-Many relationship, this is a Many-To-Many relationship, you need to have collections on both sides of the relationship. EF will create the joiner table on your behalf. Since today you cannot configure a person being in a trip only once you will need to create a unique constraint in your joiner table once is created to assure this happens since EF does not yet support Unique Key constraints through configuration.
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Trip> Trips { get; set; }
}
public class Trip
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
then
class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
this.HasMany(t => t.Trips).WithMany(t => t.Persons);
}
}
Related
Let's say I have a bike shops that sell various types of bikes: pro, kids, youth, leisure and any mixture. So I have a table of shops that refers/relates to a table of possible types. Now these shops also host events with the same types: events for pros, kids etc again any mixture. And so I have another table of events that also need to refer/relate to the same table of types:
I need to be able in a single quick query get a list of all bike types for a shop or event.
So I figured I'd have 3 main tables: Shops, Events, BikeTypes and two intermediate to link shops and events to bike types:
And I organized my models as:
public class BikeShop
{
public long Id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string phone { get; set; }
}
public class BikeEvent
{
public long Id { get; set; }
public string name { get; set; }
public string description { get; set; }
public DateTime date { get; set; }
public string location { get; set; }
}
public class BikeType
{
public long Id { get; set; }
public string name { get; set; }
public string code { get; set; }
}
public class ShopBikeTypes
{
public long Id { get; set; }
public BikeShop shop { get; set; }
public BikeType biketype { get; set; }
}
public class EventBikeTypes
{
public long Id { get; set; }
public BikeEvent bikeevent { get; set; }
public BikeType biketype { get; set; }
}
With DataCotext:
public class DataContext : DbContext
{
public DbSet<BikeShop> Shops { get; set; }
public DbSet<BikeEvent> Events { get; set; }
public DbSet<BikeType> BikeTypes { get; set; }
public DbSet<ShopBikeTypes> ShopBikeTypes { get; set; }
public DbSet<EventBikeTypes> EventBikeTypes { get; set; }
}
Migration creates correct database structure just as my diagram. Great!
Now how do I make a straight forward query:
get list of all bike types for a shop
get list of all bike types for an event
Is my structure even correct?
Do I need some List<> in the main object models BikeShop and BikeEvent?
EF's include and theninclude seem to require some list?
This feels like such a typical scenario. What's the right way of doing this?
Thank you.
Those are the linq queries that you are asked but when i look at that your class models, i can say they are wrong. U need to define first which relation theyre having. if all of that relation has based on one-to-one, u wont gonna need any List<> in your class models. but if u have one-to-many relation,u gonna need them.
1- get list of all bike types for a shop
return DbContext.Shops
.Include(x>=x.ShopBikeTypes)
.ThenInclude(x=>x.BikeTypes).ToList();
2- get list of all bike types for an event
return DbContext.Events
.Include(x=>x.EventBikeTypes)
.ThenInclude(x=>x.BikeTypes).ToList();
3- Get all data in that relation
return DbContext.BikeTypes
.Include(x>=x.EventBikeTypes)
.ThenInclude(x=>x.Events).AsSplitQuery()
.Include(x=>x.ShopBikeTypes)
.ThenInclude(x>=x.Shops).AsSplitQuery()
.ToList();
it can be a tough query, do not try to use AsNoTracking() because it can cause Cartesian Explosion.
#BerkGarip: thank you for your help. I ended up with this models structure:
public class AShop
{
public long Id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string phone { get; set; }
public List<AShopType> aTypes { get; set; }
}
public class AEvent
{
public long Id { get; set; }
public string name { get; set; }
public string description { get; set; }
public DateTime date { get; set; }
public string location { get; set; }
public List<AEventType> aTypes { get; set; }
}
public class AType
{
public long Id { get; set; }
public string name { get; set; }
public string code { get; set; }
}
public class AShopType
{
public long Id { get; set; }
public AType aType { get; set; }
}
public class AEventType
{
public long Id { get; set; }
public AType aType { get; set; }
}
In order to achieve what I needed using answer from #BerkGarip I figured out that the trick there was to have lists in the 'shop' and 'event' models to the intermediate objects which in turn have a single reference to 'type'. This way database layout is the same and it is many-to-many relationship and I can use 'include' and 'thenInclude' exactly as expected:
return await _context.AShops.Where(x => x.name == "Z")
.Include(x => x.aTypes)
.ThenInclude(y => y.aType)
.ToListAsync();
I am using EF 6.1.1.
I am unable to figure out how to create One-to-One relationship between two classes/tables with both entities have their owns PKs. I originally posted question link but could not get much help on it OR i am not able to get it. So, here i am putting my question in simple way.
Appreciate if someone can share thoughts on it.
My Requirement:
I would like create One-To-One relationship between Principle and Dependant with 'Id' from Principle class acts as Foreign Key in dependant class.
Principle Class
public class Student
{
public string FullName {get; set;}
}
Dependant Class
public class StudentReport
{
public string RollNumber { get; set; }
public string StudentType { get; set; }
}
Add PKs – EF requires this:
public class Student
{
public int Id { get; set; }
public string FullName { get; set; }
}
public class StudentReport
{
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
}
Note that EF 5 and later supports naming conventions: Id indicates a primary key. Alternately, it also supports the name of the class followed by "Id", so the above keys could have been StudentId for Student and StudentReportId for StudentReport, if you wished.
Add the foreign relation as a navigation property to at least one of the tables – in this case, you stated that StudentReport is the dependent, so let's add it to that one:
public class Student
{
public int Id { get; set; }
public string FullName { get; set; }
}
public class StudentReport
{
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public Student Student { get; set; }
}
Again – by naming convention – EF determines that a single Student property on StudentReport indicates that this is a navigational property associated with a foreign key. (By defining only the Student property, but no foreign key property, you are indicating that you don't care what EF names the associated FK ... basically, you're indicating you'll always access the related Student via the property.)
If you did care about the name of the FK property, you could add it:
public class Student
{
public int Id { get; set; }
public string FullName { get; set; }
}
public class StudentReport
{
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public int StudentId { get; set; }
public Student Student { get; set; }
}
Again – by naming convention – EF determines that StudentId is the FK associated with the Student property because it has the class name, "Student", followed by "Id".
All of this, so far, has been using conventions as defined in Entity Framework Code First Conventions, but Data Annotations are also an option, if you wish:
public class Student
{
[Key]
public int Id { get; set; }
public string FullName { get; set; }
}
public class StudentReport
{
[Key]
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
[ForeignKey("Student")]
public int StudentId { get; set; }
public Student Student { get; set; }
}
Doing this is actually a good idea, because it makes clearer your intent to other programmers that might not be aware of EF Conventions – but can easily infer them from simply looking at EF Data Annotations – and is still less cumbersome than Fluent API.
UPDATE
I just realized, I left this as a one-to-many, with enforcement of the one-to-one relationship being left to do in the code using this model. To enforce the one-to-one in the model, you could add a navigation property to the Student class going the other way:
public class Student
{
public int Id { get; set; }
public string FullName { get; set; }
public StudentReport StudentReport { get; set; }
}
public class StudentReport
{
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public Student Student { get; set; }
}
However, that's going to break, because EF doesn't know which entity to insert first on an add. To indicate which is dependent, you have to specific that the dependent class' PK is the FK to the principal class (this enforces one-to-one because – in order for a Student/StudentReport pair to be associated – their Id properties must be the exact same value):
public class Student
{
public int Id { get; set; }
public string FullName { get; set; }
public StudentReport StudentReport { get; set; }
}
public class StudentReport
{
[ForeignKey("Student")]
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public Student Student { get; set; }
}
or, using the full set of Data Annotations from earlier:
public class Student
{
[Key]
public int Id { get; set; }
public string FullName { get; set; }
public StudentReport StudentReport { get; set; }
}
public class StudentReport
{
[Key, ForeignKey("Student")]
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public Student Student { get; set; }
}
Using EntityFramework 6.1.3, I've got the following
public class RacesContext:DbContext
{
public DbSet<Race> Races { get; set; }
public DbSet<Sailboat> Sailboats { get; set; }
public DbSet<VenueParticipation> VenueParticipations { get; set; }
}
public class Crew
{
public int CrewId { get; set; }
public string Name { get; set; }
}
public class Sailboat
{
[Key]
public int SailboatId { get; set; }
public string Name { get; set; }
public string Skipper { get; set; }
public virtual ICollection<Crew> BoatCrew { get; set; }
}
public class VenueParticipation
{
[Key]
public int Id { get; set; }
public virtual ICollection<Sailboat> Boats { get; set; }
public virtual ICollection<Race> Races { get; set; }
}
public class Race
{
[Key]
public int RaceId { get; set; }
public string Venue { get; set; }
public DateTime Occurs { get; set; }
}
EF creates the Creates the Crews table with the proper PK and FK as I would expect. But creates the Races Sailboats, VenueParticipations tables in an unexpected way. Sailboats get's the expected PK but the unexpected FK VenueParticipation_Id as does Races. I was expecting the VenueParticipations table to get FKs to the others allowing a many to many relationship.. I'm sure I'm missing something here. Any advice would be great.
You can either configure the joining tables VenueParticipationSailboat, VenueParticipationRace with the proper FKs or you can use the fluent API:
modelBuilder.Entity<VenueParticipation>()
.HasMany(t => t.Sailboats)
.WithMany(t => t.VenueParticipations)
.Map(m =>
{
m.ToTable("VenueParticipationSailboat");
m.MapLeftKey("VenueParticipationID");
m.MapRightKey("SailboatID");
});
https://msdn.microsoft.com/en-us/data/jj591620.aspx#ManyToMany
my code like below
public class User
{
public int ID { get; set; }
public int BillingAddressID { get; set; }
public Address BillingAddress { get; set; }
public IList<Shipment> Shipments { get; set; }
}
public class Address
{
public int ID { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
public class Shipment
{
public int ID { get; set; }
public string State { get; set; }
public int DeliveryAddressID { get; set; }
public Address DeliveryAddress { get; set; }
public User ShipUser { get; set; }
//[ForeignKey("ShipUser")]
public int ShipUserID { get; set; }
//public int UserId { get; set; }
}
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<Shipment> Shipments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Shipment>().HasRequired(u => u.ShipUser)
.WithMany(d => d.Shipments)
.HasForeignKey(c => c.ShipUserID)
.WillCascadeOnDelete(false);
}
}
if i remove the override method,i will get an error "SqlException: Introducing FOREIGN KEY constraint 'FK_Shipments_Users_ShipUserID' on table 'Shipments' 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."
if i remove ShipUserID in Shipment Class,it will work ok,when i see the table that is created by ef,i found a column named Shipment_UserID in table Shipment.I don`t know why.
if rename the class indenty key to UserID,it also work ok.
I try it anyway,but I don`t know the reason, I need some books about EF associations.
If you don't have mapping specified without cascadeDelete=false for one relationship it will create multiple cascade paths if you have tow relationships to user from Shipment.
By convention you can use public
Public User ShipUser { get; set; }
public int ShipUserID { get; set; }
it will use ShipUserID as foreign key by convention.
If you remove ShipUserID Ef need to create his own foreign key to keep the relationship . that is your ' Shipment_UserID'
rename the class indenty key to UserID I don't understand what you meant.
Here is a good tutorial to start with
I have two tables (Table A, Table B) joined with a join table (TableAB) with 3 payload columns. By Payload I mean columns apart from Id, TableAId, and TableBId.
I can insert into all tables successfully, but I need to insert data into one of the payload columns on Insert. I'm using EF 4.3, Fluent API. Can anyone help? Thanks in advance.
public class Organisation : EntityBase<int>, IAggregateRoot
{
public string Name { get; set; }
public string Url { get; set; }
public int CountryId { get; set; }
public int? OwnershipTypeId { get; set; }
public int OrganisationStatusId { get; set; }
public virtual ICollection<Feature> Features { get; set; }
public virtual ICollection<OrganisationType> OrganisationTypes { get; set; }
public virtual ICollection<PricePlan> PricePlans { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User: EntityBase<Guid>, IAggregateRoot
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string JobTitle { get; set; }
public int? PhoneCallingCodeId { get; set; }
public int? PhoneAreaCode{ get; set; }
public string PhoneLocal { get; set; }
public int? MobileCallingCodeId { get; set; }
public int? MobileAreaCode { get; set; }
public string MobileLocal { get; set; }
public virtual ICollection<Organisation.Organisation> Organisations { get; set; }
}
public class OrganisationUser : EntityBase<int>, IAggregateRoot
{
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int OrganisationRoleId {get; set;}//Foreign Key - have tried leaving it out, tried it as public virtual Organisation Organisation {get;set;
public bool IsApproved { get; set; }
}
public class SDContext : DbContext
{
public ObjectContext Core
{
get
{
return (this as IObjectContextAdapter).ObjectContext;
}
}
public IDbSet<User> User { get; set; }
public IDbSet<Organisation> Organisation { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Organisation>().HasMany(u => u.Users).WithMany(o => o.Organisations).Map(m =>
{
m.MapLeftKey("OrganisationId");
m.MapRightKey("UserId");
m.ToTable("OrganisationUser");
});
//I have tried specifically defining the foreign key in fluent, but I really need to understand how I can add the payload properties once I access and edit them.
Your mapping is not correct for your purpose. If you want to treat OrganisationUser as an intermediate entity between Organisation and User you must create relationships between Organisation and OrganisationUser and between User and OrganisationUser, not directly between Organisation and User.
Because of the intermediate entity which contains its own scalar properties you cannot create a many-to-many mapping. EF does not support many-to-many relationships with "payload". You need two one-to-many relationships:
public class Organisation : EntityBase<int>, IAggregateRoot
{
// ...
// this replaces the Users collection
public virtual ICollection<OrganisationUser> OrganisationUsers { get; set; }
}
public class User : EntityBase<Guid>, IAggregateRoot
{
// ...
// this replaces the Organisations collection
public virtual ICollection<OrganisationUser> OrganisationUsers { get; set; }
}
public class OrganisationUser : EntityBase<int>, IAggregateRoot
{
public int OrganisationId { get; set; }
public Organisation Organisation { get; set; }
public Guid UserId { get; set; }
public User User { get; set; }
// ... "payload" properties ...
}
In Fluent API you must replace the many-to-many mapping by the following:
modelBuilder.Entity<Organisation>()
.HasMany(o => o.OrganisationUsers)
.WithRequired(ou => ou.Organisation)
.HasForeignKey(ou => ou.OrganisationId);
modelBuilder.Entity<User>()
.HasMany(u => u.OrganisationUsers)
.WithRequired(ou => ou.User)
.HasForeignKey(ou => ou.UserId);
Your derived DbContext may also contain a separate set for the OrganisationUser entity:
public IDbSet<OrganisationUser> OrganisationUsers { get; set; }
It's obvious now how you write something into the intermediate table:
var newOrganisationUser = new OrganisastionUser
{
OrganisationId = 5,
UserId = 8,
SomePayLoadProperty = someValue,
// ...
};
context.OrganisastionUsers.Add(newOrganisastionUser);
context.SaveChanges();
If you want to make sure that each pair of OrganisationId and UserId can only exist once in the link table, it would be better to make a composite primary key of those two columns to ensure uniqueness in the database instead of using a separate Id. In Fluent API it would be:
modelBuilder.Entity<OrganisationUser>()
.HasKey(ou => new { ou.OrganisationId, ou.UserId });
More details about such a type of model and how to work with it is here:
Create code first, many to many, with additional fields in association table