Self referencing loop detected for property when inserting into database - c#

I am trying to learn the C# Fluent API, and Im running into issues (I think) with my model setup. I have three tables: OrderFile, Order, LineItem. The error:
Self referencing loop detected for property 'order' with type 'BaseService.WebApi.Order'. Path 'orders[0].lineItems[0]'.
My structure:
OrderFile contains List<Orders>
Order contains List<ListItems> and a Navigation property OrderFile
ListItem contains a Navigation property Order
They are tied together with ForeignKey constraints specified in a Fluent API. Is something wrong with the constraints? I was trying to follow this example for Foreign keys
modelBuilder.Entity<OrderFile>(e =>
{
//many orders within one order file
//the FK relates the OrderFile to the nav key of the Order
e.HasMany(of => of.Orders)
.WithOne(o => o.orderFile)
.HasForeignKey(o => o.FileGuid);
e.HasKey(o => o.FileGuid);
});
modelBuilder.Entity<Order>(e =>
{
//each order has an array of line items
//each line item has one order (navigation property)
//the foreign key of the line item ties it to the Parent (List<Order>)
e.HasMany(o => o.LineItems)
.WithOne(li => li.order)
.HasForeignKey(o => o.OrderGuid);
e.HasKey(o => o.OrderGuid);
});
Models
public class OrderFile
{
public Guid FileGuid { get; set; }
public virtual ICollection<Order> Orders { get; set; } //everything with same FileGuid
}
public class Order
{
....
[JsonIgnore]
public Guid FileGuid { get; set; }
[Key]
public Guid OrderGuid { get; set; }
[JsonIgnore]
public OrderFile orderFile { get; set; }
public virtual ICollection<LineItem> LineItems { get; set; } //everything with same OrderGuid
}
public class LineItem
{
....
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public Guid OrderGuid { get; set; }
public Order order { get; set; }
}

Your LineItem entity has a reference to Order, which doesn't have a JsonIgnore attribute.
Basically your problem stems from trying to serialize an object graph that has circular dependencies (loops), while your design problem is that you use database entity classes in your API. The client facing models should be different classes than the entities you persist in the database.

Related

Proxy Items in Entity Framwork Core

Is it possible to create as a proxy element in EF Core?
For example, in the database there is the element with the id 1, which has the name Example. The second element with id 2 has no name (is null), but has a reference to element 1 ("id_replace"). In this case I would like the name returned by item 2 to be "Example" like item 1. And also the "Includes" quote to item 1 references.
The reason I have such a strange idea is that I need to have linked the elements, and if element 1 changes, the changes made are displayed on element 2 as well.
Example Registers in Database
Sure you can. Assuming that your class is:
public class YourClass
{
public int id { get; set; }
public string name { get; set; }
public int? id_replace { get; set; }
}
In your class, you need to have the one to many referencing properties:
public YourClass parent { get; set; }
public IList<YourClass> children { get; set; }
Then, in your DbContext class, in the override OnModelCreating function, you need to have a relationship set in the fluent API that indicates that id_replace is a self-referencing foreign key:
modelBuilder.Entity<YourClass>(entity =>
{
entity.HasOne(x => x.parent)
.WithMany(x => x.children)
.HasForeignKey(x => x.id_replace)
.OnDelete(DeleteBehavior.SetNull);
});
After doing that(and migrating), you have the necessary navigation properties to be able to add computed properties that do not represent anything in the database. So your class can have the property:
public int alt_name => name??$"\"{parent.name}\"";
So eventually, your class will look something like this:
public class YourClass
{
public int id { get; set; }
public string name { get; set; }
public int? id_replace { get; set; }
public YourClass parent { get; set; }
public IList<YourClass> children { get; set; }
public int alt_name => name??$"\"{parent.name}\"";
}
That way, you can discard the name property and just call on the alt_name property. You can even set the name property as private or change the names to avoid confusion.

Entity Framework adds extra field in database. and adds both members of composite key

I'm struggling with composite keys and extra fields being generated by Entity Framework. I have a question about something which I think is odd.
Let's say I have a one to many relationships with these classes:
File (dossier)
[Table("Dossier")]
public class Dossier
{
[Key]
public string Dossiernummer { get; set; }
[Key]
public string Dossierversie { get; set; }
[Required]
public string Dossierreferentie { get; set; }
[Required]
public string Relatienr { get; set; }
public ICollection<Artikel> Artikels { get; set; } ();
}
And my artikel (article) class:
[Table("Artikel")]
public class Artikel
{
[Key]
public string Artnr { get; set; }
[Key]
public string ArtVersie { get; set; }
public string ArtOmschrijving { get; set; }
public Dossier Dossier { get; set; }
public string Dossiernummer { get; set; }
}
I'm using migrations and a code first approach. For some reason using migrations creates a dossiernummer1 column in the artikel table. I don't understand why and would like it gone. Does anyone know how?
Another thing which I prefer not to have is the second primary key in my artikel table. It puts both keys from the dossier table in the artikel table yet I only want to use Dossiernummer as a foreign key. Do you know how to change this?
When getting all the dossiers from the context I notice something odd as well. When I look into a dossier object the artikels list is empty, even though data exists in the database for that. Is it normal you have to initialize it yourself?
Thanks for any help and info in advance.
Kind regards,
you must use fluent API for set relations and add ColumnAttribute to order keys :
[Table("Artikel")]
public class Artikel
{
[Key]
[Column(Order = 1)]
public string Artnr { get; set; }
[Key]
[Column(Order = 2)]
public string ArtVersie { get; set; }
public string ArtOmschrijving { get; set; }
public Dossier Dossier { get; set; }
public string Dossiernummer { get; set; }
}
[Table("Dossier")]
public class Dossier
{
[Key]
[Column(Order = 1)]
public string Dossiernummer { get; set; }
[Key]
[Column(Order = 2)]
public string Dossierversie { get; set; }
[Required]
public string Dossierreferentie { get; set; }
[Required]
public string Relatienr { get; set; }
public ICollection<Artikel> Artikels { get; set; }
}
in your dbcontext override OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
builder.Entity<Dossier>()
.HasMany(x => x.Artikels)
.WithOne(a => a.Dossier)
.HasForeignKey(a => new { a.Dossiernummer, a.Artnr });
builder.Entity<Artikel>()
.HasKey(x => new {x.Artnr,x.ArtVersie});
builder.Entity<Dossier>()
.HasKey(x => new {x.Dossiernummer,x.Dossierversie});
}
Dossiernummer1:
Artikel has a Dossier. EF knows an K relationship must be set up. This requires that the PK of Dossier must be included in Artikel and wants to add it. It finds you've already put in a field with that name (what for it has no idea) and so it adds it as Dossiernummer1. You should not add Dossiernummer to Artikel - unless you you actually need one for something else - as the only reason it's there is to be an FK. EF will take care of that for you.
Adding Dossierversie to Artikel:
It thinks that the PK of Dossier is Dossiernummer + Dossierversie, and so to point to the correct Dossier it must have both of tem. I don't use code-first so I can't advise you on a) how to specify a PK and another, separate index (I assume that's what you want) versus a compound PK (which is what you appear to have).
Dossier.Artikels is empty: That's the way EF works, known as lazy loading. It gets the 'root' objects by not anything owned by them at first. Once your code accesses an Artikels collection it should load them (for that Dossier) at that point. This prevents EF pulling in what could be a large percentage of your database Imaging an ECommerce system. Getting a Customer list would pull in all Orders related to cutomers in that list; all order lines owned by those orders; all Product data related to the products on those order lines and so on. This would not be a good thing. Instead it just gets the things you've specifically mentioned and then pulls in related items as needed.
Incidentally, when looking as an unloaded collection as an attribute of the owner (e.g. looking at Artikels on a loaded Dossier), the debugger in VS tells me that examining the collection will result in it being loaded and gives me the option to continue or not.

Many-to-one relationship

I have a schema Definitions which I would like to be able to reference itself. As I need meta data about the reference, there's a coupling schema named Associations. I'm using Entity Framework's fluent API in conjunction with data annotation attributes.
Definitions:
public class Definition
{
[Key]
public int Id { get; set; }
// ...
public virtual ICollection<Association> Associations { get; set; }
}
Associations:
public class Association
{
[Key]
public int Id { get; set; }
public int TypeId { get; set; }
public int AssociatedDefinitionId { get; set; }
public int RootDefinitionId { get; set; }
public virtual AssociationType Type { get; set; }
public virtual Definition AssociatedDefinition { get; set; }
public virtual Definition RootDefinition { get; set; }
}
OnModelCreating:
modelBuilder.Entity<Association>()
.HasRequired(p => p.AssociatedDefinition)
.WithRequiredPrincipal();
modelBuilder.Entity<Association>()
.HasRequired(p => p.RootDefinition)
.WithRequiredPrincipal();
I use MySQL as the database engine.
When I try to save a definition entity with an empty association collection, I get a constraint violation:
Cannot add or update a child row: a foreign key constraint fails
("u0228621_8"."Definitions", CONSTRAINT
"FK_Definitions_Associations_Id" FOREIGN KEY ("Id") REFERENCES
"Associations" ("Id"))
What am I doing wrong?
You have defined your association class with all relationships being "required:required" because of the WithRequiredPrincipal which doesn't seem to be what you want. Since the Associations collection appears (from the comments) to be the relation from the Root definitions, the mapping should come from definition, like so:
// Foreign key mappings included.
modelBuilder.Entity<Definition>().HasMany(d => d.Assocations)
.WithRequired(a => a.RootDefinition).HasForeignKey(a => a.RootDefinitionId);
modelBuilder.Entity<Association>().HasRequired(a => a.AssociatedDefinition)
.HasForeignKey(a => a.AssociatedDefinitionId);
So the Associations collection may be empty, but every Association requires a RootDefinition and AssociatedDefinition.

EF - Many-to-many configuration where both sides have same mapping property

I'm attempting to create a many-to-many mapping between User and Group models. Here are my classes:
public abstract class Entity
{
public Guid Id { get; set; }
public DateTime Created { get; set; }
public DateTime? Modified { get; set; }
}
public class User : Entity
{
public string Email { get; set; }
public string Name { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
public class Group : Entity
{
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
As you can see, I'm using the Entity abstract class to implement common properties in the classes that inherit from it. In this case, Id will be the key property for all of my EF classes.
Here is my configuration file where I map the many-to-many relationship:
public GroupConfiguration()
{
Property(x => x.Id).IsRequired().HasDatabaseGeneratedOption(databaseGeneratedOption: DatabaseGeneratedOption.Identity);
Property(x => x.Name).HasMaxLength(50).IsRequired();
HasMany(g => g.Users)
.WithMany(u => u.Groups)
.Map(m =>
{
m.MapLeftKey("Id");
m.MapRightKey("Id");
m.ToTable("UserGroups");
});
}
When I attempt to add a migration, I get the following error: Id: Name: Each property name in a type must be unique. Property name 'Id' is already defined. It seemingly doesn't like the fact that the property being mapped on both sides has the same identifier. When I don't inherit from Entity for one of the classes, eg. User, and make the mapping property UserId it is able to successfully create a migration.
Is there any way around this? It would be to be able to use an Id property for all of my entities defined in an abstract class.
Thanks in advance.
You can't have 2 keys with the same name, it will represent the columns for your relationship table (named "UserGroups"). When you call "MapLeftKey" or "MapRightKey", you define the columns name.
So I suggest you to rename your Ids (UserId and GroupId for example) and your mapping should be alright. I think you cannot have another solution on using fluent API.

entity framework - many to many relationship

Hi I try use Many to Many relationship with EF Fluent API. I have 2 POCO classes.
public class Project
{
public int ProjectId { get; set; }
public virtual ICollection<Author> Authors { get; set; }
public Project()
{
Authors = new List<Author>();
}
}
public class Author
{
public int AuthorId { get; set; }
public virtual ICollection<Project> Projects { get; set; }
public Author()
{
Projects = new List<Project>();
}
}
And I map many to many relationship with this part of code:
////MANY TO MANY
modelBuilder.Entity<Project>()
.HasMany<Author>(a => a.Authors)
.WithMany(p => p.Projects)
.Map(m =>
{
m.ToTable("ProjectAuthors");
m.MapLeftKey("ProjectId");
m.MapRightKey("AuthorId");
});
This created table ProjectsAuthors in DB. It is my first attempt with this case of relationship mapping.
If I omitted this mapping it created table AuthorProject with similar schema. It is correct bevahior?
By trial and error I found the following. Given two classes...
public class AClass
{
public int Id { get; set; }
public ICollection<BClass> BClasses { get; set; }
}
public class BClass
{
public int Id { get; set; }
public ICollection<AClass> AClasses { get; set; }
}
...and no Fluent mapping and a DbContext like this...
public class MyContext : DbContext
{
public DbSet<AClass> AClasses { get; set; }
public DbSet<BClass> BClasses { get; set; }
}
...the name of the created join table is BClassAClasses. If I change the order of the sets...
public class MyContext : DbContext
{
public DbSet<BClass> BClasses { get; set; }
public DbSet<AClass> AClasses { get; set; }
}
...the name of the created join table changes to AClassBClasses and the order of the key columns in the table changes as well. So, the name of the join table and the order of the key columns seems to depend on the order in which the entity classes are "loaded" into the model - which can be the order of the DbSet declarations or another order if more relationship are involved - for example some other entity refering to AClass.
In the end, it doesn't matter at all, because such a many-to-many relationship is "symmetric". If you want to have your own name of the join table, you can specify it in Fluent API as you already did.
So, to your question: Yes, naming the join table AuthorProjects is correct behaviour. If the name had been ProjectAuthors it would be correct behaviour as well though.

Categories

Resources