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

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);
...

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.

Entity Framework 6 code-first relations with Fluent API

I have 3 related tables in an existing database.
Many-to-Many (Many Person Many Group) and a table between - Event, witch ralate these two tables.
public class Person
{
public int PersonID { get; set; }
public string No { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
public class Event
{
public int EventID { get; set; }
public string EventData { get; set; }
public int PersonID { get; set; }
public int GroupID { get; set; }
public virtual Person Person { get; set; }
public virtual Group Group { get; set; }
}
public class Group
{
public int GroupID { get; set; }
public string GroupName { get; set; }
public virtual ICollection<Event> PersonGroup { get; set; }
}
I ddescribed relations using Fluent API.
First I declared PK's
modelBuilder.Entity<Person>()
.HasKey(k => k.PersonID });
modelBuilder.Entity<Event>()
.HasKey(k => k.EventID);
modelBuilder.Entity<Group>()
.HasKey(k => k.GroupID });
Now foreign keys:
Person has many Events
modelBuilder.Entity<Person>()
.HasKey(k => k.PersonID })
.HasMany(k => k.Events)
.WithRequired()
.HasForeignKey(f => f.PersonID);
In Event class I have Person (and all its parameters)
modelBuilder.Entity<Event>()
.HasRequired(s => s.Person)
.WithMany()
.HasForeignKey(fk => fk.PersonID);
Also I need a Group with all data:
modelBuilder.Entity<Event>()
.HasOptional(s => s.Group)
.WithMany()
.HasForeignKey(fk => fk.GroupID });
At least I need a Person group participating in an Event
modelBuilder.Entity<Group>()
.HasKey(k =>k.GroupID })
.HasMany(k => k.PersonGroup)
.WithOptional()
.HasForeignKey(fk => fk.GroupID);
It seems I have everything I need, but I need one mo collection (Group of persons with their names)
What I get through PersonGroup relation I have all events but also need to get Persons. Could You help?
Edit
I just realize it's not a typical many-to-many, since your Event-Group relation is optional. Is this intended?
Maybe your model should be changed to reflect a more natural event structure:
Group-Person: many-to-many
Event-Group: many-to-many
Event-Person: no direct relation, only via groups; alternative many-to-many
The way your model is currently designed, a single event entry can't be related to more than one group and one person and a person can't be part of a group unless they are associated in the context of an event entry.
Basically, the thing you ask for is not directly available, because you decided to explicitely create the many-to-many table with additional properties.
However, in queries you can always write a select to get the persons collection
db.Groups.Select(g => new {Group = g, PeopleInGroup = g.PersonGroup.Select(ev => ev.Person)})
Few side-notes regarding your model:
Consider removing EventID and instead use modelBuilder.Entity<Event>().HasKey(k => new { k.PersonID, k.GroupID }), like a typical many-to-many table would be designed.
Mention reverse properties in fluent api:
.
modelBuilder.Entity<Person>()
.HasKey(k => k.PersonID })
.HasMany(k => k.Events)
.WithRequired(e => e.Person)
.HasForeignKey(f => f.PersonID);
// redundant with the previous configuration
modelBuilder.Entity<Event>()
.HasRequired(s => s.Person)
.WithMany(p => p.Events)
.HasForeignKey(fk => fk.PersonID);
// same to be done for groups
In order to have a convenient access to the associated people of a group, you could create a not-mapped property getter which wraps the necessary query:
public class Group
{
public int GroupID { get; set; }
public string GroupName { get; set; }
public virtual ICollection<Event> PersonGroup { get; set; }
[NotMapped]
public IEnumerable<Person> PersonsInGroupEvents
{
return PersonGroup.Select(ev => ev.Person);
}
}
// or fluent api way of NotMapped:
modelBuilder.Entity<Group>()
.Ignore(x => x.PersonsInGroupEvents);

One-to-many using EF5 fluent API

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);

NHibernate 3.3: Mapping many to many relationship with composite foreign keys

I am trying to figure out how to map the following situation using NHibernate's "sexy" mapping by code system.
Please help as I have been trying to figure this out for a while with no luck! I am using components to represent the composite keys. Below are the tables I am trying to map.
Account
-------
BSB (PK)
AccountNumber (PK)
Name
AccountCard
-----------
BSB (PK, FK)
AccountNumber (PK, FK)
CardNumber (PK, FK)
Card
------------
CardNumber (PK)
Status
Here is my current attempt (which is not working at all!)
Account:
public class Account
{
public virtual AccountKey Key { get; set; }
public virtual float Amount { get; set; }
public ICollection<Card> Cards { get; set; }
}
public class AccountKey
{
public virtual int BSB { get; set; }
public virtual int AccountNumber { get; set; }
//Equality members omitted
}
public class AccountMapping : ClassMapping<Account>
{
public AccountMapping()
{
Table("Accounts");
ComponentAsId(x => x.Key, map =>
{
map.Property(p => p.BSB);
map.Property(p => p.AccountNumber);
});
Property(x => x.Amount);
Bag(x => x.Cards, collectionMapping =>
{
collectionMapping.Table("AccountCard");
collectionMapping.Cascade(Cascade.None);
//How do I map the composite key here?
collectionMapping.Key(???);
},
map => map.ManyToMany(p => p.Column("CardId")));
}
}
Card:
public class Card
{
public virtual CardKey Key { get; set; }
public virtual string Status{ get; set; }
public ICollection<Account> Accounts { get; set; }
}
public class CardKey
{
public virtual int CardId { get; set; }
//Equality members omitted
}
public class CardMapping : ClassMapping<Card>
{
public CardMapping ()
{
Table("Cards");
ComponentAsId(x => x.Key, map =>
{
map.Property(p => p.CardId);
});
Property(x => x.Status);
Bag(x => x.Accounts, collectionMapping =>
{
collectionMapping.Table("AccountCard");
collectionMapping.Cascade(Cascade.None);
collectionMapping.Key(k => k.Column("CardId"));
},
//How do I map the composite key here?
map => map.ManyToMany(p => p.Column(???)));
}
}
Please tell me this is possible!
You were pretty close.
The IKeyMapper that you get in the Action parameter of both the Key and the ManyToMany methods has a Columns method that takes as many parameters as you want, so:
collectionMapping.Key(km => km.Columns(cm => cm.Name("BSB"),
cm => cm.Name("AccountNumber")));
//...
map => map.ManyToMany(p => p.Columns(cm => cm.Name("BSB"),
cm => cm.Name("AccountNumber"))));

many to many mapping with intermediate object in entity framework code first

I have a many to many relation, and I want to add an intermediate class, which would enable me adding the many to many relations using repository pattern.
What I can't figure out is the mapping.
Here's the structure
public class Product
{
public Product()
{
Categories = new HashSet<Category>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Category> Categories { get; set; }
}
public class Category
{
public int Id { get; set; }
public String Name { get; set; }
public ICollection<Product> Products { get; set; }
}
public class PCMap
{
public int product_id { get; set; }
public int category_id { get; set; }
public Product Product { get; set; }
public Category Category { get; set; }
}
And the Mapping
modelBuilder.Entity<Product>()
.HasEntitySetName("PCMap")
.HasMany(p=>p.Categories)
.WithMany(p=>p.Products)
.Map(m=>
{
m.MapLeftKey("product_id");
m.MapRightKey("category_id");
m.ToTable("PCMap");
});
modelBuilder.Entity<PCMap>()
.ToTable("PCMap");
modelBuilder.Entity<PCMap>().HasKey(k => new
{
k.category_id,
k.product_id
});
modelBuilder.Entity<PCMap>()
.HasRequired(p=>p.Product)
.WithMany()
.HasForeignKey(p => p.product_id);
modelBuilder.Entity<PCMap>()
.HasRequired(p => p.Category)
.WithMany()
.HasForeignKey(p => p.category_id);
Here's the error that I get..
How do I fix this ?
The way you've set it up. PCMap is a non entity and is just used to facilitate the M:N join under the hood.
Product p = new Product();
p.Categories ...
Category c = new Category();
c.Products ...
So beacuse you've already defined PC as part of the Product Entity definition here.
.Map(m=>
{
m.MapLeftKey("product_id");
m.MapRightKey("category_id");
m.ToTable("PCMap");
});
I don't believe you need to (or it's possible to) define it again, separately below. Try deleting all this code.
modelBuilder.Entity<PCMap>()
.ToTable("PCMap");
modelBuilder.Entity<PCMap>().HasKey(k => new
{
k.category_id,
k.product_id
});
modelBuilder.Entity<PCMap>()
.HasRequired(p=>p.Product)
.WithMany()
.HasForeignKey(p => p.product_id);
modelBuilder.Entity<PCMap>()
.HasRequired(p => p.Category)
.WithMany()
.HasForeignKey(p => p.category_id);

Categories