How to seed new junction table - c#

I am using Code first in Entity framework. There are two tables in my database - Clients and Products. There is some data in it. I have added a new, junction table that has foreign keys to both of them. How should I seed that table? And will Entity framework add new rows when I add new Client or Product, because it seems that it doesn't.:
public class UserPriceList
{
public UserPriceList()
{
PriceFactor = 1M;
}
[Key]
public int UserPriceListId { get; set; }
[Index("IX_UserPrice", 1)]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
[Index("IX_UserPrice", 2)]
public int ClientId { get; set; }
public virtual Client Client { get; set; }
public decimal PriceFactor { get; set; }
}

Your UserPriceList looks a lot like a junction table, but EntityFramework is not viewing it that way because you defined it as an entity with additional properties. A junction table is automatically created behind the scenes as a table with ProductId and ClientId by adding an ICollection to the Client and ICollection to the product. There is no defined model, you would interact with it by Client.Products.Add(someProduct) and it would populate the junction table behind the scenes.
To get your UserPriceList working as a junction table you could try something like this in your OnModelCreating
modelBuilder.Entity<Product>()
.HasMany(x => x.Clients)
.WithMany(x => x.Products)
.Map(x =>
{
x.ToTable("UserPriceList"); // third table is named Cookbooks
x.MapLeftKey("ProductId");
x.MapRightKey("ClientId");
});
to explicitly map UserPriceList as the junction table. You may have problems with the non nullable decimal (or maybe not since you're setting a value in the constructor) and with having the UserPriceList defined as an entity.
Your could also just interact with the UserProductList as an entity and explicitly add items to it.
Probably the safest (as in most likely to work) way to go would be to remove the ICollection<Product> from Client and the ICollection<Client> from product add an ICollection<UserPriceList> to both.

Related

Mapping unconventional one-to-many relationship in EntityFramework

I'm trying to code my object relationships with my database using EntityFramework (EFCore 5). My database has an unconventional many-to-many relationship. We have a Store table which has an integer ID primary key and related fields (name, et cetera), Customer, which has an ID primary key, a name, and other related fields.
Our StoreCustomer join table schema is the odd part. The join table has StoreID which points to a single Store, but instead of CustomerID, it is 'Customer Name'. It's not a one-to one relationship from StoreCustomer to Customer, because multiple Customers can have the same name. This is intended by the schema. Thus StoreCustomer to Store is a one-to-many relationship. My classes are laid out like so:
public partial class StoreCustomer
{
[Column("Store ID")]
public long StoreId { get; set; }
[Column("Customer Name")]
public string CustomerName { get; set; } = null!;
[Column]
public long Order { get; set; }
[ForeignKey(nameof(CustomerName))]
public ICollection<Customer> Customers { get; set; } = null!;
}
public partial class Customer
{
[Key]
[Column("Customer ID")]
public long CustomerId { get; set; }
[Column]
public string Name { get; set; } = null!;
}
And my OnModelCreating looks like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<StoreCustomer>(entity =>
{
entity.HasKey(entity => new { entity.StoreId, entity.CustomerName, entity.Order });
entity.HasMany(sc => sc.Customers)
.WithOne()
.HasPrincipalKey(sc => sc.CustomerName);
});
modelBuilder.Entity<Customer>(entity =>
{
entity.Property(e => e.CustomerId).ValueGeneratedNever();
});
OnModelCreatingPartial(modelBuilder);
}
The issue is, when I call 'context.StoreCustomers.Include(sc => sc.Customers);', the resulting query has an extra column "Customer"."CustomerName" which of course causes an exception. It's like EFCore is creating a new ghost property for this column and I don't understand why. Why is it doing this? Also, in the future when weird stuff like this happens, are there ways to debug EF to catch it in the act so I understand why it's doing what it's doing?
EDIT: Sorry, I almost forgot to mention - I am using MS Access, so I am using the unofficial EntityFrameworkCore.Jet libraries to access it. I'm not sure if that has anything to do with it, but it is the reason I am using EFCore 5, because the library doesn't support 6 yet.

Entity Framework : Invalid column name *_ID even with fluent api or data annotations

Odd issue that I've been looking at all day. I am working with Entity Framework 6. The issue I have is that I have three entities:
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
// other properties and relationships here
public int? OrderProfileId { get; set; }
public int OrderTemplateId { get; set; }
public virtual OrderProfile Profile { get; set; } // optional property
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderProfile
{
public int Id { get; set; }
// other properties
// added here 6/15/2021
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderTemplate : EntityMetaData
{
public int Id { get; set; }
// other properties
public int? OrderProfileId{ get; set; }
public OrderProfile OrderProfile { get; set; }
}
In our model builder, we have these definitions:
modelBuilder.Entity<Order>()
.HasOptional(x => x.OrderProfile)
.WithMany(x => x.Orders)
.HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
But even with the above fluent api model, we get the error
Invalid column name 'OrderProfile_Id'
Throughout various testing I was unable to find why this issue was occurring, so I looked at our logs and found when this error started popping it's head up and then was able to find the changes associated to OrderProfile and found that the only change that was made was adding the relationship from OrderProfile to OrderTemplate.
When I removed that fluent api relationship OrderProfile to OrderTemplate, it worked as expected... I don't need that relationship to OrderTemplate, but would like it to be there, how can I establish a optional 1 to optional 1 relationship without breaking other relationships? Also, why would additional relationships be effected by this?
UPDATE 6/15/2021
So I found I had a reverse navigation property in the OrderProfile model:
public virtual OrderTemplate OrderTemplate{ get; set; }
removing that and the associated fluent relationship
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
Doing the above resolved the issue, but for some reason, the issue seems to have cascaded down to another relationship that has a circular reference like the above. The Order class is involved with this cascaded issue. I guess this is a pretty big cause for concern since this application worked fine for the last 4 years and for these relationships to be decaying like this is worrisome. Does anyone know why this is happening?
if you use the right naming convention, EF will do magic. in this sample, you don't need fluent API to relate entities.
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
public int? OrderProfileId { get; set; } //means HasOptional (nullable) and ForeignKey
//variable name must be OrderProfile not Profile
public virtual OrderProfile OrderProfile { get; set; }
}
public class OrderProfile
{
public OrderProfile()
{
Orders = new HashSet<Order>();
}
public int Id { get; set; }
//be aware circular reference at any conversion or mapping
public virtual ICollection<Order> Orders {get; set;} //means WithMany
}
I've got an error like this too. It's caused by unmatching OrderProfileId property in OrderTemplate class with the fluent api model
If I'm not wrong, you want the OrderProfile model a many to many relation between Order and OrderTemplate. Then if it was the case, add the nvaigation property in OrderProfile.
public class OrderProfile
{
public int Id { get; set; }
// other properties
public virtual ICollection<Order> Orders { get; set; }
public virtual OrderTemplate OrderTemplate { get; set; }
}
Then change the fluent api model to be like this
// the EF has modelled the relation for normal 1 to many relation
// modelBuilder.Entity<Order>()
// .HasOptional(x => x.OrderProfile)
// .WithMany(x => x.Orders)
// .HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderTemplate>()
.HasOptional(x => x.OrderProfile)
.WithOptional(x => x.OrderTemplate);
You're working database-first, which always leaves room for a mismatch between the actual database model and the model EF infers from class and property names and mapping code (= conceptual model). If this happens, it may help to make EF generate a database from the conceptual model and see where it creates the column it expects, OrderProfile_Id.
This is what you'll see when logging the SQL statements:
CREATE TABLE [dbo].[OrderTemplates] (
[Id] [int] NOT NULL IDENTITY,
[OrderProfileId] [int],
[OrderProfile_Id] [int],
CONSTRAINT [PK_dbo.OrderTemplates] PRIMARY KEY ([Id])
)
...
ALTER TABLE [dbo].[OrderTemplates]
ADD CONSTRAINT [FK_dbo.OrderTemplates_dbo.OrderProfiles_OrderProfile_Id]
FOREIGN KEY ([OrderProfile_Id]) REFERENCES [dbo].[OrderProfiles] ([Id])
There you see the expected nullable column OrderProfile_Id which is the FK to OrderProfiles. It's noteworthy to see that EF does not use OrderProfileId as a foreign key field. It's just a field that could be used for anything.
That's because EF6 doesn't support 1:1 associations as foreign key associations (reference property and primitive FK property).
Knowing this, the remedy is simple: remove the property OrderTemplate.OrderProfileId and tell EF to use the field OrderTemplate.OrderProfileId in the database:
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile)
.Map(m => m.MapKey("OrderProfileId"));
That said, I wonder why Order has a foreign key to OrderProfile. Isn't its OrderProfile determined by its OrderTemplate? If it's a redundant relationship it may be better to remove it.

Entity Framework : Handle multiple relationships between two entities

There are two entities:
Group
Yuvak - Person
"Other" person is designed in back-end who has no group. (null)
A Yuvak - Person will always have one HomeGroup. (1=>1) And will have no groups to control.
A Nirikshak(Head) - Person will always have one HomeGroup. (1=>1) But he will also have multiple groups to control - GroupsOfNirikshak. (1=>Many)
A Group will have multiple Yuvaks (1=>Many)
and all groups mostly will have only one Head. (Initially a new group might not have any head but zero or more yuvaks-persons.)
[Table("Group")]
public class Group
{
[Key]
public int Id { get; set; }
.....
public virtual List<Yuvak> Yuvaks { get; set; }
[ForeignKey("Nirikshak")]
public int? NirikshakId { get; set; }
public virtual Yuvak Nirikshak { get; set; }
}
[Table("Yuvak")]
public class Yuvak
{
[Key]
public int Id { get; set; }
.....
[ForeignKey("HomeGroup")]
public int? HomeGroupId { get; set; }
public virtual Group HomeGroup { get; set; }
public virtual List<Group> GroupsOfNirikshak { get; set; }
}
I already has provided two foreign keys for 1=>1 relationships (nullable) in both entities.
And now to manage many to many relationship it should automatically create a third table with "Yuvak_Id" and "Group_Id" columns if they are not null. But as here the FKs are null; instead of creating a third table it adds a foreign key column in both the tables.(Group:"Yuvak_Id" and Yuvak:"Group_Id")
What should I do so that to maintain Yuvak-HomeGroup it should use above provided foreign keys only and for many to many relationship (Head-GroupsOfNirikshak & Group-Yuvaks )it should create a seperate table.
Can I create a separate table for many to many relationship like : (ID,YuvakID,GroupID) How can I do that?
I tried to do that but got different errors like below :
The navigation property 'HomeGroup' declared on type
'YTKGoshthiManagment.Models.Yuvak' has been configured with
conflicting foreign keys.
Multiplicity is not valid in Role 'Yuvak_HomeGroup_Target' in
relationship 'Yuvak_HomeGroup'. Because the Dependent Role
properties are not the key properties, the upper bound of the
multiplicity of the Dependent Role must be '*'.
.....
and so on.
Use the "Fluent Api" !
In your context class write (for example) :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Group>()
.HasOptional(t => t.Nirikshak)
.WithMany(t => t.GroupsOfNirikshak)
.HasForeignKey(t => t.NirikshakId);
}
You can remove the annotations on the classes and properties. Once you have a Model-based Class on another, Entity Framework will automatically create a foreign key relationship on it. It will process Yuvak as a node on the Group Graph object. You need not declare the annotations since EF will do that for you automatically.

Mapping a Many-to-Many relationship with an Attribute in Entity Framework

I'm always using Attributes to map the properties of my entities to their corresponding columns. Here's an example:
[Table("news_entries")]
public class News
{
[Key]
public int Id { get; set; }
[Column("d_date")]
public DateTime Date { get; set; }
[Column("m_text")]
public string Text { get; set; }
[Column("id_user")]
public int UserId { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
}
But I still don't know, how I could map a Many-to-Many relationship, where the table and column names doesn't match with the properties.
I know I could use the DbModelBuilder in my DbContext, but I don't want to do the mapping outside of my entity class. Is it somehow possible to map those relationships with Attributes as well?
I know I could use the DbModelBuilder in my DbContext, but I don't
want to do the mapping outside of my entity class. Is it somehow
possible to map those relationships with Attributes as well?
No. There is no class for the junction table where you could apply your attributes. Once you use direct many to many mapping (where junction table is hidden behind navigation properties) you need to use fluent API.
Perhaps EF6 will allow this through custom conventions but my initial experience with handling foreign key names with convention wasn't successful so I guess junction table will be the same story.

How to make EF map NavigationProperty on one entity to Collection of the other

I want a straightforward foreign key relationship between two entities, with EF properties going both ways. However, at the moment EF is generating 2 foreign keys representing each direction of the relationship. How can I make EF treat them as a single property? The below generates two database foreign key constraints named Script_ScriptRuns and ScriptRun_Script, using foreign key field Script_Id and Script_Id1 respectively
public class Script
{
[Key]
public Guid Id { get; set; }
public virtual ICollection<ScriptRun> ScriptRuns { get; set; }
}
public class ScriptRun
{
[Key]
public Guid Id { get; set; }
[Required]
public virtual Script Script { get; set; }
}
Using the code you posted, it only created a single FK for me - Script_Id in the ScriptRun class.
Are you simply wanting to rename the FK that's getting created in that class? You can do that by mapping the column in your DbContext. This would map it to ScriptId:
modelBuilder.Entity<Script>()
.HasMany(s => s.ScriptRuns)
.WithRequired(sc => sc.Script)
.Map(x => x.MapKey("ScriptId"));
If not, can you post your entire model?

Categories

Resources