Fluent NHibernate HasMany Collection Problems - c#

Update:
It appears that changing my mapping from Cascade.All() to Cascade.AllDeleteOrphan() fixes most of my issues. I still have to explicitly set the Company property on the OperatingState, which seems unnecessary as it's being added to the Company entity, but at least I can work with that during an update. I still need to test that with a create.
If any one can explain that, that would be a big help.
Update 2: After playing with it some more, it appears I don't always have to specify the parent entity.
Original Post
I have 2 related entities
public class Company {
//... fields
public virtual IList<OperatingState> OperatingStates { get; set; }
}
public class OperatingState {
public virtual Company Company { get; set; }// Mapped on CompanyID
public virtual string State { get; set; }
}
And they are mapped like this:
public class CompanyMap : ClassMap<Company> {
public CompanyMap() {
//... fields
HasMany(x => x.OperatingStates)
.Cascade.All()
.Table("OperatingState");
}
}
public class OperatingStateMap : ClassMap<OperatingState> {
public OperatingStateStateMap() {
Id(x => x.ID);
References(x => x.Company);
Map(x => x.State);
}
}
So all is well until I try to update Company with new Operating States
Company company = _repo.GetSingle(123);
company.OperatingStates.Clear();
foreach(string state in form["OperatingStates"].Split(',')) {
company.OperatingStates.Add(new OperatingState(state));
}
_repo.Save(company); // calls ISession.SaveOrUpdate
It bombs out with:
Cannot insert the value NULL into
column 'CompanyID', table
'ConsumerCartel.dbo.CompanyOperatingState';
column does not allow nulls. INSERT
fails. The statement has been
terminated.
However, if I make 2 changes it kind of works
Company company = _repo.GetSingle(123);
// don't clear the list this time;
foreach(string state in form["OperatingStates"].Split(',')) {
OperatingState os = new OperatingState(state);
// explicitly setting the company
os.Company = company;
company.OperatingStates.Add(os);
}
_repo.Save(company); // calls ISession.SaveOrUpdate
Which will add the new states in addition to the old ones, which is not what I want. However, even when explicitly setting the company (which I shouldn't have to do when it's added to a mapped list?) it doesn't work if the list is cleared.
This code has worked on other entities, but not on this one, so I think this should work as written. What am I doing wrong?

Have you tried using Inverse()?
HasMany(x => x.OperatingStates)
.Inverse()
.Cascade.All()
.Table("OperatingState");

Related

EF Core - EntityTypeBuilder One to Many Relationship?

I am a bit confused, about how to do relationships in EF Core 2.1. I been doing it the fluent way.
I have
public class Employee : IdentityUser
{
public int BranchId { get; set; }
public Branch Branch { get; set; }
}
public class Branch
{
public int Id { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
public ICollection<Employee> Employees { get; set; }
}
public class EmployeerConfig : IEntityTypeConfiguration<Employee>
{
public void Configure(EntityTypeBuilder<Employee> builder)
{
builder.ToTable("Employees");
}
}
public class BranchConfig : IEntityTypeConfiguration<Branch>
{
public void Configure(EntityTypeBuilder<Branch> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Id).ValueGeneratedOnAdd();
builder.HasMany(x => x.Employees);
builder.ToTable("Branches");
}
}
All the examples I seen mostly to use dbcontext model builder, but that way is no longer needed as you can now use split it up as I done.
I done my relationships 2 ways first for Company and Branch I don't even specify the relationship yet went I build my db it knows, however when I try to do that with Employee and Branch the relationship was not formed.
This made me add builder.HasMany(x => x.Employees) in the branch config and now the relationship works, however I am not sure if I have specify something in the Employee area to make to complete?
I also don't know if I need to still add virtual to my collections anymore and why if I don't use ToTable() and build my db, all the table names are abbreviated, I thought that was automatic.
EmployeerConfig is fine you can add key if yoy want
For Branch Config add this line hope this'll help you.
builder.HasMany(x => x.Employee).WithOne(b => b.Branch).HasForeignKey(b => b.BranchId)
.OnDelete(DeleteBehavior.Cascade);
There seem to be multiple ways to do this. You can specify the relationship in EmployeeConfig.Configure or in BranchConfig.Configure, or in the DbContext's OnModelCreating method. I haven't tested to see what kind of behavior you get from specifying it in multiple places, but it's probably fine as long as it's consistent. However, in order to make maintenance easier, you probably want to specify it in only one place. I think the natural place is the DbContext, as this is a piece of information related to multiple entities, not just one. If you'd prefer to put it in one of the configuration files, I would suggest that putting it in the parent entity seems more natural.
Here is how you would specify it in EmployeeConfig.Configure:
builder.HasOne<Branch>(e => e.Branch).WithMany(b => b.Employees).HasForeignKey(e => e.BranchId);
That says that the Employee has one Branch, that the Branch has many Employees, and that the dependent entity (Employee) has a foreign key pointing back at the Branch, called BranchId.
Here is how you would specify it in BranchConfig.Configure:
builder.HasMany<Employee>(b => b.Employees).WithOne(e => e.Branch).HasForeignKey(e => e.BranchId);
Which says the same thing: the Branch has many Employees, each of which has one Branch, and that the dependent entity (Employee) has a foreign key called BranchId.
Here is how you would specify it in DbContext.OnModelCreating:
modelBuilder.Entity<Branch>().HasMany<Employee>(b => b.Employees).WithOne(e => e.Branch).HasForeignKey(e => e.BranchId);
or
modelBuilder.Entity<Employee>().HasOne<Branch>(e => e.Branch).WithMany(b => b.Employees).HasForeignKey(e => e.BranchId);

Strategy to persist entity's associated objects with NHibernate

I am working on a rather larger application that tries to follow a layered architecture pattern. On DBAL I use fluently configured NHibernate. Database objects sometimes have associations like these:
public class HeaderDbo
{
public HeaderDbo()
{
Details = new List<DetailDbo>();
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<DetailDbo> Details { get; set; }
}
public class DetailDbo
{
public virtual int Id { get; set; }
public virtual string DetailName { get; set; }
public virtual HeaderDbo Header { get; set; }
public virtual RelevantObjectDbo RelevantObject { get; set; }
}
public class RelevantObjectDbo
{
public virtual int Id { get; set; }
public virtual string RelevantText { get; set; }
}
Mapping is as follows:
public class HeaderDboMap : ClassMap<HeaderDbo>
{
public HeaderDboMap()
{
Table("Header");
Id(x => x.Id).Column("Id");
Map(x => x.Name);
HasMany(x => x.Details)
.Inverse()
.Cascade.All();
}
}
public class DetailDboMap : ClassMap<DetailDbo>
{
public DetailDboMap()
{
Id(x => x.Id).Column("Id");
Table("Detail");
Map(x => x.DetailName);
References(x => x.Header).Column("HeaderId");
References(x => x.RelevantObject).Column("RelevantObjectId")
.Cascade.SaveUpdate(); //??
}
}
public class RelevantObjectDboMap : ClassMap<RelevantObjectDbo>
{
public RelevantObjectDboMap()
{
Id(x => x.Id).Column("Id");
Table("RelevantObject");
Map(x => x.RelevantText);
}
}
Now, there are application domain entities that the DBOs are mapped to which do not necessarily reflect the database structure one-to-one. For example, Header might stay header, but Detail would, say, form itself from DetailDbo and parts of RelevantObjectDbo. Then the application does its thing over the entities - some transformation of Detail happens, which now needs to be persisted.
Suppose I only affected parts of the Detail entity which need to go into Detail table and don't affect RelevantObject table in any way. It might be a wrong way to think about the model but we also need to be practical about how persisting works. So, say, I would like to only have NHibernate update the Detail table without "touching" anything on the RelevantObject table. This is exactly the question, actually: how do I achieve that?
In reality, of course, the DB model is far bigger and more complicated and so is the application logic. There could be a part of BL that does not deal with the RelevantObject part of the data at all, so even though DBOs are loaded from the db fully, not all the data finds its way into the app model. But to persist the data back to the database - it seems that I need to hydrate the DB model fully and it's not always practical. So, how can we instruct NHibernate to "not touch" RelevantObject - in other words, not update dbo.Detail.RelevantObjectId?
I tried applying different Cascade options to DetailDbo.RelevantObject property, but if it stays at null, NHibernate always wants to set RelevantObjectId to NULL - I suppose, rightfully so.
I don't understand how I can write changes to the data that is relevant to my "part" of the BL without having to load and save half of my database through all the associations.
Thank you!
How are you performing these updates?
If you are not modifying anything on RelevantObject NHibernate will not send an update for that table. For example:
var header = session.Get<HeaderDbo>(1);
header.Details.First().DetailName = "Whatever";
session.Flush();
Should not cause an update to be issued to the RelevantObject table.

Nhibernate prevent update before delete

I have a self referencing object Cycle:
public class Cycle
{
public Cycle()
{
ParentCycle = this;
ChildCycles = new List<Cycle>{this};
}
public virtual Guid Id { get; set; }
public virtual Cycle ParentCycle { get; set; }
public virtual IList<Cycle> ChildCycles { get; set; }
public virtual int Version { get; set; }
}
With the following mapping:
public class CycleMap : ClassMap<Cycle>
{
public CycleMap()
{
Table("Cycle");
Id(x => x.Id).Column("CycleID");
References(x => x.ParentCycle).Column("ParentCycleID").Not.Nullable();
HasMany(x => x.ChildCycles).KeyColumn("ParentCycleID").Cascade.AllDeleteOrphan().Inverse();
Version(x => x.Version);
}
}
I run the following test code:
var parentCycle = new Cycle();
session.Save(parentCycle);
session.Flush();
session.Delete(parentCycle);
session.Flush();
The creation of the cycle works, but when I try to delete the cycle, I have 2 problems:
Before the delete statement, NHibernate does an update statement to set the ParentCycle to NULL. However this property is not nullable, because if the cycle does not have a parent it references to himself.
When I make the property nullable another problem occurs. Nhibernate does the update which now succeeds but during this update it does not increment the version number. This is a problem for our auditing system. With every update the version should be incremented.
I'm wondering if anyone else had the same problems, the ideal solution would be to stop the update because it is unnecessary. But I cannot seem to achieve this.
Github
I made my test code available on GitHub
It should be easily possible, with setting called inverse="true"
public CycleMap()
{
...
HasMany(x => x.ChildCycles)
.KeyColumn("ParentCycleID")
// the setting, instructing NHibernate that other end will care...
.Inverse()
.Cascade.AllDeleteOrphan();
...
The point is, if NHibernate knows, that the other end (the inversed one) is taking care about the relationship, it does not have to issue
update (with null)
delete
The deep description of the inverse="true" could be found here (I would really suggest to read through, it is really well structured overview, still valid for NHibernate):
inverse = “true” example and explanation by mkyong

Entity Framework Parent Child - Child refers to parent more than once

I have a situation with EF5 and a complex object. The basics is that I have a parent to child complex object, but the child refers back to the parent, more than once. I have tried various options but am not finding a solution that answers the question. The closest I have got is this answer (option 2c)
My model looks like below:
public class StaffMember
{
public virtual Guid StafId { get; set; }
// other props
// List of leave apps (Approved/Cancelled etc)
public virtual ICollection<StaffLeaveApp> LeaveApps { get; set; }
}
//Staff Leave Application
public class StaffLeaveApp
{
public virtual Guid LeaveId { get; set; }
public virtual Guid StaffId { get; set; }
// other props...
// Leave approved by? (2 approvals required)
public virtual StaffMember ApprovedBy1 { get; set; }
public virtual StaffMember ApprovedBy2 { get; set; }
}
my mappings look like this
public class StaffMap : EntityTypeConfiguration<StaffMember>
{
public StaffMap()
{
ToTable("tblStaffMembers");
HasKey(x => x.StaffId);
// other mappings...
HasMany(x => x.LeaveApps);
}
}
public class StaffLeaveAppMap: EntityTypeConfiguration<StaffLeaveApp>
{
public StaffLeaveAppMap()
{
ToTable("tblStaffMembersLeaveApps");
HasKey(x => x.LeaveId);
Property(x => x.StaffId).HasColumnName("StaffID");
//Child Relationships
HasOptional(x => x.ApprovedBy1).WithMany().Map(m => m.MapKey("LeaveApprovedBy1"));
HasOptional(x => x.ApprovedBy2).WithMany().Map(m => m.MapKey("LeaveApprovedBy2"));
}
}
Table (sorry, no images)
StaffID uniqueidentifier (FK - tblStaffMembers)
LeaveID uniqueidentifier (PK)
LeaveApprovedBy1 uniqueidentifier (FK - tblStaffMembers)
LeaveApprovedBy2 uniqueidentifier (FK - tblStaffMembers)
The business rule says: a staff member has "many" leave applications and a leave application belongs to a single staff member. Each application requires the approval of 2 staff members (managers) before it is "approved".
How would I map the above using EF so that a single staff member has a "many" leave applications (working already) and a leave application is mapped back to a staff member whom approved it for the first approval and then again for the seond approval. If I use the one mapping for "ApprovedBy1" only then EF is happy and all works as expected. The moment I add the second approval mapping EF struggles with the SQL queries it generates.
I am not sure how to tell EF to map back to the StaffMembers table to specify whom approved the application at level 1 and whom approved it at level 2. It almost ends up being a many to many relationship.
Any ideas?
You are looking for the inverse property, which is the property at the other end of an association. In EF, there are two way to mark a property as inverse.
Data annotations: InversePropertyAttribute.
Fluent mapping
As you already have fluent mapping I'll show you how you'd do it there:
HasOptional(x => x.ApprovedBy1).WithMany(x => x.LeaveApps)
.HasForeignKey(s => s.StaffId);
HasOptional(x => x.ApprovedBy2).WithMany()
.Map(m => m.MapKey("LeaveApprovedBy2"));
The HasOptional(...).WithMany(...) pair is a way to map inverse properties. Coming from the other side you can use e.g. HasMany(....).WithOptional(...).

Mapping references to companion objects with fluent-nhibernate

I've got the following basic domain model for my MVC website accounts:
public class Account
{
public Account()
{
Details = new AccountDetails( this );
Logon = new LogonDetails(this);
}
public virtual int Id { get; private set; }
public virtual AccountDetails Details { get; set; }
public virtual LogonDetails Logon { get; set; }
...
}
public class AccountDetails
{
// Primary Key
public virtual Account Account { get; set; }
public virtual DateTime Created { get; set; }
...
}
public class LogonDetails
{
// Primary Key
public virtual Account Account { get; set; }
public virtual DateTime? LastLogon { get; set; }
...
}
Both AccountDetails and LogonDetails use a mapping like this:
public class AccountDetailsOverride : IAutoMappingOverride<AccountDetails>
{
public void Override( AutoMap<AccountDetails> mapping )
{
mapping
.UseCompositeId()
.WithKeyReference( x => x.Account, "AccountId" );
mapping.IgnoreProperty( x => x.Account );
}
}
I've split the account details and logon details into separate models since I rarely need that information, whereas I need the userid and name for many site operations and authorization. I want the Details and Logon properties to be lazy-loaded only when needed. With my current mapping attempts I can get one of two behaviors:
# 1 Create table and load successfully, cannot save
Using this mapping:
public class AutoOverride : IAutoMappingOverride<Account>
{
public void Override( AutoMap<Account> mapping )
{
mapping.LazyLoad();
mapping
.References( x => x.Details )
.WithColumns( x => x.Account.Id )
.Cascade.All();
mapping
.References( x => x.Logon )
.WithColumns( x => x.Account.Id )
.Cascade.All();
}
}
The tables are generated as expected. Existing data loads correctly into the model, but I can't save. Instead I get an index out of range exception. Presumably because Account.Details and Account.Logon are both trying to use the same db field for their reference (The Account.Id itself).
#2 Table includes extra fields, does not save properly
Using this mapping:
public class AutoOverride : IAutoMappingOverride<Account>
{
public void Override( AutoMap<Account> mapping )
{
mapping.LazyLoad();
mapping
.References( x => x.Details )
.Cascade.All();
mapping
.References( x => x.Logon )
.Cascade.All();
}
}
I get a table with a separate field for Details_id and Logon_id but they are null since the value of Details.Account.Id is null when the Account is persisted. So, attempting to Session.Get the account results in Details and Logon being null. If I save Account twice, the table is updated correctly and I can load it.
Help...
There must be a way of mapping this hierarchy and I'm missing something simple. Is there a way to help nhibernate pick the proper field (to solve #1) or to have it update dependent fields automatically after save (to solve#2)?
Thanks for any insight you folks can provide.
If I'm understanding your model and desired behavior, what you have is actually a one-to-one relationship between Account and AccountDetails and between Account and LogonDetails. References creates a many-to-one relationship, so that could be your problem; try HasOne instead.
That said, for this and other reasons, I avoid one-to-ones unless absolutely necessary. There may be more than what you're showing, but is it worth the headache and ugly model to avoid loading two DateTime fields?
Finally, and this is somewhat speculation since I have not tested this functionality, NHibernate 2.1's (which FNH has switched to as supported version) mapping XML schema defines a lazy attribute for property elements. The 1.0 release of FNH (should be in the next week or two) will support setting this attribute. As I said, I have not tested it but it would seem that this would allow you to lazy load individual properties which is exactly what you want.

Categories