NHibernate One-To-One mapping using Mapping by Code - nhibernate

I have an class structure like this:
public class BaseEntity
{
public Guid Id { get; set; }
}
// CREATE TABLE Project (Id, Name)
public class Project : BaseEntity
{
public ProjectProperties Properties { get; set; }
public string Name { get; set; }
}
// CREATE TABLE ProjectProperties (Id, Markup)
// ForeignKey from ProjectProperties.Id -> Project.Id
public class ProjectProperties : BaseEntity
{
public int Markup { get; set; }
}
What is the correct way to map this using NH 3.2 and Mapping By Code? I can't find examples where the 1:1 relationship is through the PKs.

you can use Join since the primary keys match. it doesn't even need an own Id because it is dependant from Project
public class ProjectProperties
{
public int Markup { get; set; }
}
// in ProjectMapping
Join("ProjectProperties", join =>
{
join.Key("Id");
join.Component(x => x.ProjectProperties, c =>
{
c.Property(x => x.Markup);
}
});

I think you should use this code.
OneToOne(x => x.Properties,
x => x.PropertyReference(typeof(ProjectProperties).GetProperty("Properties")));

Related

Entity Framework Code First Many-to-Many relationship and inheritance

Forgive me if this question has been answered somewhere, I have been having a hard time finding a solution for this problem.
I am trying to set up EF Code First on an MVC4 Project. I have a User and Customer that both inherit from Person. I then have a Template object that has a Many-to-Many relationship with Customer and a One-to-Many relationship with User. Here is how I have it set up:
MODELS
public class Person
{
[Key]
public int PersonID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string FullName
{
get
{
return String.Format("{0} {1}", FirstName, LastName);
}
}
public string Email { get; set; }
public virtual List<Template> Templates { get; set; }
}
public class User : Person
{
....
}
public class Customer : Person
{
....
}
public class Template
{
public int TemplateId { get; set; }
public string TemplateName { get; set; }
public virtual List<Customer> Customers { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public virtual User User { get; set; }
}
CONTEXT
public class ProjectContext : DbContext
{
public ProjectContext()
: base("name=ProjectDB")
{
}
public DbSet<Template> Templates { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Template>()
.HasMany(x => x.Customers)
.WithMany(x => x.Templates)
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer")
);
}
}
If I remove the Person DBSet out of the context this works fine but sets up TPT inheritance. I would like to use TPH inheritance, but when I enable migrations with the Person DBSet in the context it chokes:
NavigationProperty 'Templates' is not valid. Type 'MvcProject.Models.Customer' of FromRole 'Template_Customers_Target' in AssociationType 'MvcProject.Models.Template_Customers' must exactly match with the type 'MvcProject.Models.Person' on which this NavigationProperty is declared on.
Where am I going wrong here?
You cannot inherit navigation properties from a base entity. They always must be declared in the class the other end of the relationship is refering to.
Template.Customers is refering to Customer (not to Person), hence the inverse navigation property Templates must be declared in Customer (not in Person)
Template.User is refering to User (not to Person), hence the inverse navigation property Templates must be declared in User (not in Person)
So, basically you must move the Templates collection from Person into both derived classes:
public class Person
{
// no Templates collection here
}
public class User : Person
{
//...
public virtual List<Template> Templates { get; set; }
}
public class Customer : Person
{
//...
public virtual List<Template> Templates { get; set; }
}
Then you can define the two relationships with Fluent API like so:
modelBuilder.Entity<Template>()
.HasMany(t => t.Customers)
.WithMany(c => c.Templates) // = Customer.Templates
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer"));
modelBuilder.Entity<Template>()
.HasRequired(t => t.User)
.WithMany(u => u.Templates) // = User.Templates
.HasForeignKey(t => t.UserId);
Change your HasMany selector to People:
modelBuilder.Entity<Template>()
.HasMany(x => x.People) // here
.WithMany(x => x.Templates)
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer")
);

My custom ForeignKeyConvention is resulting in two foreign keys being created instead of one

I am trying to create my own foreign key convention that will name the FK in "FK_SourceTable_TargetTable" format.
However, when I run it I end up with two foreign keys instead of one.
My custom foreign key convention looks like this:
public class OurForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if (property == null)
return string.Format("FK_{0}Id", type.Name); // many-to-many, one-to-many, join
if (property.Name == type.Name)
return string.Format("FK_{0}_{1}", property.DeclaringType.Name, type.Name);
return string.Format("FK_{0}_{1}_{2}", property.DeclaringType.Name, property.Name, type.Name);
}
}
My code to exercise it:
[TestMethod]
public void ShouldBeAbleToBuildSchemaWithOurConventions()
{
var configuration = new Configuration();
configuration.Configure();
Fluently
.Configure(configuration)
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<Widget>()
.Conventions.Add<OurForeignKeyConvention>()
)
.BuildSessionFactory();
new SchemaExport(configuration).Create(false, true);
}
My classes and mappings:
public class Widget
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
public virtual WidgetType Type { get; set; }
public virtual ISet<WidgetFeature> Features { get; set; }
}
public class WidgetFeature
{
public virtual int Id { get; set; }
public virtual Widget Widget { get; set; }
public virtual string FeatureDescription { get; set; }
}
public class WidgetMap : ClassMap<Widget>
{
public WidgetMap()
{
Id(w => w.Id);
Map(w => w.Description);
HasMany(w => w.Features).Cascade.AllDeleteOrphan().Inverse();
}
}
public class WidgetFeatureMap : ClassMap<WidgetFeature>
{
public WidgetFeatureMap()
{
Id(w => w.Id);
Map(w => w.FeatureDescription);
References(w => w.Widget);
}
}
The end result is two foreign keys, one called what I want - FK_WidgetFeature_Widget - and another one called FK_WidgetId.
If I change OurForeignKeyConvention to always return the same name regardless of whether the "property" parameter is null then I correctly get a single FK - but I then cannot get the "SourceTable" part of my FK name.
Can anyone explain what I am doing wrong here? Why is GetKeyName called twice? And why does one of the calls not provide a value for the "property" parameter?
Doh. ForeignKeyConvention provides the name for the FK column. What I should have been using is the IHasManyConvention, which can be used to name the FK constraint itself.
public class OurForeignKeyConstraintNamingConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.ForeignKey(string.Format("FK_{0}_{1}", instance.Relationship.Class.Name, instance.EntityType.Name));
}
}

How do I use Fluent NHibernate to configure the following joins?

This is based on a legacy system.
I have the following tables:
CREATE TABLE a
id int
CREATE TABLE b
a_id int,
c_id int
relationshipid int -- must be IN (1, 2, 3)
CREATE TABLE c
id int
I want the following domain models
public class A
{
public int Id { get; set; }
public C entityc { get ; set; }
}
public class C
{
public int Id { get; set; }
}
Table b is set up so that for a particular defined relationshipid there is (well, should only be) one pair of ids. For other relationships, that one to one mapping through B doesn't hold true. Relationshipid can be one of a small number of values.
How do I get entity C into class A from the relationship where the relationshipid is 1 using fluent NHIbernate?
As a side question, is there a name for what I am trying to do here? The original approach was trying use a HasOne with a Join table and Filter the results, but obviously that failed miserably.
EDIT: Clarified RelationshipID and purpose.
I think the easiest way to map this would be to make your table b an entity and have references to both A and C within that entity and RelationshipId as the id. So your mappings would look something like this:
public class A
{
public int Id { get; set; }
public IList<B> bEntities { get; set; }
}
public class ClassAMap : ClassMap<A>
{
public AMap()
{
Table("A");
Id(x => x.Id);
HasMany(x => x.bEntities)
.KeyColumns.Add("a_id");
}
}
public class B
{
public virtual int RelationshipId { get; set; }
public virtual A InstanceA { get; set; }
public virtual C InstanceC { get; set; }
}
public class ClassBMap : ClassMap<B>
{
public BMap()
{
Table("B");
Id(x => x.RelationshipId , "relationshipid");
References(x => x.InstanceA);
References(x => x.InstanceC);
}
}
Edit:
If your wanting to filter these results for the collection of B entities in your A entity to only ones matching RelationshipId = 1 then you should take a look at this post:
Fluent NHibernate and filtering one-to-many relationship on query requiring multiple joins?
You could also do something like this in your class A:
public class A
{
public int Id { get; set; }
public IList<B> bEntities { get; set; }
public C InstanceC
{
get { return bEntities.First<B>(x => x.RelationshipId == 1).InstanceC; }
}
}

HNibernate 1 to Many Relationship Fluent NHiberate using only the Foreign Key Id

I'm wanting to have a 1 to many relationship in NHibernate where the Child table only has access to it's parentsId. Or the foreign key in the DB.
I've tried the following setup:
public class ParentTable
{
public ParentTable()
{
_childRecords = new List<ChildTable>();
}
public virtual int ParentId { get; set; }
private IList<ChildTable> _childRecords;
public virtual IEnumerable<ChildTable> ChildRecords
{
get { return _childRecords; }
}
public void AddChildTable(string value)
{
_childRecords.Add(new ChildTable{ StringField = value });
}
}
public class ChildTable
{
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int ParentId { get; set; }
}
Mappings:
public class ParentTableMap : ClassMap<ParentTable>
{
public ParentTableMap()
{
Not.LazyLoad();
Id(x => x.ParentId);
HasMany(x => x.ChildRecords)
.Not.LazyLoad()
.KeyColumn("ParentId").Cascade.All()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
}
}
public class ChildTableMap : ClassMap<ChildTable>
{
public ChildTableMap()
{
Not.LazyLoad();
Id(x => x.ChildTableId);
Map(x => x.StringField);
Map(x => x.ParentId).Not.Nullable();
}
}
The following test fails as it's trying to insert 0 into the ParentId column?
[TestFixture]
public class Tests
{
[Test]
public void SaveOrUpdate_ParentWithChildren_WillCreateParentWithChildRecordsHavingMatchingParentId()
{
int id;
using (var sessionForInsert = SessionProvider.SessionFactory.OpenSession())
{
using (var trx = sessionForInsert.BeginTransaction())
{
//Assign
var parent = new ParentTable();
parent.AddChildTable("Testing");
parent.AddChildTable("Testing2");
sessionForInsert.SaveOrUpdate(parent); // Fails here with DB constraint error
id = parent.ParentId;
}
}
using (var sessionForSelect = SessionProvider.SessionFactory.OpenSession())
{
//Action
var result = sessionForSelect.Get<ParentTable>(id);
Assert.AreEqual(id, result.ParentId);
Assert.AreEqual(id, result.ChildRecords.First().ParentId);
Assert.AreEqual(id, result.ChildRecords.Last().ParentId);
}
}
}
This is what it's trying to do:
exec sp_executesql N'INSERT INTO ChildTable (StringField, ParentId) VALUES (#p0, #p1); select SCOPE_IDENTITY()',N'#p0 nvarchar,#p1 int',#p0='Testing;,#p1=0
I realise I could set-up a reference to the Parent Class in the Child Class. However I'd like to avoid this if at all possible, due to circular references and the problems that will cause when serializing and de-serializing these classes.
Has anyone successfully set-up and 1 to many relationship like the above?
Thanks
Dave
I think you either need to:
Make the ParentId on ChildTable nullable, or
Change your id generators to something NHibernate can generate.
The second option is nice. Switch to Guid.Comb for your id's. There's a restriction on what object relational mappers can do. Specifically, it is recommended to let NHibernate generate the id's instead of the database. I think this (long) blog post explains it in detail: http://fabiomaulo.blogspot.com/2009/02/nh210-generators-behavior-explained.html.
Good luck!
The problem is that you are attempting to insert a parent and its children in one operation. To do this, NHibernate wants to insert the child records with a null ParentId then update ParentId after the parent record is inserted. This foreign key constraint causes this to fail.
The best solution is to map the relationship from child to parent. You don't have to publicly expose the parent, you could just expose its ParentId as int? if desired.
If that's unacceptable, you should be able to accomplish this by changing the order of operations. First, I would require the ParentId in ChildTable's constructor. Then change the operation order in the test to get it to pass.
public class ChildTable
{
public ChildTable(int parentId) { ParentId = parentId; }
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int ParentId { get; private set; }
}
using (var trx = sessionForInsert.BeginTransaction())
{
//Assign
var parent = new ParentTable();
sessionForInsert.Save(parent);
sessionForInsert.Flush(); // may not be needed
parent.AddChildTable("Testing");
parent.AddChildTable("Testing2");
trx.Commit();
id = parent.ParentId;
}
EDIT:
public class ChildTable
{
private ParentTable _parent;
public ChildTable(Parent parent) { _parent = parent; }
public virtual int ChildTableId { get; set; }
public virtual string StringField { get; set; }
public virtual int? ParentId
{
get { return _parent == null : null ? _parent.ParentId; }
}
}
public class ChildTableMap : ClassMap<ChildTable>
{
public ChildTableMap()
{
Not.LazyLoad();
Id(x => x.ChildTableId);
Map(x => x.StringField);
// From memory, I probably have this syntax wrong...
References(Reveal.Property<ParentTable>("Parent"), "ParentTableId")
.Access.CamelCaseField(Prefix.Underscore);
}
}

FluentNHibernate, getting 1 column from another table

We're using FluentNHibernate and we have run into a problem where our object model requires data from two tables like so:
public class MyModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int FooId { get; set; }
public virtual string FooName { get; set; }
}
Where there is a MyModel table that has Id, Name, and FooId as a foreign key into the Foo table. The Foo tables contains Id and FooName.
This problem is very similar to another post here: Nhibernate: join tables and get single column from other table but I am trying to figure out how to do it with FluentNHibernate.
I can make the Id, Name, and FooId very easily..but mapping FooName I am having trouble with. This is my class map:
public class MyModelClassMap : ClassMap<MyModel>
{
public MyModelClassMap()
{
this.Id(a => a.Id).Column("AccountId").GeneratedBy.Identity();
this.Map(a => a.Name);
this.Map(a => a.FooId);
// my attempt to map FooName but it doesn't work
this.Join("Foo", join => join.KeyColumn("FooId").Map(a => a.FooName));
}
}
with that mapping I get this error:
The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'join' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'joined-subclass, loader, sql-insert, sql-update, sql-delete, filter, resultset, query, sql-query' in namespace 'urn:nhibernate-mapping-2.2'.
any ideas?
I think you misunderstood something here.
Instead of having Id, Name, FooId and FooName in same class you need to create two classes
public class MyModel
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Foo Foo { get; set; }
}
public class Foo
{
public virtual int Id { get; set; }
public virtual string FooName { get; set; }
}
And your mapping classes for these:
public class MyModelMapping : ClassMap<MyModel>
{
public MyModelMapping()
{
this.Id(x => x.Id);
this.Map(x => x.Name);
this.References(x => x.Foo);
}
}
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
this.Id(x => x.Id);
this.Map(x => x.FooName);
}
}
this should help.
But remember Convention other Configuration :)