EF creating an unwanted field in database - entity-framework

I've hit a snag while building a .net mvc site. I have 2 related objects and am struggling with properly linking them. Specifically:
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostCode { get; set; }
[ForeignKey("AddressCategory")] // <-- EF adds field to below object's table
public int AddressCategoryId { get; set; }
public virtual AddressCategory AddressCategory { get; set; }
}
public class AddressCategory
{
public int AddressCategoryId { get; set; }
public string Description { get; set; }
}
Adding the [ForeignKey] data annotation to the Address object results in EF adding an Address_AddressId column to the AddressCategory table, which I don't want (or need) to happen.
I've tried to omit the ForeignKey attribute, but then I run into other errors because .net can't link the tables (e.g. Unknown column 'Extent1.AddressId' in 'field list'). Additionally, I wouldn't be able to use:
var addresses = db.Addresses.Include(l => l.AddressCategory);
Is there any way to link the 2 tables without EF adding an additional column to the AddressCategory table?

Thank you to #cloudikka for responding. After much trial-and-error I seem to have gotten it to work simply by omitting any ForeignKey reference from either object. I let EF rebuild the database and perform all scaffolding (CRUD forms) and they have been created perfectly.
My take-away is that foreign key attributes should be used for parent-child relationships, but not for look-up tables. I clearly have much to learn about asp.net mvc!

Related

Specify a Parent-Child relationship in EF Core without using identity columns

Specify a Parent-Child relationship in EF Core without using identity columns
What's an efficient way within Entity Framework Core 5 (C#) to work with the data in a hierarchial table that is linked via non-identity columns.
Here's my primary class:
public class ServiceProvider
{
[Key]
public int Id { get; set; }
public string ParentSPCode { get; set; }
public string SPCode { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ContactEmail { get; set; }
public string Status { get; set; }
}
The SPCode value is unique, which I enforce via C# code. The ParentSPCode may be null or must match an existing SPCode. Again I enforce this via C# code.
I want this table to hold any number of levels of parent-child (1 or more) records, as defined by ParentSPCode-SPCode pairs.
I can retrieve these records via a complex hierarchy of LINQ "joins", but I am thinking there must be a cleaner way by defining the appropriate EF Core 5 relationship.
If I was in SQL Server, I would do this via a CTE.
I want to be able to bring in the child records in a manner similar to .Include(q => q.ParentSPCode == x.SPCode).

Defining a "one-to-one-to-one" relationship on Firebird 2.5 EF Core

I'm working with EF Core and a third-party Firebird 2.5 database and, for some reason, they decided to, rather than do a simple one-to-one relationship, create a single table with two columns that do that relationship itself, i.e.
STOCK
========
ID_STOCK(int)
more columns (and their datatypes)
STOCK_IDENTIFIER
========
ID_STOCK (int)
ID_IDENTIFIER (int)
STOCK_PRODUCT
========
ID_IDENTIFIER (int)
more columns (and their datatypes)
So, every STOCK has one STOCK_IDENTIFIER, which, in turn, has one STOCK_PRODUCT. Usually, when I'm creating my own DB in MySQL, I just set foreign keys with data annotations (I'm not fluent in Fluent API, pun intended) and let the migration do its job. However, in this case I can't change the DB's schema (on the account of it being third-party), so I need to use the existing structure.
Right now I have the following:
public class STOCK
{
[Key]
public int ID_STOCK { get; set; }
[MaxLength(50)]
public string DESCRIPTION { get; set; }
[Column(TypeName = "decimal(18, 4)")]
public decimal SELL_PRICE { get; set; }
public STOCK_IDENTIFIER STOCK_IDENTIFIER{ get; set; }
}
public class STOCK_IDENTIFIER
{
[ForeignKey("ID_STOCK")]
public STOCK ID_STOCK { get; set; }
public STOCK_PRODUCT ID_PRODUCT { get; set; }
}
public class STOCK_PRODUCT
{
[ForeignKey("ID_PRODUCT")]
public STOCK_IDENTIFIER ID_IDENTIFIER{ get; set; }
[MaxLength(18)]
public string GTIN{ get; set; }
[MaxLength(18)]
public string SKU{ get; set; }
[Column(TypeName = "decimal(18, 4)")]
public decimal INSTOCK_AMNT { get; set; }
}
I read at The property X is of type Y which is not supported by current database provider that Fluent API could fix that, however, that article works flawlessly for one-to-one. As soon as I try to implement Fluent on a cascading relationship like this one, I get
modelBuilder.Entity<STOCK_IDENTIFIER>()
.HasOne<STOCK_PRODUCT>(p => p.ID_IDENTIFIER)
.WithOne();
modelBuilder.Entity<STOCK>()
.HasOne<STOCK_IDENTIFIER>(p => p.IDENTIFICADOR)
.WithOne();
The property or navigation 'ID_IDENTIFIER' cannot be added to the entity
type 'STOCK_PRODUCT' because a property or navigation with the
same name already exists on entity type 'STOCK_PRODUCT'.
Any hints on what I've been doing wrong?

EF Core 3.1.7 Data annotations for multiple 1:1 relationships in table

I am having problems figuring out the data annotations to map more than one 1:1 relationships so that EF Core 3.11.7 understands it and can build a migration.
I have a Person table and a Notes table.
There is a 0:M Notes relationship in Person. A person record can have 0 or more notes.
In the notes table is a CreatedBy field which is a Person. It also has a LastEditedBy field which is also a person. EF keeps bombing on how to construct the relationship for Note.CreatedBy. If this were non EF, both fields would be ints with the PersonID of the proper person record. How do it, preferabbly with Data Annotations, explain this to EF Core?
When I try to create a migration it fails and says:
System.InvalidOperationException: Unable to determine the relationship represented by navigation property 'Note.CreatedBy' of type 'Person'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace VetReg.Domain.Model
{
public class Family
{
public int FamilyID { get; set; } = -1;
public string FamilyName { get; set; }
public List<Pet> Pets { get; set; } = new List<Pet>();
public List<PersonFamily> People { get; set; }
public int AddressID { get; set; } = -1;
public List<Note> Notes { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime? Birthdate { get; set; }
public string Password { get; set; }
public List<PersonFamily> Families { get; set; }
public List<Note> Notes { get; set; }
} // class People
public class Note
{
public int NoteID { get; set; }
public int CreatedByID { get; set; }
[ForeignKey("CreatedByID")]
public Person CreatedBy { get; set; }
public DateTime DateCreated { get; set; }
public int LastEditByID { get; set; }
[ForeignKey("LastEditByID")]
public Person LastEditBy { get; set; }
public DateTime? LastEditDate { get; set; }
public string NoteText { get; set; }
}
public class PersonFamily
{
public int PersonID { get; set; }
public int FamilyID { get; set; }
public Person Person { get; set; }
public Family Family { get; set; }
}
}
The question is (and this is what makes impossible to EF to automatically determine the relationships) what is the relation between Person.Notes and Note.CreatedBy / Note.LastEditBy - none probably? You've said there is 0:M relationship between Person and Note, but note that there are potentially 3 one-to-many relationships there - notes associated with person, notes created by person and notes edited by person, which potentially leads to 3 FKs to Person in Note.
Also note that none of the navigation properties is required, but when present they must be paired.
Assuming you want 3 relationships, i.e. there is no relation between Note.CreatedBy / Note.LastEditBy and Person.Notes, you need to tell EF that Note.CreatedBy and Note.LastEditBy do not have corresponding (a.k.a. inverse) navigation property in Person. This is not possible with data annotations. The only available data annotation for that purpose [InverseProperty(...)] does not accept empty/null string name, hence cannot be used for what is needed here.
Also there is another problem here which you will encounter after resolving the current, which also cannot be resolved with data annotations. Since you have multiple required (thus cascade delete by default) relationships from Person to Note, it creates the famous "cycles or multiple cascade paths" problem with SqlServer, and requires turning off at least one of the cascade delete.
With that being said, the model in question needs the following minimal fluent configuration:
modelBuilder.Entity<Note>()
.HasOne(e => e.CreatedBy)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Note>()
.HasOne(e => e.LastEditBy)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
The essential for the original issue are the HasOne / WithMany pairs. Once you do that, EF Core will automatically map the unmapped Person.Notes collection to a third optional relationship with no inverse navigation property and shadow FP property (and column) called "PersonId", i.e. the equivalent of
modelBuilder.Entity<Person>()
.HasMany(e => e.Notes)
.WithOne()
.HasForeignKey("PersonId");
Regarding the second issue with multiple cascade paths, instead of Restrict you can use any non cascading option or the newer ClientCascade. And it could be for just one of the relationships, as soon as it breaks the "cascade path" (apparently you can't break the cycle because it is demanded by the model).

EF 5 code first optional one to one mapping accessing foreign key fields on the model

This question is basically a repeat of this question regarding EF4 CTP but specific to EF 5.
I have a POCOs set up such that
public class ClassPrinciple
{
public int ClassPrincipleID { get; set; }
public virtual ClassDependent ClassDependent{ get; set; }
}
and
public class ClassDependent
{
public int ClassDependentID { get; set; }
public virtual ClassPrinciple ClassPrinciple{ get; set; }
}
in my model builder I create the optional one to one mapping like this
modelBuilder.Entity<ClassPrinciple>().HasOptional(p => p.ClassDependent)
.WithOptionalDependent(s => s.ClassPrinciple);
this creates, on the ClassPrinciples table a column called ClassDependent_ClassDependentID . I would like to be able to reference the data in this column through a property on the ClassPrinciple model but I seem unable to do so. The web page I linked to at the top of this question states:
EF in general only supports exposing FK properties on your entities in
one:many relationships (unless the FK is also the PK). This is
somewhat artificial but a side effect of EF not supporting non-PK
unique constraints. We are working on support for unique constraints
for EF at the moment but it won't be there in our first RTM of Code
First.
Sorry not to have a better answer as there really isn't a workaround
at this stage.
Is this still the case or is there a way to resolve this. I have tried fluent api map to column and data annotations in all sorts of combinations without success.
use this code :
public class ClassPrinciple
{
public int ClassPrincipleID { get; set; }
public int ClassDependentId { get; set; }
[ForeignKey("ClassDependentId")]
[InverseProperty("ClassPrinciple")]
public virtual ClassDependent ClassDependent{ get; set; }
}
public class ClassDependent
{
public int ClassDependentID { get; set; }
[InverseProperty("ClassDependent")]
public virtual ClassPrinciple ClassPrinciple{ get; set; }
}

EF 5 Code First using Inheritence in the class

I am getting Error when trying to run this code.
Unable to determine the principal end of an association between the
types 'AddressBook.DAL.Models.User' and 'AddressBook.DAL.Models.User'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.
The objective is that i am creating baseClass that has commonfield for all the tables.
IF i don't use base class everything works fine.
namespace AddressBook.DAL.Models
{
public class BaseTable
{
[Required]
public DateTime DateCreated { get; set; }
[Required]
public DateTime DateLastUpdatedOn { get; set; }
[Required]
public virtual int CreatedByUserId { get; set; }
[ForeignKey("CreatedByUserId")]
public virtual User CreatedByUser { get; set; }
[Required]
public virtual int UpdatedByUserId { get; set; }
[ForeignKey("UpdatedByUserId")]
public virtual User UpdatedByUser { get; set; }
[Required]
public RowStatus RowStatus { get; set; }
}
public enum RowStatus
{
NewlyCreated,
Modified,
Deleted
}
}
namespace AddressBook.DAL.Models
{
public class User : BaseTable
{
[Key]
public int UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string Password { get; set; }
}
}
You need to provide mapping information to EF. The following article describes code-first strategies for different EF entity inheritance models (table-per-type, table-per-hierarchy, etc.). Not all the scenarios are directly what you are doing here, but pay attention to the mapping code because that's what you need to consider (and it's good info in case you want to use inheritance for other scenarios). Note that inheritance does have limitations and costs when it comes to ORMs, particularly with polymorphic associations (which makes the TPC scenario somewhat difficult to manage). http://weblogs.asp.net/manavi/archive/2010/12/24/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph.aspx
The other way EF can handle this kind of scenario is by aggregating a complex type into a "fake" compositional relationship. In other words, even though your audit fields are part of some transactional entity table, you can split them out into a common complex type which can be associated to any other entity that contains those same fields. The difference here is that you'd actually be encapsulting those fields into another type. So for example, if you moved your audit fields into an "Audit" complext type, you would have something like:
User.Audit.DateCreated
instead of
User.DateCreated
In any case, you still need to provide the appropriate mapping information.
This article here explains how to do this: http://weblogs.asp.net/manavi/archive/2010/12/11/entity-association-mapping-with-code-first-part-1-one-to-one-associations.aspx