Say I have a "Relationship" entity:
public class Relationship
{
[Key]
[Required]
public int RelationshipId { get; set; }
[Required]
public int FriendOneId { get; set; }
public virtual User FriendOne{ get; set; }
[Required]
public int FriendTwoId { get; set; }
public virtual User FriendTwo { get; set; }
}
If I want to map these relationships with ModelBuilder, what is the difference between this:
modelBuilder.Entity<Relationship>()
.HasRequired(c => c.FriendOne)
.WithMany()
.HasForeignKey(u => u.FriendOneId);
And this:
modelBuilder.Entity<Relationship>()
.HasRequired(c => c.FriendOne)
.WithMany()
.HasForeignKey(u => u.RelationshipId);
I get confused with this every time I'm setting up a new DB. The documentation I've found and answers on SO seem to conflict one another on this... any help in understanding how to use HasForeignKey would be much appreciated.
modelBuilder.Entity<ThisT>() //configure model for entity type <T>
.HasRequired(c => c.FriendOne) // if a field, ef will create on DB as Not Null, and check in context
// if it is a navigation entity, then an underlying FK field will be marked as Not null .
// A new field will be introduce to manage this if not declared
.WithMany() // the target of foreign key can many Entity<t> pointing at it.
// The Many target could also have ICOllection<ThisT>.
// ie .withMany(MainT=>MainT.BucketOfThem)
// leave it empty if the other side doesnt track related
.HasForeignKey(u => u.RelationshipId); // dont create a field, I have one already..
// the Key to Table behind FriendOne is called RelationshipId
standard EF docu on withMany knows that call is chained.
ie first you a HasRequired, then a WithMany.
so you are in 1:M config mode.
/// <summary>
/// Configures the relationship to be required:many with a navigation property on the other side of the relationship.
///
/// </summary>
/// <param name="navigationPropertyExpression">An lambda expression representing the navigation property on the other end of the relationship. C#: t => t.MyProperty VB.Net: Function(t) t.MyProperty </param>
/// <returns>
/// A configuration object that can be used to further configure the relationship.
/// </returns>
Related
In EF Core, when defining Relationships, one can either provide the necessary FK properties explicitly or not:
Explicit FK property:
public class Person
{
public Guid Id { get; set; }
public ICollection<ParentIdentity> Identities { get; set; }
...
}
public class PersonIdentity
{
public Guid Id { get; set; }
public PersonFK { get; set; } //Explicit Data storage FK field in System Logic Entity :-(
...
}
The relationship would be defined in Fluent API as follows:
model.HasMany(x => Identities) // Person can have multiple identities
.WithOne() // Identity does not need a Nav property back up to Person
.WithForeignKey(x => x.PersonFK) // Hardcoded the FK.
The upside is its eminently clarity of how it's hooked up.
The downside is the blurring of domains between system logic and storage -- in that the system entity now has Data storage specific attributes (PersonFK) that have nothing to do with system logic that developers should be concentrating on.
Shadow properties
The alternative is to let EF sort it out, using shadow properties, by not define an FK Property on the Entity:
public class Person
{
public Guid Id { get; set; }
public ICollection<ParentIdentity> Identities { get; set; }
...
}
public class PersonIdentity
{
public Guid Id { get; set; }
...
}
And define the relationship as follows:
model.HasMany(x => Identities) // Person can have multiple identities
.WithOne() // Identity does not need a Nav property back up to Person
//.WithForeignKey(x => x.PersonFK) // Don't provide an FK property
;
EF will step up and add a property to the db table named to the following convention:
<principal primary key property name>Id
//ie, will be created as `PersonId`
But let's say I want to change it to:
<principal primary key property name>FK
//ie, will be created as `PersonFK`
Question
How?
Foraging so far
In case it helps, I'm looking in the following direction:
I can see a SqlServerConventionSetBuilder that inherits from RelationalConventionSetBuilder that inherits from ProviderConventionSetBuilder.
ProviderConventionSetBuilder in turn calls
ForeignKeyIndexConvention
ForeignKeyPropertyDiscoveryConvention
ForeignKeyAttributeConvention
found some sparse documentation at https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.conventions.foreignkeyindexconvention?view=efcore-6.0
but not enough there to know where to look really.
Can someone point me in the right direction as to:
what convention to replace
how to replace it easily?
Thank you!
Using EF Core 3.0 I am trying to configure a one-to-many navigation between the following two classes.
public class Organization
{
public decimal Id{get;set;}
public int ClientId{get;set;}
public virtual ICollection<Contact> Contacts { get; set; }
}
public class Contact
{
public int Id{get;set;}
public int? ClientId{get;set;}
public virtual Organization Organization { get; set; }
}
I need to have the navigation use the ClientId fields rather than any the Primary Keys.
I have tried both annotations and Fluent
modelBuilder.Entity<Contact>()
.HasOne<Organization>(o => o.Organization)
.WithMany(c => c.Contacts)
.HasForeignKey("ClientId");
But for some reason it keeps going back to connection the ClientId to the Id of the Organization.
How can this be configured?
Yes the Id field in the legacy db is a decimal but I can not change that.
Assuming the database already has a Foreign Key declared from Contact.ClientId to Organization.ClientId, try:
modelBuilder.Entity<Contact>()
.HasOne<Organization>(o => o.Organization)
.WithMany(c => c.Contacts)
.HasForeignKey(c => c.ClientId)
.HasPrincipalKey(org => org.ClientId);
For more details: Alternate Keys in EF Core
I have a table (Commodity) which has a one-to-one relationship with another table (CommodityMaterial), in my GET endpoint the Commodity returns it's own columns and also the columns (and values) of the referenced table which works perfectly. However, in the POST operation of the endpoint, a user should not be able to POST data of the reference table (CommodityMaterial), how can this be achieved? I used to disable this by using a DataContract, however, because I need the columns for my GET operator, this is not an option.
I already tried, following this post: https://csharp.christiannagel.com/2016/11/07/efcorefields/, removing the SET on the reference table and making a backing field but this does not seem to work (error that the backing field is read-only).
I also tried setting the SET to protected, but this is not working.
So the question is, how to make the reference table read-only (only available for my GET endpoint and not my POST endpoint).
The Commodity POCO class:
[DataContract]
public class Commodity
{
public Commodity()
{
}
public Commodity(CommodityMaterial commodityMaterial)
{
CommodityMaterial = commodityMaterial;
}
[DataMember]
public long CommodityID { get; set; }
[DataMember]
public long CommodityMaterialID { get; set; }
[DataMember]
public decimal? SpecficWeight { get; set; }
[DataMember]
public CommodityMaterial CommodityMaterial { get; }
}
Fluent part:
modelBuilder.Entity<Commodity>(entity =>
{
entity.Property(e => e.CommodityID)
.HasColumnName("CommodityID")
.ValueGeneratedOnAdd();
entity.Property(e => e.CommodityMaterialID)
.HasColumnName("CommodityMaterialID");
entity.Property(e => e.SpecficWeight)
.HasColumnName("SpecficWeight")
.HasColumnType("decimal(18, 2)");
entity.HasOne(a => a.CommodityMaterial)
.WithOne(b => b.Commodity)
.HasForeignKey<Commodity>(b => b.CommodityMaterialID);
});
The parameters your action accepts should represent what your action does/is allowed to do. If a client should not be able to update a related entity, then the class you bind the request body to, should not have that entity available. Use a view model, essentially:
public class CommodityRequest
{
// all properties you want editable
// exclude `CommodityMaterial` obviously
}
Then:
public IActionResult Update(CommodityRequest model)
I have the following table being created (by using DNX commands in EF7, now EF Core)
[Table("FishGrade")]
public partial class FishGrade
{
public FishGrade()
{
FishPrices = new HashSet<FishPrice>();
}
[HiddenInput]
[Column("FishGradeId")]
public int Id { get; set; }
[Column("GradeCode")]
[MaxLength(5), Required]
public string Code { get; set; }
[Column("GradeName")]
public string Name { get; set; }
public string IsActive { get; set; }
public virtual ICollection<FishPrice> FishPrices { get; set; }
}
But when it the table is created, the Code column (column named=GradeCode), is created as a 1 character long column.
I also have the following in the OnModelCreating method
modelBuilder.Entity<FishGrade>(entity =>
{
entity.Property(e => e.Code)
.HasColumnType("char");
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(50);
entity.Property(e => e.IsActive)
.HasMaxLength(1)
.HasColumnType("char");
});
Why is this happening? How can I get the column to be created with length = 5?
You can define your model in three ways:
conventions
attributes
fluent API
Conventions are applied first, then attributes, and finally the fluent API in your model builder. Your model builder is resetting the attribute configuration.
You should try to simplify your model configuration and use always the same method.
NOTE: take into account that, if you use something like MVC client side validation, it only understands the configuration made via attributes. In all other regards it doesn't mind how you configure your EF model.
I am a newbie in the EF. I read http://msdn.microsoft.com/en-us/data/gg193958.aspx and still confused.
I have an existing database and I'm writing a Code First model. I have Operators table with op_code Char(6) Primary Key. In the Operator class I named it OperatorCode, e.g.
[Key]
[Column("op_code",TypeName = "char")]
[DisplayName("Operator")]
public virtual string OperatorCode { get; set; }
In several of my tables I have EnteredBy and in some ModifiedBy columns that are FK to the op_code.
Say, for the Clients table I have both of these fields.
So, I added to the Operator class at the bottom:
[InverseProperty("EnteredBy")]
public virtual ICollection<Client> ClientsEnteredBy { get; set; }
[InverseProperty("ModifiedBy")]
public virtual ICollection<Client> ClientsUpdatedBy { get; set; }
and I added the following into the Client class:
public virtual Operator EnteredBy { get; set; }
public virtual Operator ModifiedBy { get; set; }
and I am getting a run-time error about EnteredBy_OperatorCode and ModifiedBy_OperatorCode columns.
What should I fix /add to let EF know my column names?
Thanks in advance.
Your foreign column names in the database do not match the default convention for FK names which is NavigationPropertyName_PrimaryKeyNameinTargetClass. Because your navigation properties are called EnteredBy and ModifiedBy and the primary key property is called OperatorCode EF expects - according to the mentioned convention - EnteredBy_OperatorCode and ModifiedBy_OperatorCode as foreign key columns. But those do not exist in the database which is the reason for your exception. Instead your FK columns are EnteredBy and ModifiedBy.
So, to fix the problem you must override the convention.
If you don't have FK properties in your model use Fluent API:
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsEnteredBy)
.WithRequired(c => c.EnteredBy) // or HasOptional
.Map(m => m.MapKey("EnteredBy")); // mapping for the FK column name
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsUpdatedBy)
.WithRequired(c => c.ModifiedBy) // or HasOptional
.Map(m => m.MapKey("ModifiedBy")); // mapping for the FK column name
(With this mapping you can remove the InverseProperty attribute.)
An alternative approach is to expose the FKs as properties in the model. Rename the navigation properties and use their names for the FK properties. The mapping is then possible with data annotations.
In Client class:
[ForeignKey("EnteredByOperator")]
public string EnteredBy { get; set; }
[InverseProperty("ClientsEnteredBy")]
public virtual Operator EnteredByOperator { get; set; }
[ForeignKey("ModifiedByOperator")]
public string ModifiedBy { get; set; }
[InverseProperty("ClientsUpdatedBy")]
public virtual Operator ModifiedByOperator { get; set; }
And remove the InverseProperty attributes in the Operator class.
Instead of the data annotations you can also use Fluent API:
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsEnteredBy)
.WithRequired(c => c.EnteredByOperator) // or HasOptional
.HasForeignKey(c => c.EnteredBy);
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsUpdatedBy)
.WithRequired(c => c.ModifiedByOperator) // or HasOptional
.HasForeignKey(c => c.ModifiedBy);
If both relationships are required you will need to disable cascading delete for at least one of the relationships (append .WillCascadeOnDelete(false) at the end of one of the mappings), otherwise SQL Server will throw an error that multiple cascading delete paths between the tables are not allowed.
I would suggest to use the "alternative approach" (expose foreign keys as properties) because it is easier to work with in most cases.