EF 4.1 RC Many to many relationship in EF CF - entity-framework

I have two entities with many to many relationship like this:
class author
{
public int AuthorID{get;set;}
public string Name{get;set;}
public virtual ICollection<book> books{get;set;}
}
class book
{
public int BookID{get;set;}
public string Name{get;set;}
public virtual ICollection<author> authors{get;set;}
}
and I have an intermediate table named Bookauthor defined like this:
BookAuthor: int
int AuthorID
int BookID
How to map this using FluentAPI
Thanks!!

This was problematic with EDMX but with EF 4.1 fluent API you can map it:
modelBuilder.Entity<book>()
.HasMany(b => b.authors)
.WithMany(a => a.books)
.Map(m => m.MapLeftKey("BookID")
.MapRightKey("AuthorID")
.ToTable("BookAuthor"));
As you can see I don't map BookAuthor column. That column is unknown to EF and must be auto incremented.
This obviously can't work with a Code-first approach but only if you use Fluent API against existing database.

I don't think EF allows you to have a separate Id in many-to-many junction tables.
The alternative is just mapping BookAuthor as an entity.
On a side note, NHibernate supports this construct using an idbag

Related

Self referencing / parent-child relationship one-to-zero or one in Entity Framework Core

I want to create a referencing / parent-child relationship one-to-zero or one in Entity Framework Core. I mean that my entity could have a parent:
public class MyEntity
{
public Guid Id { get; set; }
public Guid? ParentEntityId { get; set; }
public MyEntity ParentEntity { get; set; }
public MyEntity ChildEntity { get; set; }
}
I am trying to configure it via fluent api:
entity.HasOne(x => x.ParentEntity)
.WithOne(x => x.ChildEntity)
.HasForeignKey( .... )
I do not understand what I do have to write in the last line. I am not either sure my entity is correct.
Can anyone help me please?
EDIT: This question does not resolve my problem: Self referencing / parent-child relationship in Entity Framework
My problem is about create the foreign key. This line does not work:
.HasForeignKey(x => x.ParentEntityId)
HasForeignKey expects a string in input.
In a one-to-one relationship you always have to specify the dependent entity type in the HasForeignKey call, i.e. the entity that will contain the foreign key. For a one-to-one relationship between two different classes that makes sense, see the standard EF example. For a self-reference it looks obvious that EF should figure out there's no option. Still, you have to specify the type:
modelBuilder.Entity<MyEntity>()
.HasOne(x => x.ParentEntity)
.WithOne(x => x.ChildEntity)
.HasForeignKey<MyEntity>(c => c.ParentEntityId);

Entity Framework nested joins

I am new to entity framework and am having a hard time trying to figure out how to query with a join when my models look like this (drastically simplified)
class Customer
{
public int Id {get; set;}
public Vehicles Vehicles {get; set;}
}
class Vehicles
{
public List<Vehicle> Items {get; set;}
}
class Vehicle
{
public int Id {get; set;}
public int CustomerId {get; set;}
}
If I put the List<Vehicle> on the customer class directly. I am able to do fluent mapping like this
builder.Entity<Customer>()
.HasMany(x => x.Items)
.WithOne()
.HasForeignKey(x => x.CustomerId);
Which then I can do this and I get back a customer object with vehicles
db.Customers.Include(x => x.Items).FirstOrDefault(x => x.Id == 1);
What I am not understanding is how to do this with my original set of models. I would like to keep them the way they are if possible. I have tried doing various versions of this in my onModelCreating method with no luck.
builder.Entity<Customer>(t =>
{
t.OwnsOne(x => x.Vehicles, v =>
{
v.HasMany(x => x.Items).WithOne().HasForeignKey(x => x.CustomerId);
});
});
It's possible to map the original classes, but in quite counterintuitive way.
Since the Vehicles class is just a container, mapping it as owned entity as you have tried seems the most natural way. However currently EF Core does not allow owned entity to be at the principal side of the relationship, and in your case this is needed.
So instead you need to map the Vehicles class as regular "entity" sharing the same table with the Customer - the so called table splitting. You have to do explcitly all that EF Core does implicitly for owned entities - define a shadow property and map is a both PK and FK for the one-to-one relationship with the Customer. You'd need also the explicitly map the Vehicle.CustomerId as a FK because from EF point of view the Vehicle is related to Vehicles rather than to Custome, hence the conventional FK property / column name assumed will be VehiclesId. Note that with this model you'll never be able to define an inverse navigation property Customer of the Vehicle.
With that being said, here is the fluent configuration needed:
modelBuilder.Entity<Vehicles>(builder =>
{
// Table
builder.ToTable(modelBuilder.Entity<Customer>().Metadata.Relational().TableName);
// PK
builder.Property<int>("Id");
builder.HasKey("Id");
// One-to-one relationship with Customer
builder.HasOne<Customer>()
.WithOne(e => e.Vehicles)
.HasForeignKey<Vehicles>("Id");
// One-to-many relationship with Vehicle
builder.HasMany(e => e.Items)
.WithOne()
.HasForeignKey(e => e.CustomerId);
});
and usage:
db.Customers
.Include(x => x.Vehicles.Items) // <--
// ...
Use .Join
See this question for some examples:
Entity Framework Join 3 Tables

Entity Framework map one to many with an intermediate table without a class for that table

I have
Issue
{
//Id, etc
public List<CloudFile> Files { get; set; }
}
and since CloudFile can be used by other classes, the cleanest solution seems to create an intermediate table called IssueFile. The thing is I have no use for such a class in my code... so how do I map between Issue and CloudFile without creating such an intermediate class. Or is this even possible? I know that there are similar questions (One to Many mapping with an intermediate table) but they create this intermediate but, hopefully, unnecessary class.
What you want is Table-per-Type (TPT) inheritance. CloudFile would be your base class and the derived types would represent the relationship to the owning entities (Issue, Order, etc.):
[Table( "CloudFile" )]
public class CloudFile
{
public int Id { get; set; }
}
[Table( "IssueCloudFile" )]
public class IssueCloudFile : CloudFile
{
}
public class Issue
{
public int Id { get; set; }
public List<IssueCloudFile> Files { get; set; }
}
Or via Fluent API:
modelBulider.Entity<CloudFile>().ToTable( "CloudFile" );
modelBuilder.Entity<IssueCloudFile>().ToTable( "IssueCloudFile" );
If you use DbSet<CloudFile> only without DbSets for a derived type of CloudFile, use .OfType<T>() to get only those types:
var issueCloudFiles = db.CloudFiles.OfType<IssueCloudFile>();
// or through the referencing entities themselves
issueCloudFiles = db.Issues.SelectMany( i => i.Files );
The only time there is no getting out of an intermediate table is for a many-to-many mapping. In your case, you're dealing with a many-to-optional or many-to-required. Now you have two options:
1) Foreign Key association: for this you will need to define your foreign key in your CloudFile class. This has some advantages, because you can update relationships by changing foreign key value and you can add new entities without needing the dependant entity by using dummy foreign key values. Overall it's just easier to work with.
modelBuilder.Entity<Issue>()
.HasMany(i => i.CloudFile)
.WithRequired(i => i.Issue)
.HasForeignKey(i => i.Id);
2) Independant association: no foreign key on your model (it does use the key internally of course) and the relationship has it's own state tracked.
modelBuilder.Entity<Issue>()
.HasMany(i => i.CloudFile)
.WithRequired(i => i.Issue)
.Map(i => i.MapKey("Id"));

Specify an association table in Code First

Consider the scenario where an entity called List can hold many User.
public class List{
public int Id{get;set;}
public string Name{get;set;}
public virtual List<User>Users{get;set;
}
public class User{
public int Id{get;set;}
public string Name{get;set;}
public virtual List<List>List{get;set;
}
I have an association table called ListUserAssociation in my db and that contains multiple records.
The Table has 2 columns ListId and UserId
How do I specify this association in EF code First?
P.S . I cannot drop and recreate the table.
Since you have collections defined on both sides, you can define the relationship from either side:
modelBuilder.Entity<List>().HasMany(e => e.Users).WithMany(e => e.List).Map(ca => ca.MapLeftKey(new string[] {"ListId"}).MapRightKey(new string[] {"UserId"}).ToTable("ListUserAssociation");
modelBuilder.Entity<User>().HasMany(e => e.List).WithMany(e => e.Users).Map(ca => ca.MapLeftKey(new string[] {"UserId"}).MapRightKey(new string[] {"ListId"}).ToTable("ListUserAssociation");
I believe you only need one line or the other in order to define the relationship. Even though it is redundant, I prefer to include both lines so that I see the relationship defined from either entity (I place all of the mapping code in separate classes for each entity).

Map names for generated relationship tables in EF code first

I'm giving Code first a try and I have the requirement of a prefix of (all) my tables in db.
In my DbContext I have these entities:
public DbSet<Person> People { get; set; }
public DbSet<Department> Departments { get; set; }
I can successfully map table names for my entities by overriding:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().ToTable("w_people");
modelBuilder.Entity<Department>().ToTable("w_departments");
}
However for tables that created that don't directly map to a table I can't figure out to prefix.
In my example people can belong to many departments so a "non-entity" table is created by EF. (I'm a EF noob so these tables probably have a fancy name) So in my db I get three tables:
w_people
w_departments
PersonsDepartments
The PersonsDepartments table is what I'm after. How can I prefix these generated tables or change name/mapping after generation?
TIA
I have solved this:
modelBuilder.Entity<Person>().HasMany(p => p.Departments)
.WithMany(d => d.People)
.Map(mc => mc.ToTable("w_peopledepartments"));