How do i assosiate my PICO class with a specific table in my database with Entity Framework 5 - entity-framework

I have a table in my db (a many to many table) that two classes A and B have created like this.
this.HasMany(t => t.TrafficImageQuestions)
.WithMany(t => t.TrafficImages)
.Map(m =>
{
m.ToTable("TrafficImage_Answers");
m.MapLeftKey("TrafficImagesGuid");
m.MapRightKey("TrafficImageQuestionsId");
});
Now i would like to assosiate my custom class to this same table "TrafficImage_Answers", the class offcause have the left and right key and then also a 3. custom property.
(i did add the column to the database "Answer")
public class TrafficImageAnswer
{
public System.Guid TrafficImageGuid { get; set; }
public int TrafficImageQuestionId { get; set; }
public byte Answer { get; set; }
}
I am doing this as i want entity model to keep track of my many to many relationship of A and B and still be able to look up the 3. property Answer that is in the database.
What i have tried
I tried to do the following:
this.Property(t => t.TrafficImageQuestionId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(t => t.TrafficImageGuid)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// Table & Column Mappings
this.ToTable("TrafficImage_Answers");
But i get that the table already exists, logic. I need to tell it that it just should use that table and not try to create it.
(im doing this with DB mitigrations in EF 5 and Package manager).

That is not supported. If you want to have additional field in the junction table for many-to-many relation you cannot map it as many-to-many any more. Each table can be mapped only once but mapping table to entity and to many-to-many relation in the same time makes it mapped twice.
You must change your TrafficImageQuestions and TrafficImages to use one to many relations with TrafficImageAnswer instead of many-to-many relation with each other.

Related

Table Splitting - Migration Warning

My scenario:
I have a Product that has various properties such a price, size, etc. that are declared in the Product Entity.
Additionally, a Product can have a collection of StockRequirements, i.e. when that Product is used the constituent StockItems can be depleted by the StockRequirement quantity accordingly.
Under one use case I just want the Product so that I can play with the core properties. For another use case I want the Product with its StockRequirements.
This means that when retrieving a Product I may be using it in different contexts. My chosen approach has been to use EF table splitting.
I have one repository for Products and one repository for ProductStockRequirements. They are referring to the same unique Product.
The Product repository will provide a Product Entity with the core details only.
The ProductStockRequirements repository will provide ProductStockRequirements entity which does not have the core details, but does have the list of StockRequirements.
This seemed a reasonable approach so that I am not retrieving 'owned' StockRequirements when I only want to change the price of the product. Similarly, if I'm only interested in playing with the StockRequirements then I don't retrieve the other core details.
Entities
class Product
{
public int Id { get; set; }
public string CoreProperty { get; set; }
}
class ProductStockRequirements
{
public int Id { get; set; }
public List<StockRequirement> StockRequirements { get; set; }
}
Product Mapping
b.ToTable("Products");
b.HasKey(p => p.Id);
b.Property(p => p.CoreProperty).IsRequired();
ProductStockRequirementsMapping
b.ToTable("Products");
b.HasKey(p => p.Id);
b.OwnsMany<StockRequirement>(p => StockRequirements, b =>
{
b.ToTable("StockRequirements");
b.WithOwner().HasForeignKey("ProductId");
}
b.HasOne<Product>()
.WithOne()
.HasForeignKey<ProductStockRequirements>("Id");
When running a migration, I get the warning:
The entity type 'ProductStockRequirements' is an optional dependent
using table sharing without any required non shared property that
could be used to identify whether the entity exists. If all nullable
properties contain a null value in database then an object instance
won't be created in the query. Add a required property to create
instances with null values for other properties or mark the incoming
navigation as required to always create an instance.
Focusing on the advice:
mark the incoming navigation as required to always create an instance
I have tried:
b.HasOne<Product>()
.WithOne()
.HasForeignKey<ProductStockRequirements>("Id")
.IsRequired();
and
b.HasOne<Product>()
.WithOne()
.IsRequired()
.HasForeignKey<ProductStockRequirements>("Id");
to no avail.
The warning does not appear to result in any bad behaviour. All my tests are passing. But, it seems that I should be able to create a map that removed this warning, but cannot find the way.
This should really just be
class Product
{
public int Id { get; set; }
public string CoreProperty { get; set; }
public List<StockRequirement> StockRequirements { get; set; } = new List<StockRequirement>();
}
As the StockRequiremens are not part of the Product entity, and related data isn't loaded unless you request it.
And the Entity model is simply not the correct layer to define your aggregates. An Aggregate is defined by selecting a single Entity from your entity model along with 0-few related entities. Typically you include the closely-related and weak entities together in an aggregate.
If your entity model is a graph of 23 related entities, you might organize it into 10 separate and partially-overlapping aggregates or sub-graphs.

EF Core Cascading Referential Integrity with DeleteBehavior.Restrict does not work well

I have one sql server database created with code first. There are two tables that have a one to many relationship. The database works and is created well.
In sql server if I try to delete one of the classification records, I get an error (referencial integrity restriction). This is how I want it to work. But in ef core, if I delete one classification dbset.Remove(classification), the classification is deleted and the classification in the customer is set to null.
I think this is how it should work for DeleteBehavior.ClientSetNull.
There is a note "Changes in EF Core 2.0" in https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete that explains the DeleteBehavior function.
I have the next records:
Classification:
Id Name
1 General
2 Others
Customers:
Id Name IdClassification
1 Customer A 1
2 Customer B 2
3 Customer C <null>
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
...
public int? IdClassification { get; set; }
public Classification Classification { get; set; }
}
public class Classification
{
public int Id { get; set; }
public string Name { get; set; }
...
public ICollection<Customer> Customers { get; set; }
}
public class Context : DbContext
{
public virtual DbSet<Classification> Classifications { get; set; }
public virtual DbSet<Customer> Customers { get; set; }
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Classification>(
entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<Customer>(
entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.IdClassification);
...
// Claves foráneas
entity.HasOne(c => c.Classification)
.WithMany(x => x.Customers)
.HasForeignKey(x => x.IdClassification)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_Customer_Classification");
});
}
}
Is there a way to prevent deletion of classification records in ef core? (I don't want to check if there is any customer record that is linked to the classification because I have to use the classification with more tables).
Thanks in advance.
EF Core 3.0 added several new values to the DeleteBehavior enum - ClientCascade, NoAction, ClientNoAction. Unfortunately the documentation is not updated (except for enum values in API reference), and only the ClientNoAction is mentioned in the 3.0 Breaking Changes - DeleteBehavior.Restrict has cleaner semantics:
Old behavior
Before 3.0, DeleteBehavior.Restrict created foreign keys in the database with Restrict semantics, but also changed internal fixup in a non-obvious way.
New behavior
Starting with 3.0, DeleteBehavior.Restrict ensures that foreign keys are created with Restrict semantics--that is, no cascades; throw on constraint violation--without also impacting EF internal fixup.
Why
This change was made to improve the experience for using DeleteBehavior in an intuitive manner, without unexpected side-effects.
Mitigations
The previous behavior can be restored by using DeleteBehavior.ClientNoAction.
More info is contained in the associated tracking issue - 12661: Update DeleteBehavior to be more consistent and understandable
Honestly even after reading all that, I don't find it cleaner, but even more confusing. Restrict seems to be obsoleted and replaced with NoAction, which regardless of what have been said actually does set loaded related entities navigation property/FK to null, thus causing SET NULL database behavior as you already experienced.
After trying all of them, the only option which does what you expect is the aforementioned ClientNoAction:
Note: it is unusual to use this value. Consider using ClientSetNull instead to match the behavior of EF6 with cascading deletes disabled.
For entities being tracked by the DbContext, the values of foreign key properties in dependent entities are not changed when the related principal entity is deleted. This can result in an inconsistent graph of entities where the values of foreign key properties do not match the relationships in the graph.
If the database has been created from the model using Entity Framework Migrations or the EnsureCreated() method, then the behavior in the database is to generate an error if a foreign key constraint is violated.
regardless of their note at the beginning.
With all that being said, simply replace Restrict with ClientNoAction and the issue will be solve. No database migration is needed because this change affects only the client behavior.
Well, the classification entity needs correct initialization, suppose to delete restriction rule.
modelBuilder.Entity<Classification>()
.HasKey(e => e.Id)
.HasMany(e => e.Customers)
.WithOne(e => e.Classification)
.OnDelete(DeleteBehavior.Restrict)
.IsRequired(true);
Hope this helps.

EF migration generating duplicated FK with different name

I'm Trying to rename the Default FK, but Code First Migration keep generating a second FK to the same table with a different name, messing up the Table schema.
The FK Model has a PK named Id, I just want to keep the convention required by my client, changing the name for Id(something).
1) Generated migration:
2) Mapping:
What should I do?
If your CorpoGestor entity exposes a property for the foreign key, use HasForeignKey instead of Mapand MapKey.
HasRequired(x => x.Conselho)
.WithMany()
.HasForeignKey(x => x.IdConselho);
Another solution: you can use the ForeignKey attribute in the property, instead of mapping the relation in the CorpoGestorMap class:
public class CorpoGestor
{
...
public int IdConselho { get; set; }
[ForeignKey("IdConselho")]
public virtual Conselho Conselho { get; set; }
}
A warning: using attributes in the entities is conceptually not so cool as implementing the code in the EntityTypeConfiguration mapping classes, because you are polluting your entities with data layer code that should be kept in the EF classes.

How do I tell Entity (Code First) to not send the Key ID field to the database?

My code:
Models.Resource r = new Models.Resource();
r.Name = txtName.Text;
r.ResourceType = resTypes.Find(rt => rt.Name == "Content");
r.ResourceContents.Add(_resourceContent.Find(rc => rc.ID == _resourceContentID));
ctx.Resource.Add(r);
ctx.SaveChanges();
ctx.SaveChanges() causes the error:
Cannot insert explicit value for identity column in table 'Resources' when IDENTITY_INSERT is set to OFF.
Looking at what's being sent to SQL:
ADO.NET:Execute NonQuery "INSERT [dbo].[Resources]([ID], [Name], [Description], [IsOnFile],
[ContentOwnerAlias], [ContentOwnerGroup], [ResourceTypes_ID])
VALUES (#0, #1, #2, #3, #4, #5, NULL)"
My POCO Resource has ID as a Key:
public partial class Resource
{
public Resource()
{
}
[Key]
public int ID { get; set; }
And my Map code:
public class ResourceMap : EntityTypeConfiguration<Resource>
{
public ResourceMap()
{
// Primary Key
this.HasKey(t => t.ID);
How do I tell Entity to not send the Key ID field to the database?
If your PK is generated by the database (like an identity) you have to configure it in your Map.
public class ResourceMap : EntityTypeConfiguration<Resource>
{
public ResourceMap()
{
// Primary Key
this.HasKey(t => t.ID);
this.Property(t => t.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
You do not need the HasKey(t => t.ID) Fluent API mapping or the [Key] Data Attribute because by convention EF will assume that an integer field named ID is the key and is database generated.
As an aside, I'd recommend that when you are not following conventions you should choose one method or the other - otherwise you are repeating yourself and when you want to change something you need to change it in 2 places.
I'm not sure why the field in the database isn't already database generated - maybe when you define the field via the fluent api you have to specify that too. What I do know is that in order to make EF change a key field to be database generated you will need to drop the table.
So - rollback the migration or drop the table / database, then remove the data attribute, remove the fluent mapping and recreate.
This issue is currently on a "backlog" in the entity framework. If you want to vote for it you can do that here: Migrations: does not detect changes to DatabaseGeneratedOption
Other References:
Identity problem in EF
Switching Identity On/Off With A Custom Migration Operation

One-To-One relationship with fluent api. A Hacky way?

EF 4.3.1. I have defined User and Box entities. Each box may or may not be assigned to a user.
What I'd like to achieve is to have a OwnBox property in User class, and an Owner property in Box class.
in Database, I have defined OwnerId foreignkey in Boxes (Boxes.OwnerId has relation with Users.UserId).
To define the relationship with fluent api, I have defined the following classes:
public partial class User
{
public int UserId {get; set;}
public virtual Box OwnBox { get; set; }
}
public partial class Box
{
public int? OwnerId { get; set; }
public virtual User User { get; set; }
}
Then in my Mapping class for Box, I have defined the relations as follows:
this.HasOptional(t => t.User).WithOptionalDependent(d => d.OwnBox).
Map(m => m.MapKey("OwnerId")).WillCascadeOnDelete(true);
But by firing up the project, I got the error:
Schema specified is not valid. Errors: (56,6) : error 0019: Each
property name in a type must be unique. Property name 'OwnerId' was
already defined.
So I had to tell EF to forget about the OwnerId column first:
this.Ignore(t => t.OwnerId);
Now the project works fine. But I'm still doubtful if this is a good approach and will everything work fine on CRUD operations with foreign key associations.
First of all, this is not one-to-one relationship. In one-to-one relationship the foreign key must be a primary key.
I believe in your scenario the situation can happen:
User = { UserID = 2 }
Box1 = { UserID = 2 }
Box2 = { UserID = 2 }
Nothing stops you from doing that, but which box should be returned when you do that:
User.OwnBox, Box1 or Box2?
EF can deal with that using Independent Association. It will create foreign key, hidden from your POCO class. You can specify the name of the column using MapKey as you did. However, because you also created a property called OnwerID, just as the column used with MapKey, the EF has a problem as two properties are mapped to the same column.
When you use ignore, the POCO OwnerID property is ignored by EF so that fixes the problem of two properties, however, the OwnderID value never gets saved or read to the database. Because EF just ignores it.
Thanks for your question, I have learnt a lot thanks to this.