How automatically define navigational properties? - c#

Let these 2 classes,
A Parent class
class Parent{
[Key]
public int Id { get; set; }
public virtual ICollection<Child> child { get; set; }
}
And a Child class
class Child {
[Key]
public int Id { get; set; }
[ForeignKey("Parent")]
public int Parent_Id { get; set; }
public Parent Parent{ get; set; }
}
I would like to set a new child class like this:
var child = new Child();
MyParentInstacnce.child.Add(child)
However, when I set the parent class state.
db.Entry(child).State = System.Data.Entity.EntityState.Added;
db.Entry(MyParentInstacnce).State = System.Data.Entity.EntityState.Modified;
I got the fallowing error:
Additional information: A referential integrity constraint violation occurred: The property value(s) of 'Parent_Id' on one end of a relationship do not match the property value(s) of 'Parent.Id' on the other end.
This problem is solved setting mannualy the value of child.Parent_Id to the current parent id.
I would like to know if is somehow possible to the EntityFramework set automatically the relational properties by doing MyParentInstacnce.child.Add(child)
How can I configure Entity Framework to automaticaly define navigational properties?
OBS
Entify Frameworks is able to make this happens., All you need to do is to DO NOT create the navigational properties. Then the navigational will be under the hood and the EF will make all the work. However in this case I really need to be able to read the navigational properties.

Related

How to set up multiple one-to-one relationships in Entity Framework Core?

I have two entities say Parent, and Child; each parent can have at most two child references. I have set up my entities as follows:
class Parent
{
[Key]
public int ParentId { get; set; }
public int PrimaryChildId{ get; set; }
public Child PrimaryChild { get; set; }
public int SecondaryChildId { get; set; }
public Child? SecondaryChild { get; set; }
// remaining properties
}
class Child
{
[Key]
public int ChildId { get; set; }
public int ParentId { get; set; }
public Parent Parent {get; set; }
// remaining child properties
}
In the DbContext.OnModelCreating I have this code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>(builder =>
{
builder.HasOne(p => p.PrimaryChild);
builder.HasOne(p => p.SecondaryChild);
});
}
This isn't enough to accomplish what I'm trying to achieve here. I get an error:
Unable to determine the relationship represented by navigation property 'Child.Parent' of type 'Parent'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'
I've tried to set up the relationship from the Child entity, but I get different errors because this makes me set up two relationships for the same property. I don't want to have two navigation properties on my child when I know only one will be used at a time as it would make for a confusing model.
I've searched the internet a bit, but I'm not having any luck finding relationships that are set up in this manner.
I've been trying something like this for the last few days, and after trying all sorts of Data Annotations and Fluent API nonsense, the cleanest solution I could come up with turned out to be very simple which requires neither. It only requires adding a 'private' constructor to the Parent class (or a 'protected' one if you're using Lazy Loading) into which your 'DbContext' object is injected. Just set up your 'Parent' and 'Child' classes as a normal one-to-many relationship, and with your database context now available from within the 'Parent' entity, you can make 'PrimaryChild' and 'SecondaryChild' simply return a query from the database using the Find() method. The Find() method also makes use of caching, so if you call the getter more than once, it will only make one trip to the database.
Here is the documentation on this ability: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#injecting-services
Note: the 'PrimaryChild' and 'SecondaryChild' properties are read-only. To modify them, set the 'PrimaryChildId' and 'SecondaryChildId' properties.
class Parent
{
public Parent() { }
private MyDbContext Context { get; set; }
// make the following constructor 'protected' if you're using Lazy Loading
private Parent(MyDbContext Context) { this.Context = Context; }
[Key]
public int ParentId { get; set; }
public int PrimaryChildId { get; set; }
public Child PrimaryChild { get { return Context.Children.Find(PrimaryChildId); } }
public int? SecondaryChildId { get; set; }
public Child SecondaryChild { get { return Context.Children.Find(SecondaryChildId); } }
// remaining properties
}
class Child
{
[Key]
public int ChildId { get; set; }
public int ParentId { get; set; }
public Parent Parent { get; set; }
// remaining child properties
}

Errors with non-mapped child entity

I have a Parent and Child class/table and I'm using Fluent API to configure my mapping. The tables are joined on non primary key fields in each table, so according to what I've read, I can't configure the join in Entity Framework. Because of that, I'm going to load my Parent.Children property manually (parent.Children = (from x in context.Children...)
However, I'm getting an exception on my mapping of Parent. My classes look like
public class Parent
{
// Unique primary key
public int ParentPrimaryKey { get; set; }
// These two fields together compose the foreign key to join to Child
public string ParentForeignKey1 { get; set; }
public string ParentForeignKey2 { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
// Unique primary key
public int ChildPrimaryKey { get; set; }
// These two fields together compose the (non-unique) foreign key to join to Parent
public string ChildForeignKey1 { get; set; }
public string ChildForeignKey2 { get; set; }
}
When I try to query context.Parents, I get an exception because the SQL that Entity Framework generates is trying to join Parent to Child and is looking for a property on Child called Child_ParentPrimaryKey.
If I add modelBuilder.Entity<Parent>().Ignore(p => p.Children); I get the exception System.NotSupportedException: The specified type member 'Packages' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
How can I keep Children as a non-mapped property on Parent without getting errors?
You can exclude a property from EF mapping by adding the [NotMapped] attribute:
using System.ComponentModel.DataAnnotations.Schema;
public class Parent {
// ...
[NotMapped]
public List<Child> Children { get; set; }
}
DataAnnotations - NotMapped Attribute
Thanks to everyone who responded. Your comments made me realize the problem was that I was using the Parent.Children property in my LINQ query. I removed that, and it's working now.

Saving related entities at once - An error occurred while saving entities that do not expose foreign key properties for their relationships

(I've checked the related questions, and can't find an answer.)
I'm doing some tests with Code First Entity Framework 6.
I have a "Child" entity that references two "Parent" entities.
I want to create the child entity and parent entities, and then save them all at once (so I can cut down on the number of db.Save() calls, and to keep it as one unit of work).
public class Child
{
public int ChildID { get; set; }
public string Name { get; set; }
public virtual Parent Father { get; set; }
public virtual Parent Mother { get; set; }
}
public class Parent
{
public int ParentID { get; set; }
public string Name { get; set; }
[ForeignKey("Child")]
public int? ChildID { get; set; }
public virtual Child Child { get; set; }
}
(A bit of a confusing setup -- the parents are actually the "children" of the child in the relationship. I know it's bad abstraction. This is just a test.)
Controller:
public ActionResult AddWithParents()
{
Parent father = new Parent { Name = "Test Father" };
Parent mother = new Parent { Name = "Test Mother" };
Child newChild = new Child
{
Name = "Test Child",
Father = father,
Mother = mother
};
db.Children.Add(newChild);
father.Child = newChild;
mother.Child = newChild;
db.SaveChanges();
return RedirectToAction("Index");
}
This works, but doesn't populate the Parent.ChildID foreign key.
If I do something like father.Child = newChild, I get the following error:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
Is there any way to get this to work?
I figured out problem source in your code: the ChildId property in Parent class declared as nullable integer type, while ChildId property in Child class belongs to non-nullable integer type, which they're different types (Nullable<int> against int).
Hence, you should declare ChildId property in Parent class as non-nullable type, thus circular reference problem should be solved as this:
public class Child
{
public int ChildID { get; set; }
public string Name { get; set; }
public virtual Parent Father { get; set; }
public virtual Parent Mother { get; set; }
}
public class Parent
{
public int ParentID { get; set; }
public string Name { get; set; }
[ForeignKey("Child")]
public int ChildID { get; set; } // set this FK as non-nullable int
public virtual Child Child { get; set; }
}
According to this answer, circular dependency problem occurs when a foreign key property data type mismatch with source primary key, or a foreign key property has been set incorrectly.
Edit: Since ChildId property in Child class is non-nullable int primary key property in database table, the best way to remove circular dependency is setting foreign key with same data type as primary key (i.e. int).
Related problem:
Clean way to deal with circular references in EF?
Unable to determine a valid ordering for dependent operations

EF Code First Navigation Property to same table

I'm new to EF and struggling to implement the following scenario. I have an entity I'd like to have a navigation property to another of the same entity. E.g.
public class Stage {
public int ID { get; set; }
public int? NextStageID { get; set; }
public string Name { get; set; }
public virtual Stage NextStage { get; set;}
}
The only example I've found so far was where the entity had a parent / child relationship, i.e. the navigation property was an ICollection of the same entity. I tried adapting this but couldn't get it to work in my instance. Also, I only need it to be one way, i.e. the entity doesn't have a 'PreviousStage' property, just a 'NextStage' one. I'm configuring using Fluent API. Could someone advise if / how this can be achieved?
I am getting this error:
Unable to determine the principal end of an association between the types 'namespace.Stage' and 'namespace.Stage'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations
Edit
Just realised in my slightly simplified example, I didn't show that NextStageID is optional (int?).
You can explicitly define the relation as follows:
public class Stage {
public int ID { get; set; }
public int NextStageID { get; set; }
public string Name { get; set; }
[ForeignKey("NextStageID ")]
public virtual Stage NextStage { get; set;}
}
you need to add a parentId and Parent navigation property
and Children navigation property so entity framework understands that is a recursive relation
check the answer in this stack Overflow link

Breeze navigation properties on inherited base class do not map parent to child

I have C# classes that use Inheritance on the server side of a Web API. The base/super class has some 'one two many' navigation properties that are common to all the inherited classes. These classes are not EF or NH classes.
Breeze correctly generates the base class and inherited classes in the metadata and correctly shows the navigation properties from the base class on the inherited classes.
When loading the data from the API, breeze correctly "links" the "Child" -> "Parent" side of the linked navigation property, but does not map the reverse "Parent" -> "Child" relationship on the base class.
If I move the navigation property to the inherited child class, out of the base/super class then Breeze correctly links all the relationships from the Parent to the Child and the Child to the Parent.
public class Fine
{
public int Id { get; set; } // Primary Key
public int VehicleId {get; set; } // Foreign Key
public decimal Amount { get; set; }
... etc
}
public class Vehicle
{
public int Id { get; set; } //Primary Key
... other common properties
public IList<Fine> Fines { get; set; } // Common nav property
}
public class Motorcycle
{
public string MudguardColour { get; set; }
... other specific properties
}
public class MotorVehicle
{
public string BumperColour { get; set; }
... other specific properties
}
Breeze creates the entities on the client side that correctly reflects the shapes above and has ko.observable as well as ko.observablearray for the one to many (Vehicle -> Fines) properties.
If I query the server for the list of MotorVehicles and MotorCycles first, all is well. I then query the list for fines separately and breeze correctly links the "Fines" that are related to a specific Vehicle correctly. I can the say "fine.motorVehicle.id" or "fine.motorCycle.id", depending on the type and get the base/super class properties, like Id as well.
Breeze however does not link the "vehicle.fines", "motorVehicle.fines" nor the "motorCycle.fines" navigation properties. They remain empty arrays even though the "fine" entity is linked correctly to the vehicle, motorVehicle and motorCycle entities.
If I move the navigation property to to the child/inherited class, then breeze correctly links the entities as expected.
public class Fine
{
public int Id { get; set; } // Primary Key
public int VehicleId {get; set; } // Foreign Key
public decimal Amount { get; set; }
... etc
}
public class Vehicle
{
public int Id { get; set; } //Primary Key
... other common properties
//public IList<Fine> Fines { get; set; } // Common nav property
}
public class Motorcycle
{
public string MudguardColour { get; set; }
... other specific properties
public IList<Fine> Fines { get; set; } // Common nav property now in child class
}
public class MotorVehicle
{
public string BumperColour { get; set; }
... other specific properties
}
This gives me "fine.vehicle.id", "fine.motorVehicle.bumperColour" and "fine.MotorCycle.mudguardColour" as well as parent -> child relationship "vehicle.fines[0].amount and "motorCycle.fines[0].amount".
How do I get breeze to set these navigation properties when they are on the base/super class to avoid repeating them on the child/inherited classes?
PS: I am not using EF nor NH, but my metadata is modelled based on the NH layout (from the breeze NH samples) as well as the Breeze documentation with the required foreign keys related etc.
PPS: All of this works 100% for normal classes that do not use the inheritance setup.
Hope I have explained sufficiently?
The solution to the above issue was to ensure that the "associationName" for both the Base class and navigation property classes have the same "association" property "name" within their metadata.
Base Class:
navigationProperties: [{nameOnServer: Fine, entityTypeName: Fine, isScalar: false, associationName: AN_Fine_FineId, invForeignKeyNamesOnServer: [Id]
Related Class:
navigationProperties: [{nameOnServer: Vehicle, entityTypeName: Vehicle, isScalar: true, associationName: AN_Fine_FineId, foreignKeyNamesOnServer: [Id]}]

Categories