NHibernate, not saving when inverse is set to true - c#

I have what seems to be a very simple question but I can't seem to figure it out. If I have a one to many relationship between [Member] and [Mail]
If I take off inverse it all works, it's just when I call instance.inverse that it doesnt save.
public class Mail : Entity
{
public virtual Member Receiver
{
get; set;
}
public virtual string Subject
{
get;
set;
}
public virtual string Body
{
get; set;
}
}
public class Member : Entity
{
public Member()
{
Mail = new List<Mail>();
}
[Cascade(Enums.CascadeOptions.All)]
public virtual IList<Mail> Mail
{
get; set;
}
}
Inverse is true on a one to many (through an automapping convention)
public class HasManyConvention : IHasManyConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IOneToManyCollectionInstance instance)
{
instance.Key.Column(instance.EntityType.Name + "ID");
instance.Inverse();
}}
I try to add a mail item to a member through:
mail.Receiver = receiver;
receiver.Mail.Add(mail);
_repository.SaveOrUpdate(mail);
FlushSessionAndEvict(mail);
Now what's weird is the mail item is saved to the database, there is a foreign key called 'MemberId' which SHOULD have the foreign key to the parent member table and a column called ReceiverFk which is according to my convention but shouldn't be there if there is already a foreign key relationship with MemberId. When I retrieve the user it doesn't have the mail item attached to it. If I take off inverse everything works, the foregin key is saved in MemberId.
I don't want to take off inverse though because it entails another update call to the database. What can I do?
Thanks

Inverse = true means "ignore this relationship because I'm saving it from the other side". So you need to cascade the save on the collection side as you are attempting to do. I suspect that the problem is that you're using Fluent NHibernate for the mapping, but the cascade is annotated via NHibernate mapping attributes. Fluent NHibernate doesn't read the attributes as far as I know. So you need some code to mix NHibernate mapping attributes into the Configuration along with Fluent NHibernate or use an explicit ClassMap or Fluent NHibernate convention to override the cascade relationship on the Member.Mail collection.

Try adding Cascade.All to your mapping, it normally solves most of my NHibernate problems =)

Related

Entity Framework foreign key references not mapping

Using EF6 and .NET 4.6.1. I know this somewhat is a duplicate (I've looked at this stack overflow post) but hear me out. My case is different and I have tried to get it to work using their solutions with mine but it didn't work. So no this isn't actually a duplicate. It's another issue altogether and I haven't found a post that really helps me on this topic.
I am trying to map relationships between 3 models; Employee, Position and Employment. I want a one-to-many between Employment and Position (employments map to one position) and a one-to-one between Employment and Employee.
public class Employment
{
public int EmploymentID { get; set;}
...
public Position Position { get; set; }
public Employee Employee { get; set; }
}
public class Position
{
public int PositionID { get; set;}
...
[InverseProperty("Position")]
public ICollection<Employment> Employments { get; set; }
}
public class Employee
{
public int EmployeeID { get; set;}
...
[InverseProperty("Employee")]
public Employment Employment { get; set; }
}
However, when I try to run this with DbContext automapping, it fails and says it can't find the relationships. I've tried multiple combinations of data annotations like setting inverseproperties and foreignkey("____ID") on some of them but haven't been able to get it to work.
I did also try adding virtual keywords in for some of the but that didn't do anything either.
I'd rather not use FluentAPI as I want to let the auto mapper do as much as possible with this. It's not complicated a problem enough to warrant manually mapping it with FluentAPI (At least in my opinion it isn't. Maybe I'm wrong).
What data annotations do I need? I've looked at this stack overflow post and various articles on entityframeworktutorial.net trying to apply their solutions to my case. But haven't gotten anything to work.
The failing line is here:
using (EmploymentContext ctx = new EmploymentContext())
{
Position pos = new Position()
{
PositionID=1,
Name="General Manager"
};
ctx.Positions.Add(pos); // Failing here
ctx.SaveChanges();
}
and the error message is:
'Unable to determine the principal end of an association between the types 'Ianmann.Hr.DataAccess.Employment.Employee and Ianmann.Hr.DataAccess.Employment.Employment. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
I don't know why you insist on using data annotations. Everything you can do with data annotation can be done with the Fluent API, but the opposite is not true. Also, and especially with relationships, the data annotations are not intuitive and error prone.
In the concrete case the problem is with the one-to-one relationship (btw, the error message should contain that information). It's because EF cannot determine the principal and the dependent of the relationship when the ends of the relationship are both optional (as in your case) or both required. So one way to resolve it is to mark the principal by making the navigation property required:
public class Employment
{
public int EmploymentID { get; set; }
...
public Position Position { get; set; }
[Required] // <--
public Employee Employee { get; set; }
}
The InverseProperty is redundant (not needed) in this case.
The same can be achieved more intuitively with fluent API:
modelBuilder.Entity<Employment>()
.HasRequired(e => e.Employee)
.WithOptional(e => e.Employment);
But please note that while either way will resolve the issue in question, the resulting design will be the so called Shared Primary Key association, where EmploymentID is both PK and also FK to Employee. In case you want a separate FK property / column, then fluent API is a must as it cannot be done via data annotations:
modelBuilder.Entity<Employment>()
.HasRequired(e => e.Employee)
.WithOptional(e => e.Employment)
.Map(m => m.MapKey("EmployeeID"));

Entity Framework - Multiple 1 to 0..1 relationships using the same Key

I've read as many posts as I can on this topic but none of the solutions I have tried seem to work. I have an existing database and created a new Code First From Existing Database project.
I have a base table called Thing. Every object has a record in this table using Id as the Unique Primary Key. Each other object inherits from this but they use the same Id in the child tables without using a new Identity column in the sub tables. Effectively giving each 'Thing' a unique Id:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Car
{
public int Id { get; set; }
//other properties
}
public class Person
{
public int Id { get; set; }
//other properties
}
public class Color
{
public int Id { get; set; }
//other properties
}
Every new record first creates an item in 'Thing' and then using that Id value creates a new record in its respective table, creating multiple 1 to 0..1 relationships where the Id field on the derived tables is also the FK to Thing.
Thing 1 to 0..1 Car
Thing 1 to 0..1 Person
Thing 1 to 0..1 Color
and so on
I have tried many different Data Annotation and Fluent API combinations but it always comes back to the same error:
'Unable to retrieve metadata for Model.Car'. Unable to determine the principal end of association between the types 'Model.Thing' and 'Model.Car'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
I did manage to get past this error by using virtual with the inverse annotation and setting the Id field to be Key and ForeignKey, but then the message jumps to Person. If you then set it up the same as Car the message reverts back to Car.
It seems I could go back and create a normal Foreign Key to each child table, but that is a lot of work and I am sure it is possible to get this working somehow. Preferably using fluent API.
If you are going to use Data Annotations, you need to declare the PK of the dependent entity as FK too:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Car Car{get;set;}
}
public class Car
{
[Key,ForeignKey("Thing")]
public int ThingId { get; set; }
//other properties
public virtual Thing Thing{get;set;}
}
And if you are going to use Fluent Api (remove the attributes from your model), the configuration would be like this:
modelBuilder.Entity<Car>().HasRequired(c=>c.Thing).WithOptional(t=>t.Thing);
Based on the multiplicity that is specified, it only makes sense for Thing to be the principal and Car to be the dependent, since a Thing can exist without a Car but a Car must have a Thing.
As you can see you don't need to specify that ThingId is the FK of this relationship.This is because of Entity Framework’s requirement that the primary key of the dependent be used as the foreign key. Since there is no choice, Code First will just infer this for you.
Update
Reading again your question I think you are trying to create a hierarchy. In that case you could use the Table per Type (TPT) approach.

How to expose Foreign Key property to existing entity having navigational property using EF6 Code First

I have an entity which is already being used with an underlying database, and it was created with just the navigational property to an optional entity (1:0..1). So by default conventions, EF created a nullable foreign key column in the DB and gave it the "MyProp_Id" name with underscore, according to that convention.
Now, I wish to expose that foreign key as a property on the entity, because it will make certain scenarios easier for me. I don't want to rename/change the underlying foreign key column in the DB (the MyProp_Id one). In fact, there shouldn't be any underlying DB updates, I just want to expose that FK on the entity. A code sample to clarify:
public class MyEntityA
{
public long Id { get; set; }
//public long? MyOptionalEntityB_Id { get; set; } <== this is what I am trying to add
//public long? MyOptionalEntityBId { get; set; } <== this didn't work either
public MyEntityB MyOptionalEntityB { get; set; }
}
I've tried just simply adding the "MyOptionalEntity_Id" property as property on the entity, hoping that EF would "automagically" see that because the names are the same, it would just map and be happy. NO DICE.
Then I tried to name my property "MyOptionalEntityId" (no underscore), but still NO DICE.
Then I tried adding an explicit mapping configuration to say:
this.Property(p => p.MyOptionalEntityId).HasColumnName("MyOptionalEntity_Id");
NO DICE
Is there a way to do this? Is this clear and make sense?
Try adding foreign key attribute.
public long? MyOptionalEntityB_Id { get; set; }
[ForeignKey("MyOptionalEntityB_Id")]
public MyEntityB MyOptionalEntityB { get; set; }
Or using fluent api.
modelBuilder.Entity<MyEntityA >()
.HasOptional(x => x.MyOptionalEntityB)
.WithMany().HasForeignKey(x => x.MyOptionalEntityB_Id);
// ^^^ -> if MyEntityB has collection of MyEntityA, mention it

Cascadable one-to-one, required:required relationship with EF

I have a Video class and a MediaContent class that are linked by a 1-1, required:required relationship: each Video must have exactly 1 associated MediaContent. Deleting a MediaContent object must result in the deletion of the associated Video object.
Using the fluent API, the relationship can be modeled as follows:
modelBuilder.Entity<Video.Video>()
.HasRequired(v => v.MediaContent).WithRequiredPrincipal(mc => mc.Video)
.WillCascadeOnDelete(true);
When adding a migration to reflect this change in the database, this is how the relationship gets transcribed in terms of foreign keys:
AddForeignKey("MediaContents", "MediaContentId", "Videos", "VideoId", cascadeDelete: true);
Updating the database, I get the following error:
Cascading foreign key 'FK_MediaContents_Videos_MediaContentId' cannot be created where the referencing column 'MediaContents.MediaContentId' is an identity column.
Dropping the WillCascadeOnDelete(true) property removes the error, but I'm not sure I understand why. Shouldn't the error appear whether or not cascading is turned on? The way I understand the problem, the error comes from the fact that the generation of VideoId and MediaContentId is handled by auto-increment (or by whatever the id generation strategy is), potentially contradicting the foreign key constraint. But I can't see what this has to do with delete-cascading...
What am I missing? More generally, how would you go about modeling a cascadable one-to-one, required:required relationship with EF?
I avoid the modelBuilder cruft approach and use simple POCOs and attributes generally - which you can use to accomplish your goals like so:
public class Video
{
public int Id { get; set; }
// Adding this doesn't change the db/schema, but it is enforced in code if
// you try to add a Video without a MediaContent.
[Required]
public MediaContent MediaContent { get; set; }
}
public class MediaContent
{
[ForeignKey("Video")]
public int Id { get; set; }
public Video Video { get; set;}
}

EF4.1 (code first) - How to specify a composite relationship

In Linq to SQL I could specify a relationship that didn't have to depend on the foreign keys and pks existing in the database, useful for creating composite relationships like this:
public class Equipment_CableNormalised
{
...
[Association(ThisKey = "EquipmentId,PortNumber", OtherKey = "EquipmentId,PortNumber", IsForeignKey = false)]
public List<EquipmentPort> EquipmentPorts
{
get; set;
}
}
This then generated the sql similar to " .. join EquipmentPorts EP on EP.EquipmentId = blah and EP.PortNumber = Blah".
Can I do the same sort of thing in EF4.1 (using annotations or fluent api)? I know you can specify composite keys and use the [Keys] and [ForeignKeys] attributes, but this relationship doesn't map to keys...
How does the sample relation from your code works? I expect that EquipementId must be either PK or unique key (not supported in both L2S and EF) on one side because otherwise the relation could not exist (both one-to-one and one-to-many demands unique principal). Once it is PK on one side the port number is redundant.
Code first allows only mapping to keys. If you have existing database you can cheat it in your model and map new relations in the same way as you would map existing but you still have to follow simple rule - properties in principal are primary keys, properties in dependent entity are mapped as foreign keys.
If you want EF to generate DB for you, you will always have all relations in the database.
Use HasKey http://www.ienablemuch.com/2011/06/mapping-class-to-database-view-with.html
Either use HasKey, put this on OnModelCreating
 modelBuilder.Entity<SalesOnEachCountry>().HasKey(x => new { x.CountryId, x.OrYear });   
Or use Key Column Order
public class SalesOnEachCountry
{       
    [Key, Column(Order=0)] public int CountryId { get; set; }
    public string CountryName { get; set; }
    [Key, Column(Order=1)] public int OrYear { get; set; }
     
    public long SalesCount { get; set; }     
    public decimal TotalSales { get; set; }
}
Regarding your question about foreign key, I haven't yet tried the pure code(OnModelCreating) approach, perhaps you can just put two ForeignKey attribute on child class itself, might need to put Column Order too.
This could be the answer composite key as foreign key
That answer confirms my hunch that you could put two ForeignKey attributes on child class itself.

Categories