NHibernate References Dont' save Foreign Key - c#

I have 2 classes that reference each other. It's a weird situation that our CRM needs.
I have an Organization and EmAddress tables and classes. The Organization inherits from Subscriber, which also has a table. I think this could be my problem, or the fact that I can't set Inverse on these because there is no "HasMany"...
The order of insert/update is ..
INSERT Email
INSERT Org
UPDATE Email to set Email.Subscriber
Email.Subscriber needs to be "NOT NULL", so this doesn't work. How can I change the order, I can't use Inverse because there is no list. Just 2 references.
public class Organization : Subscriber
{
public override string Class { get { return "Organization"; } }
EmAddress PrimaryEmailAddress {get;set;}
}
public class OrganizationMap : SubclassMap<Organization>
{
public OrganizationMap()
{
Table("Organization");
KeyColumn("Organization");
References(x => x.PrimaryEmail,"PrimaryEmailAddress").Cascade.SaveUpdate();
}
}
public EmAddressMap()
{
Id(x => x.Id, "EmAddress");
Map(x => x.EmailAddress, "eMailAddress");
References<Subscriber>(x => x.Subscriber,"Subscriber").LazyLoad().Fetch.Select().Not.Nullable();
/*not.nullable() throw s error. NHibernate INSERTS email, INSERTS org, UPDATES email. */
}
public class EmAddress
{
public virtual Guid Id { get; set; }
public virtual string EmailAddress { get; set; }
public virtual Subscriber Subscriber { get; set; }
}
//Implementation
var session = NHIbernateHelper.GetSession();
using(var tx = session.BeginTransaction())
{
var org = new Organization();
org.PrimaryEmail = new EmAddress(){Subscriber = org};
session.Save(org);
tx.commit();
}

This post might help:
http://ayende.com/blog/3960/nhibernate-mapping-one-to-one
Have only one side use many-to-one (Fluent: "References") and the other side uses one-to-one (Fluent: "HasOne").

Related

Many entities to one junction table NHibernate modelling

I would like to be able to add a collection of Notes to any of my main entities in my NHibernate application. I can see how you could do this with a seperate junction table per entity. However, I would like to be able to avoid this and only have one junction table - if this is possible.
Below is the code so far, however this will result in all Notes being loaded for every Entity and I only want to load the notes for that particular entity. What are the alternative approaches I need to take?
public class Entity
{
public virtual int Id { get; set; }
}
public class EntityType1 : Entity
{
public EntityType1()
{
Notes = new List<Note>();
}
public virtual string EntityTypeName { get; set; }
public virtual IList<Note> Notes {get;set;}
}
public class EntityType2 : Entity
{
public EntityType2()
{
Notes = new List<Note>();
}
public virtual string EntityType2Name { get; set; }
public virtual IList<Note> Notes { get; set; }
}
public class Note
{
public virtual int Id { get; set; }
public virtual IList<Entity> Entities { get; set; }
public virtual string NoteText { get; set; }
}
}
namespace FluentNHib.Mappings
{
public class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Id(m => m.Id);
}
}
public class EntityType1Map : ClassMap<EntityType1>
{
public EntityType1Map()
{
Id(m => m.Id);
Map(m => m.EntityTypeName1);
HasManyToMany(m => m.Notes).Table("EntityToNotes")
.ParentKeyColumn("EntityId")
.ChildKeyColumn("NoteId")
.LazyLoad()
.Cascade.SaveUpdate();
}
}
public class EntityType2Map : ClassMap<EntityType2>
{
public EntityType2Map()
{
Id(m => m.Id);
Map(m => m.EntityType2ame);
HasManyToMany(m => m.Notes).Table("EntityToNotes")
.ParentKeyColumn("EntityId")
.ChildKeyColumn("NoteId")
.LazyLoad()
.Cascade.SaveUpdate();
}
}
public class NoteMap : ClassMap<Note>
{
public NoteMap()
{
Id(m => m.Id);
Map(m => m.NoteText);
}
}
I am not sure what the real issue is:
...however this will result in all Notes being loaded for every Entity and I only want to load the notes for that particular entity...
Is the issue in lazy loading? or in fact that Entity1 and Entity2 can have same ID, therefore the references are mixed? (I expect that and this should be part of the answer below)
Anyhow, I would say that we can achieve what you need: map the Note with just one table EntityToNotes. And that is good.
But, in general, I would descourage you from using the many-to-many. It is just my own feeling, experience. Below are some links with more explanation:
Am I doing many to many incorrectly when using fluent nhibernate?
NHibernate how do you map a crossreference table to a bag?
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
Draft of the SOLUTION:
So, firstly we have to extend the table "EntityToNotes" with two columns
EntityToNoteId column - we need a primary key for new pairing object
Discriminator column
The Discriminator column will be used for (almost like a standard inheritance)
inserting Discriminator value during creation
filtering te IList<Notes> per Entity
These could be the pairing Entity (with an abstract base gathering the common stuff)
public abstract class EntityToNote<TEntity>
{
public abstract string Discriminator { get; set; }
public virtual TEntity Entity {get;set;}
public virtual Note Note {get;set;}
}
// the pairing objects
public class EntityType1ToNote : EntityToNote<EntityType1>
{
string _discriminator = "EntityType1"; // here we set the discriminator
public virtual string Discriminator
{
get { return _discriminator; }
set { _discriminator = value; }
}
...
// Similar for other pairing objects
The Entities will now be referencing lists of pairing objects
public class EntityType1 : Entity
{
public virtual IList<EntityType1ToNote> Notes {get;set;}
...
public class EntityType2 : Entity
{
public virtual IList<EntityType2ToNote> Notes { get; set; }
...
Here is snippet of the mapping (all other Entities will have usual mapping, including ClassMaps for EntityType1ToNote, EntityType2ToNote...)
public class EntityType1Map : ClassMap<EntityType1>
{
public EntityType1Map()
{
Id(m => m.Id);
Map(m => m.EntityTypeName1);
HasMany(m => m.Notes)
// this "table" setting is redundant, it will come from EntityType1ToNote
//.Table("EntityToNotes")
.KeyColumn("EntityId")
// here is the trick, that only related rows will be selected
.Where("Discriminator = 'EntityType1'")
.Cascade.AllDeleteOrphan();
}
}
As I tried to explain in the links provided, we gained this way a lot. Mostly the ability to use more columns on the pairing table - e.g. Discriminator (later we can have more columns like SortBy...) and we are able to use powerful searching with subqueries - see Query on HasMany reference
Also, in fact, the pairing could be mapped via the real inheritance... But the main point here is: Instead of many-to-many we introduced the pairing object and gained a lot

EF Code First working with many-to-many relationships

I am using Visual Studio 2010, C# 4.0 and Entity Framework 5.0. I have been using database first development for many years but am trying to move to code first and am running into problems. Reading and searching does not seem to address the problems
I have simplified my problem as follows - I have two classes - Assessors and Documents.
public class Assessor
{
public int AssessorID { get; set; }
public virtual List<Document> Documents { get; set; }
}
public class Document
{
public int DocumentID { get; set; }
public string DocumentLocation { get; set; }
public string DocumentName { get; set; }
public virtual List<Assessor> Assessors { get; set; }
}
with the context
public class DocumentAssignment : DbContext
{
public DbSet<Assessor> Assessors { get; set; }
public DbSet<Document> Documents { get; set; }
}
An assessor can have many documents and a document can have many assessors (a classic many-to-many relationship).
I am using convention to create the relationship but have also used the fluent API. I have seeded the document table.
My two questions:
ONE - I want to assign documents to assessors - what is the best way to save this to the database?
TWO I have the following method to retrieve documents assigned to an assessor:
public static IEnumerable<MaternalDocument> GetAssignedDocumentList(int UserID, string ConnectionString)
{
using (DocumentAssignment dbContext = new DocumentAssignment(ConnectionString))
{
return returnValue = dbContext.MaternalAssessments
.Where(m => m.AssessorID == UserID)
.Include(m => m.MaternalDocuments)
.Select(m => m.MaternalDocuments)
.ToList();
}
}
but I cannot get this to compile because of mapping issues. What am I doing wrong?
You have to tell the DbContext about how the many-to-many relationship is set up, by overriding OnModelCreating in DocumentAssignment. Replace AssessorDocuments in this code with your relation table name.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Assessor>()
.HasMany(a => a.Documents)
.WithMany(d => d.Assessors)
.Map(m =>
{
m.MapLeftKey("AssessorID");
m.MapRightKey("DocumentID");
m.ToTable("AssessorDocuments");
});
base.OnModelCreating(modelBuilder);
}
To assign a Document to an Assessor (assuming a Document exists with DocumentID of 1 and an Assessor exists with an AssessorID of 1):
using (var context = new DocumentAssignment())
{
var assessor = context.Assessors.Find(1);
var document = context.Documents.Find(1);
assessor.Documents.Add(document);
context.SaveChanges();
}
Your GetAssignedDocumentList method would look something like this:
public static IEnumerable<Document> GetAssignedDocumentList(int UserID)
{
using (var context = new DocumentAssignment())
{
return context.Documents.Where(d => d.Assessors.Any(a => a.AssessorID == UserID));
}
}

Inserting a parent entity with existing child in Fluent NHibernate

This is a general question, I'm sure it's quite common, however I haven't found anything on it (or I don't know what to search for I guess).
I'm having the following entities in my project:
public class User
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Unit Unit { get; set; }
}
public class Unit
{
public virtual int Id { get; set;}
public virtual string Name { get; set;}
}
This is how I've done the Fluent NHibernate mappings:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id).GeneratedBy.Identity().Column("UserId");
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Unit).Column("UnitId");
}
}
public class UnitMap : ClassMap<Unit>
{
public UnitMap()
{
Table("Unit");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("UnitId");
Map(x => x.Name).Column("Name").Not.Nullable();
HasMany(x => x.Users).KeyColumn("UnitId");
}
}
Now here is my question. How do I create a new User if I only have the user's unit Id not a full unit object, and the unit already exists in the database (created previously) ?
Something like this:
public class TestClass
{
// Adding a user to a unit example
public void SavingAUser(int unitId)
{
var user = new User
{
FirstName = "TestFirstName",
LastName = "TestLastName",
Unit = new Unit() // <-- I have only the Id of the unit I don't actually have a unit object here I don't want to query the DB to get the full object, I already have the Id
};
var userRepository = new UserRepository();
userRepository.Save(user);
}
}
How would I go about something like this. I hope I'm making sense, if not please let me know I'll throw in more clarifications. I'm also pretty certain that this is a very common scenario
You can return a proxy to the unit without fetching it.
var user = new User
{
FirstName = "TestFirstName",
LastName = "TestLastName",
Unit = Session.Load<Unit>(unitId)
}
You'll need to expose the session object.

How to set NHibernate mappings for 2 entities which share data that is stored in another table?

Given is the following class hierarchy:
Class Diagram http://img535.imageshack.us/img535/4802/personusermanager.jpg
Additional info:
The Person class is not abstract.
A person could be a User, a Manager or something else that implements the IPerson interface.
The Person class must not have any knowledge about its child classes.
A child class could reside in another assembly.
It is also possible that a Person is a User and a Manager as wel. In that case the UserRepository must return a User object for the given PersonId and the ManagerRepository must return a Manager for the same PersonId.
It must also be possible to get the Person (base) part for all objects that implement the IPerson interface via a PersonRepository.
How can this be mapped within NHibernate?
We are using FluentNHibernate 1.2 and NHibernate 3.1.
In our current situation each class has its own table. So we have a Person table, a User table and a Manager table.
I have already tried the following options without success:
Mapping this with inheritence mapping (one table per subclass);
A join with Person in the mapping for User and Manager (without inheritance mapping);
A HasOne mapping with Person in the User and Manager mapping (without the join and inheritance mapping);
As you've no doubt discovered, it is easy enough to map inheritance like this: Person -> User, or Person -> Manager, or Person -> Manager -> User (or alternately, Person -> Manager -> User).
NHibernate does not allow you to promote/demote to or from a subclass. You'd have to run native SQL to promote or demote.
However, if you followed my initial "map" of your inheritance, you should have had an epiphany that using subclasses for what you are trying to do is an inappropriate solution for what you are trying to model. And that's only with two subclasses! What happens when you add more roles?
What you have is a Person, who can be a member of any number of roles, where roles are extensible. Consider this solution (Source on github: https://github.com/HackedByChinese/NHibernateComposition):
(Assume we have an Entity abstract class which handles equality, where to objects of the same type with the same ID are considered equal)
Project: Models
public class Person : Entity, IPerson
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual IList<Role> Roles { get; protected set; }
public Person()
{
Roles = new List<Role>();
}
public virtual void AddRole(Role role)
{
if (Roles.Contains(role)) return;
role.Person = this;
Roles.Add(role);
}
public virtual void RemoveRole(Role role)
{
if (!Roles.Contains(role)) return;
role.Person = null;
Roles.Remove(role);
}
}
public interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
Int32 Id { get; }
}
public abstract class Role : Entity
{
public virtual Person Person { get; set; }
public virtual string RoleName { get; protected set; }
}
public class User : Role
{
public virtual string LoginName { get; set; }
public virtual string Password { get; set; }
}
Project: Models.B
public class Manager : Role
{
public virtual string Division { get; set; }
public virtual string Status { get; set; }
}
Project: Models.Impl
I placed fluent mappings for both projects into one to save time. There could easily be separate mapping assemblies for Models and Models.B
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(c => c.Id)
.GeneratedBy.HiLo("100");
Map(c => c.FirstName);
Map(c => c.LastName);
HasMany(c => c.Roles)
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
public class RoleMap : ClassMap<Role>
{
public RoleMap()
{
Id(c => c.Id)
.GeneratedBy.HiLo("100");
DiscriminateSubClassesOnColumn<string>("RoleName");
References(c => c.Person);
}
}
public class UserMap : SubclassMap<User>
{
public UserMap()
{
DiscriminatorValue("User");
Join("User", joined =>
{
joined.Map(c => c.LoginName);
joined.Map(c => c.Password);
});
}
}
Project: Models.Impl.Tests
[TestFixture]
public class MappingTests
{
private ISessionFactory _factory;
#region Setup/Teardown for fixture
[TestFixtureSetUp]
public void SetUpFixture()
{
if (File.Exists("test.db")) File.Delete("test.db");
_factory = Fluently.Configure()
.Database(() => SQLiteConfiguration.Standard
.UsingFile("test.db")
.ShowSql()
.FormatSql())
.Mappings(mappings => mappings.FluentMappings
.AddFromAssemblyOf<PersonMap>())
.ExposeConfiguration(config =>
{
var exporter = new SchemaExport(config);
exporter.Execute(true, true, false);
})
.BuildSessionFactory();
}
[TestFixtureTearDown]
public void TearDownFixture()
{
_factory.Close();
}
#endregion
#region Setup/Teardown for each test
[SetUp]
public void SetUpTest()
{
}
[TearDown]
public void TearDownTest()
{
}
#endregion
[Test]
public void Should_create_and_retrieve_Person()
{
var expected = new Person
{
FirstName = "Mike",
LastName = "G"
};
using (var session = _factory.OpenSession())
using (var tx = session.BeginTransaction())
{
session.SaveOrUpdate(expected);
tx.Commit();
}
expected.Id.Should().BeGreaterThan(0);
using (var session = _factory.OpenSession())
using (var tx = session.BeginTransaction())
{
var actual = session.Get<Person>(expected.Id);
actual.Should().NotBeNull();
actual.ShouldHave().AllProperties().EqualTo(expected);
}
}
[Test]
public void Should_create_and_retrieve_Roles()
{
// Arrange
var expected = new Person
{
FirstName = "Mike",
LastName = "G"
};
var expectedManager = new Manager
{
Division = "One",
Status = "Active"
};
var expectedUser = new User
{
LoginName = "mikeg",
Password = "test123"
};
Person actual;
// Act
expected.AddRole(expectedManager);
expected.AddRole(expectedUser);
using (var session = _factory.OpenSession())
using (var tx = session.BeginTransaction())
{
session.SaveOrUpdate(expected);
tx.Commit();
}
using (var session = _factory.OpenSession())
using (var tx = session.BeginTransaction())
{
actual = session.Get<Person>(expected.Id);
// ignore this; just forcing the Roles collection to be lazy loaded before I kill the session.
actual.Roles.Count();
}
// Assert
actual.Roles.OfType<Manager>().First().Should().Be(expectedManager);
actual.Roles.OfType<Manager>().First().ShouldHave().AllProperties().But(c => c.Person).EqualTo(expectedManager);
actual.Roles.OfType<User>().First().Should().Be(expectedUser);
actual.Roles.OfType<User>().First().ShouldHave().AllProperties().But(c => c.Person).EqualTo(expectedUser);
}
}
If you want to constrain a Person to one instance of a particular role, just put a unique index and mess with the Equals method to check if Id is the same OR RoleName is the same.
You can easily get or check a user's role of any type:
if (person.Roles.OfType<User>().Any())
{
var user = person.Roles.OfType<User>().FirstOrDefault();
}
You can also query roles directly to look up their Person:
var peopleWhoAreManagersInDistrictOne = (from role in session.Query<Manager>()
where role.District == "One"
select role.Person);
You can also see that other assemblies can define additional roles. Manager is in a different assembly than Models.
So, you can see this will do everything you want plus more, despite the fact that it uses a different approach.

Difficulty Concerning EF Code First Fluent API, TPH, and Foreign Keys

I have two tables in my database. One is called Users, and the other is called Widgets. The Widgets table represents 3 entities in my code model. One of the entities, Widget, is a parent class for the other two entities, WidgetTypeA and WidgetTypeB. Both WidgetTypeA and WidgetTypeB have navigation properties to the User entity, which is persisted to the Users table in the database. I'm having trouble getting Code First to use the same foreign key for both the WidgetTypeA and WidgetTypeB entities (UserId). Does anyone know how to do this? It seems like it should be a common problem with Table Per Hierarchy mapping.
My entity classes are as follows:
public class Widget
{
public int Id { get; set; }
public string Name { get; set; }
}
class WidgetMap : EntityTypeConfiguration<Widget>
{
public WidgetMap()
{
ToTable("Widgets");
HasKey(w => w.Id);
Property(w => w.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(w => w.Name)
.IsRequired()
.HasMaxLength(75)
.IsUnicode(true);
}
}
public class WidgetTypeA : Widget
{
public int UserId { get; set; }
public virtual User User { get; set; }
public string Color { get; set; }
public int DepthLevel { get; set; }
}
class WidgetTypeAMap : EntityTypeConfiguration<WidgetTypeA>
{
public WidgetTypeAMap()
{
Map(w => w.Requires("WidgetTypeId").HasValue(1));
HasRequired(w => w.User)
.WithMany(u => u.WidgetTypeAs)
.HasForeignKey(w => w.UserId)
.WillCascadeOnDelete(false);
Property(w => w.Color)
.IsOptional()
.IsUnicode(true)
.HasMaxLength(75);
Property(w => w.DepthLevel)
.IsOptional();
}
}
public class WidgetTypeB : Widget
{
public int UserId { get; set; }
public virtual User User { get; set; }
}
class WidgetTypeBMap : EntityTypeConfiguration<WidgetTypeB>
{
public WidgetTypeBMap()
{
Map(w => w.Requires("WidgetTypeId").HasValue(2));
HasRequired(w => w.User)
.WithMany(u => u.WidgetTypeBs)
.HasForeignKey(w => w.UserId)
.WillCascadeOnDelete(false);
}
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public int Age { get; set; }
public virtual ICollection<WidgetTypeA> WidgetTypeAs { get; set; }
public virtual ICollection<WidgetTypeB> WidgetTypeBs { get; set; }
}
class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
ToTable("Users");
HasKey(u => u.Id);
Property(u => u.Username)
.IsRequired()
.HasMaxLength(75)
.IsUnicode(true);
Property(u => u.Age)
.IsRequired();
}
}
At any rate, I keep getting the error
Invalid column name 'UserId1'
when I try to perform the following operations:
using (var entities = new MyEntities())
{
User u = new User
{
Username = "Frank",
Age = 14
};
entities.Users.Add(u);
entities.SaveChanges();
WidgetTypeA wa1 = new WidgetTypeA
{
Name = "0SDF81",
UserId = u.Id,
DepthLevel = 6
};
entities.WidgetTypeAs.Add(wa1);
entities.SaveChanges();
}
Not sure if this can be fixed or not. I can always specify a second UserId foreign key for the Widgets table, but that seems pointless. Perhaps there's a way to do this using Fluent API?
You cannot map properties defined in different derived entities to the same column. That is limitation in EF. If your WidgetTypeA has UserId property and your WidgetTypeB has UserId property they must be different columns in the database. It should work if you move both UserId and User properties from derived types to the parent Widget type.
I know its a long way late, but hopefully may help other readers.
Although Ladislav was correct that using a mapped Foreign Key is not supported in EF6, I did find a useful workaround.
It is possible to define a computed column specification whose expression simply refers to the original column. Userid in the description above. This can be used as the discriminator for the TPH mapping. With this approach, the column need not be persisted, but can be used for TPH, with the original column being available for use as a foreign key.

Categories