I have a model that looks like this:
public class Category
{
public string Id { get; set; }
public string Description { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; }
public ICollection<Product> Products { get; set; }
}
With a database table that looks like
Categories
Id (PK varchar(5))
Description (nvarchar(50))
ParentId (FK varchar(5))
But Im stumped when it comes to setting up the mapping
modelBuilder.Entity<Category>()
.HasMany(x => x.Children)
.WithMany(x => x.Children)
.Map(m =>
{
m.ToTable("Categories");
m.MapLeftKey(x => x.Id, "Id");
m.MapRightKey(x => x.Id, "ParentId");
});
I can see why the mapping fails (StackOverflowException), but am unsure as to how to fix it. Any help would be greately appreciated.
This is using the latest release of EF (4.1?).
Thanks!
Why do you map many-to-many relation on the same navigation property? That is completely wrong. First your table obviously expect one-to-many relation. Even if you need many-to-many relation you cannot use the same navigation property for that.
Just try:
modelBuilder.Entity<Category>()
.HasMany(x => x.Children)
.WithOptional(y => y.Parent)
.Map(m => m.MapKey("ParentId"));
Related
I need to create this db context:
public class Contact
{
public int ID { get; set; }
public virtual List<Contact> Employers { get; set; }
public virtual List<Contact> Staff { get; set; }
}
EF creates the table ContactContacs with columns Contact_ID and Contact_ID1. How (where) can I rename this context columns?
In your DbContext's OnModelCreating() method:
modelBuilder.Entity<Contact>().HasMany(x => x.Employers).WithMany().Map(x =>
{
x.ToTable("ContactEmployers");
x.MapLeftKey("ContactId");
x.MapRightKey("EmployerId");
});
modelBuilder.Entity<Contact>() => The model you're configuring
HasMany(x => x.Employers) => Indicates it's a many-to-X relationship with a navigation property
WithMany() => Configures it as a many-to-many relationship without a navigation property on the other side
Map() => Configuring the table and columns for the IDs
I have something like this:
public class Account
{
[Key]
public Guid AccountId { get; set; }
}
public class TransactionHistory
{
[Key]
public int Id { get; set; }
public Account Sender { get; set; }
public Account Receiver { get; set; }
}
My modelbuilder looks like this:
modelBuilder.Entity<TransactionHistory>()
.HasRequired(history => history.Sender)
.WithMany()
.Map(s => s.MapKey("Sender"))
.WillCascadeOnDelete(false);
modelBuilder.Entity<TransactionHistory>()
.HasRequired(history => history.Receiver)
.WithMany()
.Map(s => s.MapKey("Receiver"))
.WillCascadeOnDelete(false);
And in my database, my TransactionHistory table got three columns: sender, receiver, and Account_AccountId. While first two columns are ok, i don't want that third column and i don't know why CodeFirst created it... Can you help me with this?
A collection like ICollection<TransactionHistory> in Account will lead to a third relationship with its own foreign key in Account. That's the additional key you are seeing. If you don't want a third relationship you must decide which navigation property in Account you want to relate the collection to. Either...
modelBuilder.Entity<TransactionHistory>()
.HasRequired(history => history.Sender)
.WithMany(account => account.TransactionHistories)
.Map(s => s.MapKey("Sender"))
.WillCascadeOnDelete(false);
...or...
modelBuilder.Entity<TransactionHistory>()
.HasRequired(history => history.Receiver)
.WithMany(account => account.TransactionHistories)
.Map(s => s.MapKey("Receiver"))
.WillCascadeOnDelete(false);
But not both. Or alternatively remove the collection property and the third FK will disappear.
I think doing this will fix it.
[Column("ID")]
[Key]
public int Id { get; set; }
Also I would consider changing your mappings to s.MapKey("SenderAccountID") and s.MapKey("ReceiverAccountID"). Makes life easier for people working in your DB.
I need to map a many-to-many relationship using Entity Framework Code First. Its a standard socialnetworking FriendRequests mapping. A User Object has a Collection of FriendRequests of type List<User>. In the database I'm using a join table for the relationship as follows:
CREATE TABLE dbo.FriendRequests(
UserId INT NOT NULL FOREIGN KEY REFERENCES dbo.Users(id),
FriendId INT NOT NULL FOREIGN KEY REFERENCES dbo.Users(id),
RequestDate SMALLDATETIME NOT NULL DEFAULT GETUTCDATE())
GO
ALTER TABLE dbo.FriendRequests ADD PRIMARY KEY (UserId,FriendId)
GO
How do I map the user object in Entity Framework Code First to enable a Collection via a join table?
You can try it this way:
public class User
{
public int Id { get; set; }
public ICollection<FriendRequest> FriendRequests { get; set; }
}
public class FriendRequest
{
public int UserId { get; set; }
public int FriendId { get; set; }
public DateTime RequestDate { get; set; }
public User User { get; set; }
public User Friend { get; set; }
}
Mapping with Fluent API:
modelBuilder.Entity<User>()
.HasMany(u => u.FriendRequests)
.WithRequired(f => f.User)
.HasForeignKey(f => f.UserId);
modelBuilder.Entity<FriendRequest>()
.HasKey(f => new { f.UserId, f.FriendId });
modelBuilder.Entity<FriendRequest>()
.HasRequired(f => f.Friend)
.WithMany()
.HasForeignKey(f => f.FriendId);
Because of the RequestDate property in the link table you cannot map this as a many-to-many relationship. Instead you need two one-to-many relationships as shown above.
Possibly you need to disable cascading delete, I am not sure. You can do this by appending .WillCascadeOnDelete(false) at the end of the two one-to-many mappings.
Edit
To your comment: If you remove the RequestDate column you can create your model with a many-to-many relationship. You don't need the FriendRequest entity in this case:
public class User
{
public int Id { get; set; }
public ICollection<User> FriendRequests { get; set; }
}
Mapping with Fluent API:
modelBuilder.Entity<User>()
.HasMany(u => u.FriendRequests)
.WithMany()
.Map(m =>
{
m.ToTable("FriendRequests");
m.MapLeftKey("UserId");
m.MapRightKey("FriendId");
});
I'm trying to apply a filter (ApplyFilter) on a column that is Join (and projected) from another table. I have the following entity:
public class User
{
public virtual int Id { get; private set; }
public virtual string EMail { get; set; }
...
public virtual bool IsActive { get; set; }
public virtual int CompanyId { get; set; }
}
With a UserMap:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("Users");
Id(x => x.Id, "UserId");
Map(x => x.EMail);
...
Join("CompanyUser", r =>
{
r.KeyColumn("UserId");
r.Map(x => x.IsActive);
r.Map(x => x.CompanyId);
r.Fetch.Join();
});
ApplyFilter<CompanyFilter>("this_1_.CompanyId = :companyId");
}
This actually works right now, but as you can see I'm having to include the alias name "this_1_" for the CompanyUser table in the filter... this doesn't sound correct, but if I leave the filter as is defined in the FilterDefinition class I get a Sql with a:
where this.CompanyId = ?p0
which is not mapped 'cos the CompanyId column comes from a different projection (CompanyUser as this_1_)
Is there a way to correct this and let nhibernate figure out the correct alias for the filter?
Thanks in advance for any help.
I think you must apply the filter on the join:
Join("CompanyUser", r =>
{
r.KeyColumn("UserId");
r.Map(x => x.IsActive);
r.Map(x => x.CompanyId);
r.Fetch.Join();
}).ApplyFilter<CompanyFilter>("CompanyId = :companyId");
I'm using the CTP 5 of EF 4 and Code first.
I don't get a many-many relation working with a composite key on one side.
modelBuilder.Entity<Item>()
.HasMany(i => i.Categories)
.WithMany(o => o.Items)
.Map(
mc =>
{
mc.ToTable("ItemCategories");
mc.MapLeftKey(i => i.Id, "ItemId");
mc.MapRightKey(o => o.TemplateID, "TemplateId");
mc.MapRightKey(o => o.ItemId, "ItemId");
}
);
So instead of having a simple key for Categories in my matching table, I've got a composite one. And one part of the composite key is also the key for the Item type,
which seems to be the problem here.
I get the error: "Each property name in a type must be unique. Property name 'ItemId' was already defined."
How can I configure EF to use a composite key in this case?
Of course you cannot have 2 columns with the same name within one table. This will work:
modelBuilder.Entity<Item>()
.HasMany(i => i.Categories)
.WithMany(c => c.Items)
.Map(m =>
{
m.MapRightKey(i => i.Id, "ItemId");
m.MapLeftKey(c => c.ItemId, "ItemId2");
m.MapLeftKey(c => c.TemplateId, "TemplateId");
});
public class Category
{
[Key]
public string ItemId { get; set; }
[Key]
public string TemplateId { get; set; }
public string Value { get; set; }
public ICollection<Item> Items { get; set; }
}
public class Item
{
public string Id { get; set; }
public string Name { get; set; }
public ICollection<Category> Categories { get; set; }
}
The mapping table ItemCategories is not a POCO, but used for mapping those 2 as
shown.
It has SQL columns
Id (own primary key)
ItemId (FK to Item table and Category table)
TemplateId (FK to Category table)
and another ID column which maps to a different table.
In my opinion the only difference here to "normal" many-many scenario is the composite key
in the ItemCategories table, which builds the relation to the Category table.