Entity Framework is appending "1" to table name and query is failing - entity-framework

We have an e-commerce site that uses Web Api and Entity Framework. I recently had created an ApiController for an entity called "BundleProduct". When I call the GET method on the controller, I get a System.Data.SqlClient.SqlException: "Invalid object name 'dbo.BundleProducts1'."
Using the VS debugger, this is the query that was being executed (there are only two columns in this table, with together form a primary key):
SELECT
[Extent1].[Fk_BundleID] AS [Fk_BundleID],
[Extent1].[Fk_ProductID] AS [Fk_ProductID]
FROM [dbo].[BundleProducts1] AS [Extent1]
There is no "BundleProducts1" table, it should be called "BundleProducts". I did a search in the code and cannot find any instances where the name "BundleProducts1" is used.
The BundleProducts table represents a many-to-many relationship between a "Bundles" table and "Products" table. This particular table has only two columns and both together are the primary key. I did look at the DbContext class and the only references it has to BundleProducts are:
public DbSet<BundleProduct> BundleProducts { get; set; }
modelBuilder.Entity<Bundle>()
.HasMany(e => e.Products)
.WithMany(e => e.Bundles)
.Map(m => m.ToTable("BundleProducts")
.MapLeftKey("Fk_BundleID")
.MapRightKey("Fk_ProductID"));
Why is EF appending a "1" to the table name and what can I do to change this?

When you use the many-to-many mapping (HasMany - WithMany), EF will use a hidden association class named BundleProducts.
The problem is that you also added a visible class BundleProducts. EF tries to do what you instructed it to do: map both classes to a table and then it encounters a name conflict. The visible class is victimized and is renamed.
You either have to remove the visible class from your model, or transform the many-to-many mapping into two one-to-many mappings with BundleProducts in the middle: Bundle 1 - n BundleProduct n - Product.

Related

To add navigation property without foreign key in EF Core, DB-first migration with .NET Core Web API

I am working with an existing system and updating it to .NET Core, Web API and EF Core.
The existing system has 2 tables:
Parent table: Id, name, etc..
Child table: Id, ParentId, name, etc..
Though ParentId exists in the child table, there is no foreign key reference, but I want to be able to use include when I query the parent. I have asked not to add FK as part of deleting they are putting -ve values to parentId column. This way they can bring it back and a legacy system was built that way.
Now, in db-first migration how can I specify a navigation property without fk so my EF Core to act relational; or at least return them together. Adding nullable foreign key is not an option as it will break the system when -ve values are added.
I do have suggested for full cleanup of DB and getting rid of -ve values but that involves lots of testing and no deliverable. So long story short how to add navigation property without foreign key in database first migration ?
I tried adding collection and virtual entry in the model, but after migration it got overwritten. I have added this by using HasMany on modelbuilder as per this document - https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key
But scaffolding is overriding my navigation property
I found out the answer for this.
In EF core 3.x the dbcontext created by DBFrist scaffolding is all partial classes.
So I did the following:
1. new partial class for context class - here i added the relationship of navigation property using OnModelCreatingPartial() method. Example below
public partial class dbContext : DbContext
{
partial void OnModelCreatingPartial(ModelBuilder builder)
{
builder.Entity<Packcomponent>()
.HasOne(p => p.Pack)
.WithMany(b => b.PackComponent)
.HasForeignKey(p => p.PackId);
}
}
extended the partial class in a new file and added navigation property there.
public partial class Packcomponent
{
public Pack Pack { get; set; }
}
public partial class Pack
{
public List PackComponent { get; set; }
}
This way upon scaffolding it did not overwrite custom navigation properties and I also could use this properties to do EF operations like .Include() and to save related entities as well. It is pretty awesome!!

How can I correctly design relation of few different entities (one-many) that relate to one common?

I have few entities that related to each other (just a sample):
------User----------
->(One) Type
->(One) Company
- Name
------Type----------
->(Many) User
- Name
------Job------------
->(Many) User
->(Many) Type
- Name
------Company-----
->(Many) Users
->(Many) Job
- Name
(Name it's just a text.)
And I want to have many Names (aliases) for each row of entities and store it in one other table.
I have create new entity Name and configure EF next:
modelBuilder.Entity<User>().HasMany(e => et.Names);
modelBuilder.Entity<Type>().HasMany(e => et.Names);
modelBuilder.Entity<Job>().HasMany(e => et.Names);
modelBuilder.Entity<Company>().HasMany(e => et.Names);
It creates one table that looks loke this:
[Id],
[Name],
[User_Id],
[Type_Id],
[Job_Id],
[Company_Id]
(Can I awoid many _Ids any how?)
And when I try to set cascade deleting it throw me exception ".may cause cycles or multiple cascade paths".
modelBuilder.Entity<User>()
.HasMany(pt => pt.Names)
.WithOptional()
.WillCascadeOnDelete(true);
What is the best way to do this using entity framework?
A better solution may be to have a distinct "Name" Entity for each entity that requires many Names. It would result in a true one-to-many mapping at the database level, but require a separate table for each. It should also prevent the cascading issue.

Entity Framework: removing entity when One-to-One PK association is specified

I have existing DB with the following structure:
I'm using EF fluent API to configure relationships between tables:
public GroupEntityConfiguration()
{
HasMany(x => x.Employees).WithRequired().HasForeignKey(x => x.GroupId).WillCascadeOnDelete(true);
}
public EmployeeEntityConfiguration()
{
HasOptional(x => x.InnerGroupMember).WithRequired();
}
With this configuration applied I can add new Employee, new InnerGroupMember or fetch data. The problem appears when I try to remove Employee. Then I get an exception:
The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
As far as I understand above exception is connected with GroupId foreign key. Trying to fix it I'm adding following line to EmployeeEntityConfiguration:
HasKey(x => new { x.Id, x.GroupId});
But after adding it I get another exception which I believe is connected with InnerGroupMember object:
Invalid column name 'Guest_Id'. Invalid column name 'Guest_GroupId'.
If I comment out InnerGroupMember navigation property and remove it's configuration, Employee can be removed.
Could you please give me a hint what I'm doing wrong and how to configure entities to be able to perform all needed operations? Thanks!
I have an existing Group entity and I want to remove Employee from the Employees Group collection:
var group = groupRepository.Find(groupId);
group.RemoveEmployee(employeeId);
_unitOfWork.Save();
RemoveEmployee function inside Group entity looks like this:
public void RemoveEmployee(int employeeId)
{
var employee = Employees.Single(n => n.Id == employeeId);
Employees.Remove(employee);
}
That's why I get an exeption:
The relationship could not be changed because one or more of the foreign-key properties is non-nullable....
After reading this post I wanted to fix it adding HasKey(x => new { x.Id, x.GroupId}); function inside EmployeeEntityConfiguration what leads to the second exception:
Invalid column name 'Guest_Id'. Invalid column name 'Guest_GroupId'.
Actually I made this step (I mean adding HasKey function) without changing DB structure. To make it work, inside Employees table I have to create composite key - combination of Id and GroupId which is also a foreign key. This modification forces changes inside InnerGroupMembers table. DB structure looks now as following:
Now I'm able to remove Employee in a way I showed at the beginning.
Anyway I'm not going for this solution. They are different ways to achieve what I want. Here are some links:
Removing entity from a Related Collection
Delete Dependent Entities When Removed From EF Collection
The relationship could not be changed because one or more of the
foreign-key properties is non-nullable
For one-to-one relationships cascading delete is not enabled by default, even not for required relationships (as it is the case for required one-to-many relationships, that is: The WillCascadeOnDelete(true) in your one-to-many mapping is redundant). You must define cascading delete for a one-to-one relationship always explicitly:
HasOptional(x => x.InnerGroupMember).WithRequired().WillCascadeOnDelete(true);
When you delete an Employee now, the database will delete the related InnerGroupMember as well and the exception should disappear.

Entity framework Association in conceptual model for one to many relationship

I have two entity CUSTOMER and ORDER..there is one to many relation from CUSTOMER to ORDER where CustomerID is primary key for customer and foreign key in ORDER..now I want to add customer name property from CUSTOMER entity in ORDER entity...I have copied this property and paste it in ORDER table and have added CUSTOMER table and map this property to the CUSTOMER table's same property..but when i trying to validate it vs giving me a Error that is
3024: Problem in mapping fragments starting at line 239:Must specify
mapping for all key properties (ORDER.OrderID) of the EntitySet ORDER
That is not possible in mapping. You cannot add property from Customer table into Order entity this way. Mapping properties from multiple tables to the same entity has very strict rules and it is not possible for this case.
You can expose customer's name in your Order class without defining it in the mapping. Create partial part of Order class and add custom computed property (non mapped):
public partial class Order
{
public string CustomerName
{
get
{
// Customer is navigation property to Customer entity
return Customer.Name;
}
}
}
This will require loading Customer with your Order (eager loading) or using lazy loading. Also this property cannot be used in Linq-to-entities queries.

EF CF: many-to-many relation with additional info

We have legacy database and we map the new objects and props to the old tables and columns. So far so good. We have many-to-many relation which was mapped successfully. The intermediate table contains additional data. When we try to map the intermediate table to an object we get exception that the mapping is already defined. If we remove mapping from any side of the relation we get error that table is missing (ofc, we expect just that). I can do that easily with NHibernate and I am starting to think that EF is missing really really many features. So, please, tell me I am wrong and we can do that with EF.
Best regards
EDIT: here is a dummy sample which fails.
class User
{
public ICollection<User> Followers{get;set;}
}
class UserRelation
{
public User User{get;set;}
public User Follower{get;set;}
public DateTime CreatedOn{get;set;}
}
user mapping
modelBuilder
.Entity<User>()
.HasMany<User>(user => user.Followers)
.WithMany()
.Map(m =>m.MapLeftKey("user_id").MapRightKey("follower_id")
.ToTable("user_follower"));
user relation mapping
modelBuilder
.Entity<UserRelation>()
.ToTable("user_follower");
modelBuilder
.Entity<UserRelation>()
.HasOptional<User>(f => f.User)
.WithRequired().Map(m => m.MapKey("user_id"));
modelBuilder
.Entity<UserRelation>()
.HasOptional<User>(f => f.Follower)
.WithRequired().Map(m => m.MapKey("follower_id"));
modelBuilder
.Entity<UserRelation>()
.Property(entity => entity.CreatedOn)
.HasColumnName("created_on");
Exception
Schema specified is not valid. Errors:
(67,6) : error 0019: The EntitySet 'UserUser' with schema 'dbo' and table 'user_follower' was already defined. Each EntitySet must refer to a unique schema and table.
Edit2: Here is another example of this model: http://learnentityframework.com/LearnEntityFramework/tutorials/many-to-many-relationships-in-the-entity-data-model/
Direct many-to-many mapping is available only if junction table contains just foreign keys. If you want to expose other properties in junction table you must map it to separate entity and mapt two one-to-many relations from former entities used in many-to-many.
I'm actually not able to write you the code because I don't understand your example.
Try only (don't map Many-to-many in User):
modelBuilder.Entity<UserRelation>
.HasRequired(r => r.User)
.WithMany(u => u.Followers);
modelBuilder.Entity<UserRelation>
.HasRequired(r => r.Follower)
.WithMany();
EF maps many-to-many relationships as properties of the related objects.
So, let's say you have Cars and Drivers that are related m-to-n. In your EF model, you will see that each Car object has a Drivers collection as a property, and each Driver object has a Cars collection as a property.
That is how m-to-n relationships are modeled in EF.