Nhibernate : Updating a child entity directly without inverse - c#

I'm using fluent nhibernate to map a parent child relationship to the sql database.
Most of the times i let the parent entity save it's child entities,they are inserted in 1 transaction and if i'm not mistaken this wouldn't be possible if i used .Inverse() and sql identity columns.
The problem i have is that in 1 specific case i want to update the child entity and ONLY the child entity.
When i do this with my current setup the child record will lose the relationship to it's parent(if the parent object is null) or will replace my parent object completely(if i insert a dummy parent with just an id).
Does anyone know a way to achieve a single record update without affecting the foreign key?
I can think of a manual sql statement or a stored procedure but i'm hoping there is an nhibernate way.
I have the following setup (simplified for your convenience) :
public ProjectMap()
{
Table("Project");
Id(p=> p.Id);
HasMany(p => p.Risks).Not.LazyLoad();
}
public RiskMap()
{
Table("ProjectRisk");
Id(r=> r.Id);
References(r => r.Project).Column("ProjectId");
Map(r => r.Description);
}
public class Project
{
public virtual int Id { get; set; }
public virtual IList<Risk> Risks { get; set; }
}
public class Risk
{
public virtual int Id { get; set; }
public virtual string Description{ get; set; }
public virtual Project Project { get; set; }
}

As Miroslav suggested i'm using an HQL update statement now.
It's a little messy for my taste but it gets the job done.
Session.CreateQuery("update Risk set Closed = :completed where Id = :id")
.SetInt32("id", id)
.SetBoolean("completed", completed)
.ExecuteUpdate();

Related

How do I tell EF Core about two properties of the same type?

I have a set of models representing legal cases. One of the actions a user can do on a case is generate a document. This action is saved as a History entity, with an associated HistoryFile entity that contains the data about the file. Other actions may result in a History entity, with zero or multiple associated HistoryFile entities.
Cut-down versions of these two classes looks like this...
public class History {
public int Id { get; set; }
public ObservableCollection<HistoryFile> HistoryFiles { get; set; }
}
public class HistoryFile {
public int Id { get; set; }
public int HistoryId { get; set; }
public History History { get; set; }
}
The next requirement is that a user can pick up on a document that was previously generated and continue working on it. The bit where I'm getting stuck is that the HistoryFile entity needs a reference back to the History entity that held the previous version. This means that I need to add two lines of code to the HistoryFile entity...
public class HistoryFile {
public int Id { get; set; }
public int HistoryId { get; set; }
public History History { get; set; }
public int? PreviousHistoryId { get; set; }
public virtual History PreviousHistory { get; set; }
}
This means that there are two links from a HistoryFile to a History, one required one which is the parent History entity (via the History property) and an optional one via the PreviousHistory property.
I can't work out how to set this up for EF Core. As the code stands now, when I try to add a migration, I get the following error...
Cannot create a relationship between 'History.HistoryFiles' and 'HistoryFile.PreviousHistory' because a relationship already exists between 'History.HistoryFiles' and 'HistoryFile.History'. Navigation properties can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'HistoryFile.PreviousHistory' first in 'OnModelCreating'.
I tried adding the following to my DbContext...
builder.Entity<HistoryFile>(entity => {
entity.HasOne(hf => hf.History)
.WithMany(h => h.HistoryFiles)
.HasForeignKey(hf => hf.HistoryId)
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(hf => hf.PreviousHistory)
.WithMany(h => h.HistoryFiles)
.HasForeignKey(hf => hf.PreviousHistoryId)
.OnDelete(DeleteBehavior.Restrict);
});
...but it didn't make any difference.
Anyone able to tell me how I configure this so that EF Core knows that there are two distinct links between the two entities?
I'm using EF Core 5.0.7 in a .NET5 project in case it makes a difference.
Thanks
Got it.
I needed to add the following two lines to the History class...
public virtual ICollection<HistoryFile> HistoryFilesParentHistory { get; set; }
public virtual ICollection<HistoryFile> HistoryFilesPreviousHistory { get; set; }
...and then change the code I added to the DbContext to look like this...
builder.Entity<HistoryFile>(entity => {
entity.HasOne(hf => hf.History)
.WithMany(h => h.HistoryFilesParentHistory)
.HasForeignKey(hf => hf.HistoryId)
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(hf => hf.PreviousHistory)
.WithMany(h => h.HistoryFilesPreviousHistory)
.HasForeignKey(hf => hf.PreviousHistoryId)
.OnDelete(DeleteBehavior.Restrict);
});
This worked fine.

EF Core - how to model relation of Grandparent - Parent - Child on same model

Imagine a model of User that can have Parents and also can have Children.
How would you model such a case in EF Core?
I tried with something like that (pseudo-code)
public class User
{
public ICollection<Relation> Relations {get;set;}
public ICollection<User> Parents => Relation.Where(r => r.Relation == 'Parents')
public ICollection<User> Children => Relation.Where(r => r.Relation == 'Children')
}
public class Relaction
{
public User User1 {get;set;}
public Guid User1Id {get;set;}
public User User2 {get;set;}
public Guid User2Id {get;set;}
public Relation Relation {get;set;} //some enum or sth to indicate relation type
}
But in such modeling, I'm not able to force EF DbContext to fetch into User.Relations data where UserId is in User1Id and in User2Id.
Any idea?
What you are asking for is a classic many-to-many self relationship - (1) user as parent can have many users as children, and (2) user as child can have many users as parents.
Thus it is modelled with one main entity and one join (linking) entity similar to what you have shown. The linking entity does not need special indicator because the two FKs determine the role. i.e. lets change your example with more descriptive names:
public class User
{
public Guid Id { get; set; }
}
public class UserRelation
{
public User Parent { get; set; }
public User Child { get; set; }
public Guid ParentId { get; set; }
public Guid ChildId { get; set; }
}
Now, in pseudo code, given User user, then
user.Parents = db.Users.Where(u => user == u.Child)
user.Children = db.Users.Where(u => user == u.Parent)
EF Core 5.0+ allows you to hide the join entity (it still exists, but is maintained implicitly) and model the relationship with the so called skip navigations, which are the natural OO way of representing such relationship, e.g. the model becomes simply
public class User
{
public Guid Id { get; }
public ICollection<User> Parents { get; set; }
public ICollection<User> Children { get; set; }
}
This is all needed to create such relationship.
However the name of the join table and its columns by convention won't be what normally you would do - in this case, they would be "UserUser" table with "ParentsId" and "ChildrenId" columns.
If you use migrations and don't care about the names, then you are done and can safely skip the rest.
If you do care though, luckily EF Core allows you to change the defaults with fluent configuration (even though in a not so intuitive way):
modelBuilder.Entity<User>()
.HasMany(e => e.Parents)
.WithMany(e => e.Children)
.UsingEntity<Dictionary<string, object>>("UserRelation",
je => je.HasOne<User>().WithMany()
.HasForeignKey("ParentId").IsRequired().OnDelete(DeleteBehavior.Restrict),
je => je.HasOne<User>().WithMany()
.HasForeignKey("ChildId").IsRequired().OnDelete(DeleteBehavior.Restrict),
je => je.ToTable("UserRelations")
.HasKey("ParentId", "ChildId")
);
Here Dictionary<string, object> is the shared type EF Core will use to maintain the join entity in memory (change tracker). And is the most annoying thing in the above configuration since in a future they might change their minds and use different type (there are actually plans to do that in EF Core 6.0), so you'll have to update your mappings. Note that this does not affect the database design, just the memory storage type in EF Core change tracker.
So, because of that and the fact that in some scenarios it is better to work directly with the join entity, you could actually combine both approaches (explicit join entity and skip navigations) and get the best of both worlds.
To do than, you add the explicit entity and (optionally) navigations from/to it. The next is w/o collection navigations from User to UserRelation (with fully defined navigation you would need two ICollection<UserRelation> properties there):
public class User
{
public Guid Id { get; }
public ICollection<User> Parents { get; set; }
public ICollection<User> Children { get; set; }
}
public class UserRelation
{
public User Parent { get; set; }
public User Child { get; set; }
public Guid ParentId { get; set; }
public Guid ChildId { get; set; }
}
and required minimal fluent configuration
modelBuilder.Entity<User>()
.HasMany(e => e.Parents)
.WithMany(e => e.Children)
.UsingEntity<UserRelation>(
je => je.HasOne(e => e.Parent).WithMany(), // <-- here you would specify the corresponding collection nav property when exists
je => je.HasOne(e => e.Child).WithMany(), // <-- here you would specify the corresponding collection nav property when exists
je => je.ToTable("UserRelations")
);
The result is the same database model, but with different in-memory representation of the join entity and ability to query/manipulate it directly. Actually you can do the same with implicit entity, but in type unsafe way using sting names and object values which need to be cast to the appropriate type. This probably will improve in the future if they replace Dictionary<string, object> with some generic type, but for now explicit entity combined with skip navigations looks the best.
You can find (I guess better than mine) explanation of all this in the official EF Core documentation - Many-to-many and the whole Relationships section in general.

C# & Entity Framework Core creating entity many to many relation with itself as multiple parents and multiple children

I'm a relatively new programmer and I'm in the middle of creating Entities for Entity Framework Core for a project I'm currently on, but I'm having difficulty figuring out how to setup 1 specific entity named "Group".
The "Group" entity can contain zero to more groups as parents of it, and it can contain zero to more groups as its children.
i tried writing it the following way with the group class:
public class Group
{
public int GroupID { get; set; }
//Other properties here
public ICollection<SubGroup>? ParentGroups { get; set; }
public ICollection<SubGroup>? ChildGroups { get; set; }
}
with the SubGroup class looking like this:
public class SubGroup
{
public int ParentGroupID { get; set; }
public int ChildGroupID { get; set; }
public Group ParentGroup { get; set; }
public Group ChildGroup { get; set; }
}
alongside this piece of fluent API:
modelBuilder.Entity<SubGroup>().HasKey(sg => new { sg.ParentGroupID, sg.ChildGroupID });
modelBuilder.Entity<Group>().HasMany(g => g.ParentGroups).WithOne(sg => sg.ParentGroup).HasForeignKey(g => g.ParentGroupID).OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Group>().HasMany(g => g.ChildGroups).WithOne(sg => sg.ChildGroup).HasForeignKey(g => g.ChildGroupID).OnDelete(DeleteBehavior.Restrict);
The thought process in my head is that subgroups should show which groups are children/parents of which groups.
While i was able to generate a migration and update the database, i'm not entirely sure if this is how you're supposed to do it, so i've also thought about doing it the following way:
public class Group
{
public int GroupID { get; set; }
//Other properties here
public ICollection<Group>? ParentGroups { get; set; }
public ICollection<Group>? ChildGroups { get; set; }
}
alongside the following fluent API:
modelBuilder.Entity<Group>().HasMany(g => g.ChildGroups).WithMany(sg => sg.ParentGroups);
i want to ask anyone who's made this kind of relation before if i'm right or wrong in either case, and how it should be written. I hope anyone could help me here
Thanks in advance
EDIT: I completely forgot to mention in the first case that the only reason I could update the database there was because I added .OnDelete(DeleteBehavior.Restrict) to the end of the fluent API because it kept warning me with this:
Introducing FOREIGN KEY constraint 'FK_SubGroup_Group_ParentGroupID' on table 'SubGroup' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints
I want the group table rows to cascade and delete all subgroup relation rows associated with the deleted group without deleting the other end of the relation but don't know how in this case

How to model a collection of items where one of them is the active item?

I have a situation where an entity has a list of children that are inactive. Additionally, they have another "current/active" sub entity of the same type.
The following would be the ideal modeling, but I cannot figure out how to this with Entity Framework Core:
public class Customer
{
public Application CurrentApplication { get; set; }
public List<Application> PastApplications { get; set; }
}
public class Application
{
public bool IsCurrent { get; set; }
public Customer Customer { get; set; }
}
In the past, I've typically modeled it as so:
public class Customer
{
public Application CurrentApplication => AllApplications.Single(a => a.IsCurrent);
public List<Application> PastApplications => AllApplications.Where(a => !a.IsCurrent);
public List<Application> AllApplications { get; set; }
}
public class Application
{
public bool IsCurrent { get; set; }
public Customer Customer { get; set; }
}
However, I feel that this could lead to the possibility of another Application incorrectly being set as IsCurrent, thus breaking the .Single().
What's the suggested way to accomplish this from a DDD perspective? If that doesn't match up with what EF Core can do, what is a good practical suggestion?
I don't think that this is a DDD problem, rather a how to design a relational DB model and how to use EF Core question.
First you need to decide what is the relationship between Customers and Applications:
One-to-Many, that is, a Customer may have zero or more Applications, but an Application belongs to exactly one Customer. This scenerio is also called a master-detail relationship. The entity on the Many side (Application in this case) stores a reference (called a foreign key) to its owner (Customer).
Many-to-Many, that is, a Customer may have zero or more Applications, but an Application may belong to zero or more Customers, as well. This scenario is modelled using an extra "join" table (usually named something like CustomerApplication) so the problem is resolved to two One-to-May relationships in the end.
If there is only one active Application at a given time (per customer), the active application can be modelled using a One-to-One (Zero-to-One, to be precise) relationship between Customer and Application (with a foreign key on Customer's side). It can also be modelled using a flag field on Application as you tried but that's not as error-proof as a foreign key (but may have better performance, though).
The code you posted resembles rather a One-to-Many scenario, so I show an example for that case. Understanding the following, you can easily change it Many-to-Many if desired.
First let's define the entities:
public class Customer
{
public int Id { get; set; }
public int? CurrentApplicationId { get; set; }
public Application CurrentApplication { get; set; }
public ICollection<Application> Applications { get; set; }
}
public class Application
{
public int Id { get; set; }
public Customer Customer { get; set; }
}
The single interesting part is int? CurrentApplicationId. We need to explicitly define the foreign key for our Zero-to-Many relationship (more on this later). The nullable int (int?) tells EF that this field can be NULL.
EF is usually able to figure out the relationship mappings but in this case we need to explain them to it explicitly:
class DataContext : DbContext
{
// ctor omitted for brevity
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(customer =>
{
customer.HasMany(entity => entity.Applications)
.WithOne(relatedEntity => relatedEntity.Customer)
.OnDelete(DeleteBehavior.Cascade);
customer.HasOne(entity => entity.CurrentApplication)
.WithOne()
.HasForeignKey<Customer>(entity => entity.CurrentApplicationId);
});
}
public DbSet<Application> Applications { get; set; }
public DbSet<Customer> Customers { get; set; }
}
What's going on in the OnModelCreating method is called fluent API configuration. This topic and conventions is a must to understand and control how EF maps the entities to DB tables.
Based on the mapping EF is able to generate (see code-first migrations) the following DB schema:
CREATE TABLE [Customers] (
[Id] INTEGER NOT NULL
, [CurrentApplicationId] bigint NULL
, CONSTRAINT [sqlite_master_PK_Customers] PRIMARY KEY ([Id])
, FOREIGN KEY ([CurrentApplicationId]) REFERENCES [Applications] ([Id]) ON DELETE RESTRICT ON UPDATE NO ACTION
);
CREATE UNIQUE INDEX [IX_Customers_CurrentApplicationId] ON [Customers] ([CurrentApplicationId] ASC);
CREATE TABLE [Applications] (
[Id] INTEGER NOT NULL
, [CustomerId] bigint NULL
, CONSTRAINT [sqlite_master_PK_Applications] PRIMARY KEY ([Id])
, FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id]) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE INDEX [IX_Applications_CustomerId] ON [Applications] ([CustomerId] ASC);
Exactly what we wanted.
Now, how you query the active and inactive applications in this configuration? Something like this:
var customerId = 1;
using (var ctx = new DataContext())
{
var currentApplication = (
from customer in ctx.Customers
where customer.Id == customerId
select customer.CurrentApplication
).FirstOrDefault();
var pastApplications =
(
from customer in ctx.Customers
from application in customer.Applications
where customer.Id == customerId && customer.CurrentApplication != application
select application
).ToArray();
}
I suggest you to read through the acticles to be found here to get familiar with EF Core.
As for relational DB modelling this site seems a useful resource.
Using EFCore, the following would work:
public class Customer
{
[Key]
public int ID {get; set;}
//other properties
//Navigation Property
public virtual ICollection<Application> Applications{ get; set; }
}
public class Application
{
[Key]
public int ID {get; set;}
[ForeignKey("Customer")]
public int CustomerID{get; set;}
public DateTime ApplicationDate{get; set}
//other properties
public bool IsCurrent { get; set; }
}
I am assuming your are using Code First, so this will be the correct way to do your mappings.
After migrating and updating the context, you could use some backend code to always ensure that your most recent application returns as IsCurrent.
You would can then select your current application as follows:
private yourContext _context;
var yourContext = _context.Customers
.Include(m=>m.Application)
.Where(m=> m.isCurrent==false)
.Single(a=>a.);
return yourContext;
You would of course have to set up your constructor with the context etc

Mapping unusual table relationships

I wondered if anyone can advise me on how to resolve a problem with regards to using FluentAPI to map a couple of tables.
I have Parent table that has our key called ID
Then a Child table with two fields idA & idB.
The primary key in the parent table links to either idA or idB, not both.
public Parent()
{
this.ChildA = new HashSet<Child>();
this.ChildA = new HashSet<Child>();
}
public virtual ICollection<Child> ChildA { get; set; }
public virtual ICollection<Child> ChildB{ get; set; }
}
public Child()
public virtual Parent parent { get; set; }
}
There is much I can do about the relationship/table design because it is legacy and cannot be changed. Just need to understand the correct FluentAPI to use to account for this issue. Above example it what I envisaged would be needed along with something like...
modelBuilder.Entity<Child>().HasRequired<Parent>(p => p.parent).WithMany(q => q.childs).HasForeignKey(r => r.idA);
modelBuilder.Entity<Child>().HasRequired<Parent>(p => p.parent).WithMany(q => q.childs).HasForeignKey(r => r.idB);
I believe I was able to get the correct mapping you are looking for. I added naviation properties to the POCO which allows Entity Framework to know how to use the foreign keys in code.
public class Child
{
public int Id { get; set; }
public virtual Parent ParentA { get; set; }
public virtual Parent ParentB { get; set; }
public Child() { }
}
To map these navigation properties to you already existing foreign key columns, I used the FluentAPI Map method.
modelBuilder.Entity<Child>().HasRequired<Parent>(p => p.ParentA).WithMany(q => q.ChildA).Map(m => m.MapKey("idA")).WillCascadeOnDelete(false);
modelBuilder.Entity<Child>().HasRequired<Parent>(p => p.ParentB).WithMany(q => q.ChildB).Map(m => m.MapKey("idB")).WillCascadeOnDelete(false);
With this, I have indicated ParentA populates the ChildAcollection, and ParentB populates the ChildB collection. The Map method is what allows me to map to your already existing FKs, and I don't have to include them with the POCO as a property.
Note that each POCO that maps to a table must have a primary key. Does your already existing child table have a PK? If not, you may have some further trouble. I recommend reading this SO post about it. Entity Framework: table without primary key

Categories