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.
Related
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.
Struggling on a Foreign key with Entity Framework (1 to many relationship).
class1:
public partial class Class1
{
public int Id { get; set; }
[ForeignKey("Class2_Id")]
public Class2 Class2{ get; set; }
...}
Class2:
public partial class Class2
{
public int Id { get; set; }
public virtual ICollection<Class1> Stuff{ get; set; }
...}
1)I tried with fluentAPI:
1st try:
modelBuilder.Entity<Class2>().HasMany<Class1>(p => p.Stuff).WithOptional().Map(m => m.MapKey("Class2_Id")).WillCascadeOnDelete();
2nd try:
modelBuilder.Entity<Class1>().HasRequired(i => i.Class2).WithMany().Map(m => m.MapKey("Class2_Id"));
2)without fluentAPI:
I declared the concerned Class2 field this way:
[Column("Class2")]
public int Id { get; set; }
Even (which probably makes no sense):
[ForeignKey("Class2")]
public int Id { get; set; }
I always get this error:
The ForeignKeyAttribute on property 'Class2' on type 'Class1' is not valid. The foreign key name 'Class2_Id' was not found on the
dependent type 'Class1'. The Name value should be a comma separated
list of foreign key property names.
Any idea what is wrong?
thx in advance.
With your fluent api configurations you are defining a unidirectional relationship, so the correct way would be:
modelBuilder.Entity<Class2>().HasMany(p => p.Stuff)
.WithOptional(c=>c.Class2)
.Map(m => m.MapKey("Class2_Id"))
.WillCascadeOnDelete();
MapKey method is used when you don't want to have the foreign key as a property in your model class.
Use ForeignKey attribute when you represent the FK column as a property of your entity:
public partial class Class1
{
public int Id { get; set; }
public int? Class2_Id{ get; set; }
[ForeignKey("Class2_Id")]
public Class2 Class2{ get; set; }
}
In this last case your relationship configuration using Fluent Api would be:
modelBuilder.Entity<Class2>().HasMany(p => p.Stuff)
.WithOptional(c=>c.Class2)
.HasForeignKey(c=>c.Class2_Id))
.WillCascadeOnDelete();
I have this model
public class Event {
[Key]
public int Id { get; set; }
public string Title { get; set; }
}
public class EventAction {
[Key]
public int Id { get; set; }
public int EventId { get; set; }
[ForeignKey("EventId")]
public Event Event { get; set; }
public int? RelatedEventId { get; set; }
[ForeignKey("RelatedEventId")]
public Event RelatedEvent { get; set; }
}
When I generate the code first migration, it tries to add a new Event_Id column instead of using my RelatedEventId even if I put the ForeignKey attribute to indicate the field to use.
This is not the first time I define multiple foreign key that link to same table, but I never went thru this problem before.
Here's the migration code generated
public override void Up() {
AddColumn("dbo.EventActions", "Event_Id", c => c.Int());
AddColumn("dbo.EventActions", "RelatedEventId", c => c.Int());
CreateIndex("dbo.EventActions", "Event_Id");
CreateIndex("dbo.EventActions", "RelatedEventId");
AddForeignKey("dbo.EventActions", "Event_Id", "dbo.Events", "Id");
AddForeignKey("dbo.EventActions", "RelatedEventId", "dbo.Events", "Id");
}
I finally found the problem.
For simplicity I have omited to include all my Event Class and also tought the problem was on EventAction.
So the missing piece in Event class is a collection to EventAction.
public class Event {
[Key]
public int Id { get; set; }
public string Title { get; set; }
//this line was missing in my original post
//and that's the one that was confusing entity framework
public ICollection<EventAction> Actions { get; set; }
}
EventAction class contains two properties with Event type.
Entity framework must know which one we want to bind to.
So to accomplish this we need to, whith annotation, add [InverseProperty("Event")] on the collection property. Here, "Event" refer to property name, not the type.
Using Fluent API you have to use the overload version of WithMany(d => d.Actions) when defining the foreign key.
I have class like this:
class Tree
{
[Key]
public int TreeID { get; set; }
public int? ParentID { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public virtual Tree Parrent { get; set; }
public virtual ICollection<Tree> Children { get; set; }
}
And configuration class :
class TreeConfiguration : EntityTypeConfiguration<Tree>
{
public TreeConfiguration()
{
this.HasOptional(d => d.Parrent)
.WithMany(p => p.Children)
.HasForeignKey( d => d.ParentID)
.WillCascadeOnDelete(false);
}
}
It works good, but what I want is to child node use ID (from parrent node) for ParentID, not TreeID.
It should work like this:
Each node has an id - that is ID - and id of its parent - that is ParentID. TreeID is primary key and it has nothing to do with child-parent mapping - it's for database only.
I can't change the columns so I it must be this way
Well.. apparently it has something to do with the fact that FKs must always point to primary keys. So it's not possible.
Why don't you use your "ID" as a unique column - e.g. use a unique index for it?
Then you have the same semantics like you would have if ID would be the PK..
how do I create a collection of classes which always have a certain predefined value set in AutoFixture?
Fixture.Register<IList<Child>>(() => Fixture.CreateMany<Child>().ToList());
Say the child class has the following:
public class Child
{
public int ParentId { get; set; }
public int ChildId { get; set; }
public string ChildName { get; set; }
public int ChildValue { get; set; }
}
How do I ensure the anonymous classes always have the same parent Id whilst all other properties can be random? I guess it would also be advisable to set ChildId to 0 too as these will be pushed into a database in a repository data test.
Have you tried this?
fixture.Customize<Child>(c => c.With(x => x.ParentId, 42).With(x => x.ChildId, 0));