Cannot define EF 1 to 0..1 relationship properly - c#

I am unable to defined 1:0..1 relationship in Entity Framework.
I have "PerfData" and "AttachmentData" entity.
Perf is parent entity and it has zero or one Attachment.
In code AttachmentData.CustomForeignKeyId is FK to PerfData.Id, but in database FK should have different name Attachment.PerfId is FK to PerfData.Id (this is due to some base class and inheritance that I am not describing here).
public class AttachmentData {
...
public Guid Id {get;set;}
public Guid CustomForeignKeyId{ get; set; } // this is FK to Perf.Id
public PerfData Perf { get; set; } // navigation property
}
and corresponding Configuration is
internal class AttachmentDataConfig : BaseConfig<AttachmentData>
{
public AttachmentDataConfig () : base("Attachment")
{
Property(x => x.CustomForeignKeyId)
.HasColumnName("PerfId");
HasRequired(o => o.Perf)
.WithMany()
.HasForeignKey(f => f.CustomForeignKeyId)
.WillCascadeOnDelete(true);
}
}
If I have only this, it works fine. Migration is generated properly. However, I also need to have navigation property on other side:
public class PerfData {
public Guid Id {get;set;}
public AttachmentData> Attachment { get; set; } //navigation property
}
If I add this navigation property, then EF creates new migration with new column Perf.Attachment_Id that is unnecessary, as FK is already defined within Attachment table.

EF does not support 1:0..1 relationship with explicit FK column at the dependent side (it considers such relationship to be 1:0..N). For 1:0..1 it uses a so called Shared Primary Key Associations in which the PK of the dependent side is also a FK to the principal side.
With that being said, in your model the CustomForeignKeyId is redundant. The correct (from EF perspective) model would be like this:
public class PerfData
{
public Guid Id { get; set; }
public AttachmentData Attachment { get; set; } //navigation property
}
public class AttachmentData {
...
public Guid Id { get; set;}
public PerfData Perf { get; set; } // navigation property
}
with the following configuration:
internal class AttachmentDataConfig : BaseConfig<AttachmentData>
{
public AttachmentDataConfig() : base("Attachment")
{
HasRequired(e => e.Perf)
.WithOptional(e => e.Attachment)
.WillCascadeOnDelete(true);
}
}
UPATE: If you want to keep the existing FK field and use 1:0..N relationship, then change the PerfData class navigation property
public AttachmentData Attachment { get; set; }
to
public ICollection<AttachmentData> Attachments { get; set; }
and inside the configuration
.WithMany()
to
.WithMany(e => e.Attachments)

Related

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 Fluent API Cannot Insert one-to-one

I am trying to insert an object into a database table with Entity Framework and using code first (fluent api). Whilst doing this I keep running into one of the following errors:
1) InvalidOperationException: A dependent property in a
ReferentialConstraint is mapped to a store-generated column. Column:
'Id'
2) Cannot insert value into identity column with IDENTITY_INSERT set
to OFF
My relationship is a one-to-one however perhaps I can rework or structure the database to accomplish what I am wanting. I have also thought about utilizing a one to zero or zone even though the other object will always be required.
So I have the following database tables mapped into these C# objects (with virtual for the mapping):
public class test
{
[Key]
public long Id { get; set; }
public DateTime ResultDate { get; set; }
public virtual test_additional test_additional { get; set; }
public virtual test_status test_status { get; set; }
}
public class test_additional
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long TestId { get; set; } //Foreign Key to test
...
public virtual test test { get; set; }
}
public class test_status {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long TestId { get; set; } //Foreign Key to Test
public long TestFormId { get; set; } //this is the object I want to insert, Foreign key to the Primary key of test_form
...
public virtual test test { get; set; }
public virtual test_form test_form { get; set; } //object mapping
}
public class test_form {
[Key]
public long Id { get; set; } //Primary Key
public string FileName { get; set; }
public virtual test_status test_status { get; set; }
}
So some pretty simple objects, I've stripped members/columns that are necessary for the functionality for ease of readability.
So there are test objects that have an optional test_additional or test_status .
These are generated with a one to zero-or-one relationship. Which are working fine and I have the relationship defined as:
modelBuilder.Entity<test>()
.HasOptional(e => e.test_additional)
.WithRequired(e =>e.test);
modelBuilder.Entity<test>()
.HasOptional(e => e.test_status)
.WithRequired(e => e.test);
Now the entity I am having trouble with is the test_form, if a test_status is defined there should always be a test_form associated with that. I currently have a relationship defined as:
modelBuilder.Entity<test_form>()
.HasRequired(e => e.test_status)
.WithRequiredDependent(e => e.test_form);
In addition I have tried appending this config:
modelBuilder.Entity<test_status>()
.HasKey(e => e.TestFormId);
--
Here is a simple implementation of inserting this object in the database:
try {
test UserTest = new test { ResultDate = DateTime.Now; }
UOW.test.Insert(UserTest);
UOW.Save();
test_additional ta = new test_additional { TestId = UserTest.Id; }
test_form tf = new test_form { FileName = "Testing.pdf"; }
UOW.test_additional.Insert( ta );
UOW.test_form.Insert( tf );
UOW.Save(); //This is where it will throw that error.
test_status status = new test_status {
TestId = UserTest.Id;
TestFormId = tf.Id;
}
UOW.test_status.Insert( status );
UOW.Save();
} catch {
throw;
}
--
I have used BreakPoints before the Unit of Work saves and I can confirm that the Id in the test_form object is the default of long which is 0. So I am not setting the Identity Column explicitly. Upon removing of test_form (in the implemented method) I can insert into the test_additional category and save with no issue.
So my question is really... are my entity relationships defined correctly? Would it be smarter to use an additional One to Zero-or-One for the test_form object? Why can I not insert this simple object into my database?
I have also thought about defining the virtual test_form object in test_status as an ICollection, then I could use .HasMany(e => e.test_form).HasForeignKey(e => e.TestFormId); so it would bind to the Foreign Key even though I would only be using 1 item for the test_status.
Opinions? Am I close?
Thanks again for taking the time to read my question!
i had your problem. just do delete your database and migration files. after do it add the new migration to create the new database.

InvalidOperationException due to circular dependency in Entity Framework Core

I am getting the following InvalidOperationException when I try to save an entity with a one-to-one relationship:
System.InvalidOperationException: Unable to save changes because a
circular dependency was detected in the data to be saved: 'ForeignKey:
DeviceLicenseSubscriptionPlan {'LicenseId'} -> DeviceLicense {'Id'}
Unique ToPrincipal: License, ForeignKey: DeviceLicense
{'SubscriptionPlanId'} -> DeviceLicenseSubscriptionPlan {'Id'}
ToPrincipal: SubscriptionPlan'.
Here is my modell:
public class DeviceLicense
{
public Guid? Id { get; set; }
public int DeviceLimit { get; set; }
public DeviceLicenseSubscriptionPlan SubscriptionPlan { get; set; } = new DeviceLicenseSubscriptionPlan();
}
public class DeviceLicenseSubscriptionPlan
{
public Guid? Id { get; set; }
public Guid? LicenseId { get; set; }
public DeviceLicense License { get; set; }
}
Here the OnModelCreating():
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var deviceLicense = modelBuilder.Entity<DeviceLicense>().ToTable("DeviceLicense");
deviceLicense.HasKey(l => l.Id);
deviceLicense.HasOne<DeviceLicenseSubscriptionPlan>()
.WithOne(s => s.License)
.HasForeignKey<DeviceLicenseSubscriptionPlan>(s => s.LicenseId)
.HasConstraintName("LicenseId");
deviceLicense.Property(l => l.DeviceLimit).HasColumnName("DeviceLimit");
var deviceLicenseSubPlan = modelBuilder.Entity<DeviceLicenseSubscriptionPlan>().ToTable("DeviceLicenseSubscriptionPlan");
deviceLicenseSubPlan.HasKey(s => s.Id);
deviceLicenseSubPlan.Property(s => s.Id).HasColumnName("SubscriptionPlanId");
base.OnModelCreating(modelBuilder);
}
I am using EF Core 2.0. I probably do something wrong within the ModelBuilder? Any hints?
The problem is this line
deviceLicense.HasOne<DeviceLicenseSubscriptionPlan>()
It basically tells EF that there is no navigation property to DeviceLicenseSubscriptionPlan in DeviceLicense. However there is a navigation property, so EF by convention maps it to a second relationship with FK in DeviceLicense pointing to DeviceLicenseSubscriptionPlan. Which of course with the combination of the desired FK in DeviceLicenseSubscriptionPlan creates a cycle.
Make sure fluent configuration uses the correct overloads which exactly represent the presence/absence of a navigation property in either side of the relationship. In this particular case, replace the above with
deviceLicense.HasOne(l => l.SubscriptionPlan)

Entity Framework Core not supporting generic abstract entities with many to many relationships

I have faced a strange problem witch EF Core 1.1. I m trying to build application where some entities can be tagged, thus I've created an abstract generic class for the relation table list. The problem is that, it seems like EF do not support to have a generic abstract classes which FK (Id property works).
Here are models:
public abstract class TaggedEntityBase<T> : EntityBase
{
public ICollection<T> EntityTags { get; set; }
public List<Tag> Tags { get { return EntityTags?.Select(x => x.Tag).ToList(); } }
}
public class AddressTag
{
public long TagId { get; set; }
public Tag Tag { get; set; }
public long EntityId { get; set; }
public Address Entity { get; set; }
}
public class Address : TaggedEntityBase<AddressTag>
{
public string Street { get; set; }
public string City { get; set; }
}
public class Tag : EntityBase
{
public string Name { get; set; }
public virtual ICollection<AddressTag> AddressTags { get; set; }
}
The Model Builder mappings:
public DbSet<Address> Addresses { get; set; }
public DbSet<AddressTag> AddressTag { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AddressTag>()
.ToTable("AddressTag");
modelBuilder.Entity<AddressTag>()
.HasKey(t => new { t.EntityId, t.TagId });
modelBuilder.Entity<AddressTag>()
.HasOne(pt => pt.Entity)
.WithMany(p => p.EntityTags)
.HasForeignKey(p => p.EntityId);
modelBuilder.Entity<AddressTag>()
.HasOne(pt => pt.Tag)
.WithMany(p => p.AddressTags)
.HasForeignKey(p => p.TagId);
}
There is an error when EF try to fetch Tags
An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.dll Additional information: Invalid column name 'AddressId'.
I dont even have that Id convention.
Note: when I place explicitly public ICollection<AddressTag> EntityTags { get; set; }inside Address POCO, then it works perfectly, including EntityTags.Tag too.
Thanks for any help :)
The issue has nothing to do with generic and/or abstract base entity classes.
First, to make your sample model compile, I've added the following classes
public abstract class EntityBase
{
public long Id { get; set; }
}
public abstract class EntityTagBase
{
public long TagId { get; set; }
public Tag Tag { get; set; }
}
modified the AddressTag class as follows:
public class AddressTag : EntityTagBase
{
public long EntityId { get; set; }
public Address Entity { get; set; }
}
and added where T : EntityTagBase constraint to TaggedEntityBase<T> class to allow Tag property accessor inside Select(x => x.Tag).
So far so good. The Tag related part of generated migration looks like this:
migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
AddressId = table.Column<long>(nullable: true),
Name = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Tags", x => x.Id);
table.ForeignKey(
name: "FK_Tags_Addresses_AddressId",
column: x => x.AddressId,
principalTable: "Addresses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
See the AddressId column and FK to Addresses table? Why is that? Because of your Tags property:
public List<Tag> Tags { get { return ...; } }
It's probably a current EF Core bug of mapping a read only collection property, but the net effect is that it considers one to many relationship between Address and Tag which of course is not your intention.
In general I would recommend keeping the entity model clean and not include such "helper" properties - both collection and reference type. They look like navigation properties, but they are not, and it's easy to use them by mistake inside a query, which will totally change the execution plan and lead to unexpected exceptions or wrong results (in case the underlying property is not loaded). Not speaking about the violation of a general rule to not create property returning List which is not a member of the class, but created in every property access call.
Shortly, simply remove that property and the problem will be gone. Or if you insist keeping it, then decorate it with NotMapped data annotation:
[NotMapped]
public List<Tag> Tags { get { return ...; } }

How to set foreign key in EntityTypeConfiguration Class

I just started to make EntityTypeConfiguration class and did following
public class Xyz
{
public int PlaceId { get; set; }
public string Name { get; set; }
public DbGeography Location { get; set; }
public int HumanTypeId { get; set; }
public int AddressId { get; set; }
}
and in EntityTypeConfiguration class
public sealed class XyzConfiguration:EntityTypeConfiguration<Xyz>
{
public XyzConfiguration()
{
ToTable("Place", "dbo");
HasKey(p => p.PlaceId);
Property(p => p.PlaceId)
.HasColumnName("PlaceId")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(p => p.Name);
Property(p => p.Location). ;
Property(p => p.HumanTypeId);
Property(p => p.AddressId);
}
}
Now how to set DbGeography and foreign key columns HumanTypeId , AddressId ?
Thanks in advance
It depends on what you're going to do with the columns. If you have foreign key columns like AddressId, you probably have some Address entities that you want to relate to your Xyz entities. You need to decide how the entites relate to each other, and configure the mapping you want between them.
You will need a navigation property either in your Address class, or your Xyz class, otherwise there isn't anything to bind the foreign key to, and your foreign ID columns would just be treated as normal columns (which is fine, if that's what you want).
So, if your were to add a navigation property to your Xyz entity
public class Xyz
{
// Your code
public int AddressId { get; set; }
public virtual Address MyAddress { get; set; }
}
// Your Address class
public class Address
{
public int ID;
}
You could configure the mapping by doing something along these lines (it will vary depending on the relationship:
public sealed class XyzConfiguration : EntityTypeConfiguration<Xyz>
{
public XyzConfiguration()
{
// Your code.
this.HasOptional(x => x.MyAddress) // Your Xyz has an optional Address
.WithMany() // Address may be owned by many Xyz objects
.HasForeignKey(x => x.AddressId); // Use this foreign key.
}
}
I haven't tried using spatial types and EF, but I'd start here: http://msdn.microsoft.com/en-us/data/hh859721.aspx
There's a wealth of information on mapping configurations on the getting started with EF pages: http://msdn.microsoft.com/en-us/data/ee712907 try "Fluent API - Configuring/Mapping Properties & Types"
There's also a slightly abridged explanation of the different association types here:
Code First: Independent associations vs. Foreign key associations?

Categories

Resources