One-to-many using EF5 fluent API - c#

Using EF5, I want a one-to-many mapping for Car:Wheel == 1:0..n
public class Car {
public int ID { get; set; }
public virtual ICollection<Wheel> Wheels { get; set; }
}
public class Wheel {
public int ID { get; set; }
// I don't want to define a reverse relationship here
}
So for Car I did:
modelBuilder.Entity<Car>()
.HasMany(x => x.Wheels)
.WithMany()
.Map(x => x
.MapLeftKey("CarID")
.MapRightKey("WheelID")
.ToTable("Car_Wheel"));
Which gives me an n:n join table. But I want 1:n.
Do I need to define a unique constraint on Car_Wheel.CarID (if so, how?), or is there an easier way?

But I want 1:n
Use WithRequired or WithOptional:
modelBuilder
.Entity<MyParentEntity>()
.HasMany(_ => _.Children)
.WithRequired() //.WithOptional()
.Map(/* map association here */);
But it will be better, if you will use foreign key associations:
public class Wheel
{
public int ID { get; set; }
public int CarID { get; set; }
}
modelBuilder
.Entity<MyParentEntity>()
.HasMany(_ => _.Children)
.WithRequired() //.WithOptional()
.HasForeignKey(_ => _.ParentId);

Related

EF Core - many to many relationship use / access custom join table

I am trying to implement a many to many relationship.
The Models -
public class User
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public List<Book> OwnedBooks { get; set; }
}
public class Own
{
public int UserId { get; set; }
public int BookId { get; set; }
public User User { get; set; }
public Book Book { get; set; }
}
public class Book
{
[Key]
public int Id { get; set; }
public int AuthorId { get; set; }
public User Author { get; set; }
public List<User> OwnedBy { get; set; } //Not really needed, but without it I can't create the join table "Own"
[NotMapped]
public int UsersReached; //Get this via the "Own" table
}
The DbContext -
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Book> Books { get; set; }
public DbSet<Own> Own { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlServer("Server=DESKTOP-BT4H8CA;Database=Test;Trusted_Connection=True");
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Book>().HasOne(x => x.Author);
builder.Entity<User>()
.HasMany(x => x.OwnedBooks)
.WithMany(x => x.OwnedBy)
.UsingEntity(x => x.ToTable("Own"));
builder.Entity<Own>()
.HasKey(x => new {x.BookId, x.UserId});
}
}
I am struggling with accessing the join table "Own". I need it to get the amount of each Book that is sold, without completely loading the users. That's why I don't want to use the auto generated one:
Cannot use table 'Own' for entity type 'BookUser (Dictionary<string, object>)' since it is being used for entity type 'Own' and potentially other entity types, but there is no linking relationship. Add a foreign key to 'BookUser (Dictionary<string, object>)' on the primary key properties and pointing to the primary key on another entity typed mapped to 'Own'.
Thanks in advance for your help!
You can actually use the auto-generated joining table and still get the count of each book sold, without completely loading the users.
With your current User and Book models, configure the relationships as -
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Book>()
.HasOne(p => p.Author)
.WithMany()
.HasForeignKey(p => p.AuthorId)
.OnDelete(DeleteBehavior.NoAction);
builder.Entity<User>()
.HasMany(p => p.OwnedBooks)
.WithMany(p => p.OwnedBy);
}
Then you can query the books with their count of sales as -
var books = dbCtx.Books
.Select(p => new Book
{
Id = p.Id,
AuthorId = p.AuthorId,
Author = p.Author,
UsersReached = p.OwnedBy.Count // this will not load User entities
})
.ToList();
EDIT:
You can use AutoMapper which can do the projection in .Select() method for you, like -
var dtos = _Mapper.ProjectTo<BookDTO>(dbCtx.Books).ToList();
For that, you'll need to -
create a DTO model with properties you want from the query result, like -
public class BookDTO
{
public int Id { get; set; }
public string Author { get; set; }
public int UsersReached { get; set; }
}
define a map from Book to BookDTO -
CreateMap<Book, BookDTO>()
.ForMember(d => d.Author, opt => opt.MapFrom(s => s.Author.Name))
.ForMember(d => d.UsersReached, opt => opt.MapFrom(s => s.OwnedBy.Count));
You can remove the [NotMapped] property UsersReached from the Book model.

EF: one to one relationship without generate new column using fluent API

every time I tried to add-migration and then it shows me the code like this:
public override void Up()
{
AddColumn("dbo.Orders", "OrderItems_Id", c => c.Int(nullable: false));
CreateIndex("dbo.Orders", "OrderItems_Id");
AddForeignKey("dbo.Orders", "OrderItems_Id", "dbo.OrderItems", "Id");
}
I seek the google and haven't found the solution,my entity belows,I want to set the OrderId of orderItems as a foreign key references by Orders.
public class OrderItems
{
public int Id { get; set; }
public int OrderId {get; set;}
public virtual Orders Order { get; set; }
}
public class Orders
{
public int Id { get; set; }
public virtual OrderItems Order { get; set; }
}
My Fluent API looks like belows:
public OrderItemsMapping()
{
this.HasKey(x => x.Id);
this.Property(x => x.GoodId).IsRequired();
this.Property(x => x.OrderId).IsRequired();
this.Property(x => x.Price).IsRequired();
this.Property(x => x.Quantity).IsRequired();
this.Property(x => x.Score).IsRequired();
this.HasOptional(x => x.Order)
.WithOptionalPrincipal(x => x.OrderItems)
.Map(configurationAction: new Action<ForeignKeyAssociationMappingConfiguration>(x => x.MapKey()));
}
another things I have to be mentioned is that x.MapKey("Id") will trigger error :Name had been set and it must be unique. thanks.
You have said that, there is one-to-one relationship between Order and OrderItem entities. If this is the case, then OrderId must be primary key in the OrderItem entity.
Also, your entity's names shouldbe named in singular rather than plural by convention.
As a result, your entities must look like:
public class OrderItem
{
public int OrderId { get; set;}
public virtual Order Order { get; set; }
}
public class Order
{
public int Id { get; set; }
public virtual OrderItem OrderItem { get; set; }
}
And OrderItem mapping:
// Primary Key
this.HasKey(m => m.OrderId);
...
this.HasRequired(m => m.Order)
.WithRequiredDependent(m => m.OrderItem);
...

How do I properly add multiple one-to-one relationships on two tables using Entity Framework?

I want to create a Lighthead class that has two one-to-one relationships with the Lens class. When I map the classes as they are set up below I receive the following error.
The operation failed because an index or statistics with name 'IX_Id' already exists on table 'dbo.Lens'.
How do I fix this?
here is my mapping
public class LensMap : EntityTypeConfiguration<Lens>
{
public LensMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// Table & Column Mappings
this.ToTable("Lens");
this.Property(t => t.Id).HasColumnName("Id");
// Relationships
this.HasRequired(t => t.LightHead)
.WithOptional(t => t.Lens);
this.HasRequired(t => t.LightHead1)
.WithOptional(t => t.Lens1);
}
}
Here is my lens class
public class Lens
{
public int Id { get; set; }
public virtual LightHead LightHead { get; set; }
public virtual LightHead LightHead1 { get; set; }
}
Here is my lighthead class
public class LightHead
{
public int Id { get; set; }
public virtual Lens Lens { get; set; }
public virtual Lens Lens1 { get; set; }
}
Note I am using Entity Framework 6 and C#
also I have tried to do this with one LightHead in the Lens class it returns the following error
Schema specified is not valid. Errors: The relationship 'WebApplication2.Models.LightHead_Lens' was not loaded because the type 'WebApplication2.Models.Lens' is not available.
I believe you need a foreing key here.
// Relationships
this.HasRequired(t => t.LightHead)
.WithOptional(t => t.Lens)
.HasForeignKey(t => t.LightHeadId)
this.HasRequired(t => t.LightHead1)
.WithOptional(t => t.Lens1);
.HasForeignKey(t => t.LightHead1Id)
...
public class Lens
{
public int Id { get; set; }
public int LightHeadId {get;set;}
public int LightHead1Id {get;set;}
public virtual LightHead LightHead { get; set; }
public virtual LightHead LightHead1 { get; set; }
}
public class LightHead
{
public int Id { get; set; }
public virtual Lens Lens { get; set; }
public virtual Lens Lens1 { get; set; }
}
Alternatively you can use Entity Framework table splitting feature to map two entities to single table.
This also lets you query part of your table columns as single entity in order to improve performance.
So Your mapping should be as below
modelBuilder.Entity<LightHead>.HasOptional(x=> x.Lens).WithRequired(a=> a.LightHead);
modelBuilder.Entity<LightHead>.HasOptional(x=> x.Lens1).WithRequired(a => a.LightHead1);
modelBuilder.Entity<Lens>().Property(x=> x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
You need to set databaseGeneratedOption to None becuase Id of Lens will be same as Id of LightHead as in one to one relationship you can't have primary keys in secondary tables.
Secondary entity is treated as extension of primary entity.

How to set foreign key where column names are different using Entity Framework 5 Fluent API?

I am trying to establish a foreign key relationship between following domain classes using Fluent API (Entity Framework v5):
public partial class User
{
public long UserID { get; set; }
public string UserName { get; set; }
}
public partial class AccountGroup : BaseEntity
{
public long AccountGroupID { get; set; }
public string Name { get; set; }
public long ModifiedBy { get; set; }
public virtual User User { get; set; }
}
Fluent API
builder.Entity<User>().HasKey(p => p.UserID); //Set User Id as primary key
builder.Entity<AccountGroup>().HasKey(x => x.AccountGroupID); //SetAccountGroupId as PK
I am not sure how to set a relationship between User.UserId and AccountGroup.ModifiedBy column using fluent API. I can do this by Data Annotation but I am looking for a solution using fluent api
Remove the ModifiedBy property from your entity:
public partial class AccountGroup
{
public long AccountGroupID { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
}
And then map the foreign key like so:
builder.Entity<AccountGroup>()
.HasRequired(x => x.User)
.WithMany()
.Map(m => m.MapKey("ModifiedBy"));
Use HasOptional instead if the foreign key should be nullable:
builder.Entity<AccountGroup>()
.HasOptional(x => x.User)
.WithMany()
.Map(m => m.MapKey("ModifiedBy"));
You also don't need to specify the primary keys like you are. Entity Framework will discover those by convention.

EF5 one-to-many relationship results in incorrect foreign key column name

I'm trying to configure a one-to-many relationship with navigation only on the "many" side, but can't seem to get EF to generate the right column name.
Here's the relevant schema:
QuoteItem
Id INT PRIMARY KEY
QuoteItemFiles
Id INT PRIMARY KEY
QuoteItemId INT NOT NULL
FileId INT NOT NULL
Files
FileID INT PRIMARY KEY (legacy naming convention)
And the model:
public class QuoteItem
{
public int Id { get; set; }
public ICollection<QuoteItemFile> Attachments { get; set; }
}
public class QuoteItemFile
{
public int Id { get; set; }
public File File { get; set; }
public QuoteItem QuoteItem { get; set; }
}
public class File
{
public int FileID { get; set; }
}
I'm configuring the relationship like this:
public class QuoteItemMap()
{
HasMany(x => x.Attachments)
.WithRequired(x => x.QuoteItem)
.Map(x => x.MapKey("QuoteItemId"))
.WillCascadeOnDelete(true);
}
public QuoteItemFileMap()
{
HasRequired(x => x.QuoteItem)
.WithMany(x => x.Attachments)
.Map(x => x.MapKey("QuoteItemId"))
.WillCascadeOnDelete(true);
HasRequired(x => x.File)
.WithMany()
.Map(x => x.MapKey("FileId")) <-- This isn't working
.WillCascadeOnDelete(true);
}
When I try to access QuoteItem.Attachments, I get "Invalid column name 'File_FileID'."
Why isn't it obeying my MapKey("FileId") instruction?

Categories