NHibernate Mapping 1 Object To 2 Tables - c#

We want to use NHibernate as our persistence layer in our application. We are also using Fluent NHibernate for the mappings.
We get Person data from a 3rd party and we need to save the data to our database. It works better in the code to have all properties on one object, but it makes more sense to save the data to 2 tables in our database.
Our object looks like this:
public class Person
{
public virtual long VersionNumber { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string IdentificationNumber { get; set; }
}
Our database tables look like this:
CREATE TABLE PersonVersion (
[PK] VersionNumber bigint NOT NULL,
[FK] PersonDemographicsId int NOT NULL
)
CREATE TABLE PersonDemographics (
[PK] PersonDemographicsId int NOT NULL,
IdentificationNumber nvarchar(9) NOT NULL,
FirstName nvarchar(100) NOT NULL,
LastName nvarchar(100) NOT NULL
)
When we receive new data, the version number can change, but the rest of the demographics could be the same. What we need NHibernate to do is save a new record to the PersonVersion table which links to the existing PersonDemographics record. If the demographics have changed, then we will create a new record in both tables. But most of the time, once we've downloaded the initial data, the demographics won't change as often as the version number. We need to keep track of all version numbers so that's why it's necessary to create a new PersonVersion record.
How would we accomplish this using NHibernate and mappings using Fluent NHibernate?
Also, as you can see, our Person object currently does not have a PersonDemographicsId because it is not needed by our application at all; it is just an ID for the table relationship which is needed in the database. In order to properly map this in NHibernate, do we have to add a PersonDemographicsId property on our Person object?
Thanks for the help!

This article http://ayende.com/blog/2327/multi-table-entities-in-nhibernate explains a way to map a single class to two tables in the database.

just an idea, maybe has to be tweaked
public class Person
{
public virtual int Id { get; set; }
internal protected virtual IList<long> VersionNumbers { get; set; }
public virtual long VersionNumber {
get { return VersionNumbers[VersionNumbers.Count - 1]; }
set { VersionNumbers.Add(value); }
}
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string IdentificationNumber { get; set; }
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("PersonDemographics");
Id(p => p.Id, "PersonDemographicsId").GeneratedBy.Assigned();
Map(p => p.FirstName);
Map(p => p.LastName);
Map(p => p.IdentificationNumber);
HasMany(p => p.VersionRecord)
.Table("PersonVersion")
.KeyColumn("PersonDemographicsId")
.Element("VersionNumber")
.OrderBy("VersionNumber")
.Cascade.All();
}
}

Related

What type of relationship is this and how to implement it in .NET Core (EFCore, FluentAPI)?

The problem I have is pretty easy, but my mind stopped working. Sorry if my questions is dumb, but I'm not really good with databases (neither I am with EFCore).
I want to have the following tables:
CVs: with ID and Name/Title (string)
Skills: with ID and Name/Title (string)
SkillsCV: with ID, CvID (foreign key to a record in CVs), SkillID (foreign key to a record in Skills)
I don't want to have a foreign key to SkillsCV in the CVs and Skills tables. Is it possible? Is it possible in .NET Core and more importantly with Fluent API?
I've made a small research for FluentAPI and there are foreign keys in the both ends in one-to-one relationships. Is this needed? BTW, it is a one-to-one relationship, right? I don't want the one side to know about the other side. Is this what's called 0 to 1, or this is a completely different thing? I'm really confused.
So what I've seen for one-to-one relationships in FluentAPI, I need the following code:
modelBuilder.Entity<Skill>()
.HasOne(skill => skill.SkillCV) // but I don't have a SkillCV object in Skill model
.WithOne(skillCV => skillCV.Skill) // I have skillCV.Skill
.HasForeignKey<SkillCV>(skillCV => skillCV.SkillID); // I have this foreign key in skillCV as well
But I don't want to have an object (or foreign key) in the Skills table (as I don't want such in the CVs table). Is this possible? I'm for sure doing something wrong. Can you help me to find my mistake (if there is one)?
If you know a better way to do this, please share it. Thanks in advance!
EDIT: A quick example to what I want to create:
CVs Table:
ID, Name
1 "CV1"
2 "CV2"
3 "CV3"
Skills Table:
ID, Name
1 "C#"
2 "Java"
3 "Python"
SkillsCVs Table:
ID, CvID, SkillID
1 1 1
2 1 3
3 2 1
Is this a good solution to solve this problem? I haven't created the SkillsCVs table yet, now I have only CVs and Skills (every skill has a CV_ID), but this way when I need to populate a select box in the frontend, I need to return DISTINCT Skills from the API (because there are 800 C# for example records for different CVs). I thought a SkillsCVs table will solve this issue, but I'm not entirely sure now :D
What you have is a many-to-many relationship. You could model things like this:
public class Skill
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CV
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SkillCV
{
public int SkillID { get; set; }
public Skill Skill { get; set; }
public int CVID { get; set; }
public CV CV { get; set; }
}
Then to set this up with Fluent API, you could do this:
modelBuilder.Entity<SkillCV>()
.HasKey(t => new { t.SkillID, t.CVID});
modelBuilder.Entity<SkillCV>()
.HasOne(pt => pt.Skill)
.WithMany()
.HasForeignKey(pt => pt.SkillID);
modelBuilder.Entity<SkillCV>()
.HasOne(pt => pt.CV)
.WithMany()
.HasForeignKey(pt => pt.CVID);
This way your joins later will be simple, and the SkillCV table will have a composite key made up of SkillID and CVID (ensuring referential integrity).
You can do it only with annotations if I understand you correctly.
public class Skill
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class Cv
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class SkillCv
{
[ForeignKey("Skill")]
public int SkillId { get; set; }
[ForeignKey("Cv")]
public int CvId { get; set; }
public virtual Skill Skill{ get; set; }
public virtual Cv Cv { get; set; }
}
public class TestContext : DbContext
{
public DbSet<Skill> Skills { get; set; }
public DbSet<Cv> Cvs { get; set; }
public DbSet<SkillCv> SkillCvs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//The entity does not have its own key, only the relationship of the two
modelBuilder.Entity<SkillCv>().HasNoKey();
}
}
So you will have a row in the table for each skill that has a cv
var allSkillFromCv = await _context.SkillCvs.Where(s => s.CvId == 1).ToListAsync()
If you want to use the navigation property
var allSkillFromCv = await _context.SkillCvs.Where(s => s.CvId == 1).Include(s => s.Skill).ToListAsync();
You need three table here: CVs Table, Skills Table and SkillsCVs Table. You have a
Many-to-Many relationship between CVs Table and Skills Table. You have to define the Many-to-Many relationship using Fluent API. you need navigation property in both CVs Table and Skills Table
public class CV
{
....
public IList<SkillCV> SkillsCVs { get; set; }
}
public class Skill
{
....
public IList<SkillCV> SkillsCVs { get; set; }
}
in DB context
public DbSet<SkillCV> SkillsCVs { get; set; }
You also need to define relationship using Fluent API
modelBuilder.Entity<SkillCV>()
.HasOne<Skill>(sc => sc.Skill)
.WithMany(s => s.SkillsCVs)
.HasForeignKey(sc => sc.SkillID);
modelBuilder.Entity<SkillCV>()
.HasOne<CV>(sc => sc.CV)
.WithMany(s => s.SkillsCVs)
.HasForeignKey(sc => sc.CvID);

Entity Framework Fluent API Cannot Insert one-to-one

I am trying to insert an object into a database table with Entity Framework and using code first (fluent api). Whilst doing this I keep running into one of the following errors:
1) InvalidOperationException: A dependent property in a
ReferentialConstraint is mapped to a store-generated column. Column:
'Id'
2) Cannot insert value into identity column with IDENTITY_INSERT set
to OFF
My relationship is a one-to-one however perhaps I can rework or structure the database to accomplish what I am wanting. I have also thought about utilizing a one to zero or zone even though the other object will always be required.
So I have the following database tables mapped into these C# objects (with virtual for the mapping):
public class test
{
[Key]
public long Id { get; set; }
public DateTime ResultDate { get; set; }
public virtual test_additional test_additional { get; set; }
public virtual test_status test_status { get; set; }
}
public class test_additional
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long TestId { get; set; } //Foreign Key to test
...
public virtual test test { get; set; }
}
public class test_status {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long TestId { get; set; } //Foreign Key to Test
public long TestFormId { get; set; } //this is the object I want to insert, Foreign key to the Primary key of test_form
...
public virtual test test { get; set; }
public virtual test_form test_form { get; set; } //object mapping
}
public class test_form {
[Key]
public long Id { get; set; } //Primary Key
public string FileName { get; set; }
public virtual test_status test_status { get; set; }
}
So some pretty simple objects, I've stripped members/columns that are necessary for the functionality for ease of readability.
So there are test objects that have an optional test_additional or test_status .
These are generated with a one to zero-or-one relationship. Which are working fine and I have the relationship defined as:
modelBuilder.Entity<test>()
.HasOptional(e => e.test_additional)
.WithRequired(e =>e.test);
modelBuilder.Entity<test>()
.HasOptional(e => e.test_status)
.WithRequired(e => e.test);
Now the entity I am having trouble with is the test_form, if a test_status is defined there should always be a test_form associated with that. I currently have a relationship defined as:
modelBuilder.Entity<test_form>()
.HasRequired(e => e.test_status)
.WithRequiredDependent(e => e.test_form);
In addition I have tried appending this config:
modelBuilder.Entity<test_status>()
.HasKey(e => e.TestFormId);
--
Here is a simple implementation of inserting this object in the database:
try {
test UserTest = new test { ResultDate = DateTime.Now; }
UOW.test.Insert(UserTest);
UOW.Save();
test_additional ta = new test_additional { TestId = UserTest.Id; }
test_form tf = new test_form { FileName = "Testing.pdf"; }
UOW.test_additional.Insert( ta );
UOW.test_form.Insert( tf );
UOW.Save(); //This is where it will throw that error.
test_status status = new test_status {
TestId = UserTest.Id;
TestFormId = tf.Id;
}
UOW.test_status.Insert( status );
UOW.Save();
} catch {
throw;
}
--
I have used BreakPoints before the Unit of Work saves and I can confirm that the Id in the test_form object is the default of long which is 0. So I am not setting the Identity Column explicitly. Upon removing of test_form (in the implemented method) I can insert into the test_additional category and save with no issue.
So my question is really... are my entity relationships defined correctly? Would it be smarter to use an additional One to Zero-or-One for the test_form object? Why can I not insert this simple object into my database?
I have also thought about defining the virtual test_form object in test_status as an ICollection, then I could use .HasMany(e => e.test_form).HasForeignKey(e => e.TestFormId); so it would bind to the Foreign Key even though I would only be using 1 item for the test_status.
Opinions? Am I close?
Thanks again for taking the time to read my question!
i had your problem. just do delete your database and migration files. after do it add the new migration to create the new database.

Database-first approach in Entity Framework without auto generated code

I wonder, if there is any way ,
to use Database-first approach with manually generated classes (models) in advance(just like Code-first approach),
but without using auto-generated code which Entity Framework creates using Database-first approach?
I have 3 Classes(first two of them Student and Courses have many to many relationship), which represents models:
First one is Student:
public class Student
{
public int StudentID { get; set;}
public string Name { get; set;}
public DateTime BirthDate { get; set;}
public ICollection<StudentToCourse> StudentToCourses { get; set; }
public Student()
{
StudentToCourses = new List<StudentToCourse>();
}
}
Then Course:
public class Course
{
public int CourseID { get; set; }
public string CourseName { get; set; }
public ICollection<StudentToCourse> StudentToCourses { get; set; }
public Course()
{
StudentToCourses = new List<StudentToCourse>();
}
}
And Relation/Intermediate Class with additional properties StudentToCourse:
public class StudentToCourse
{
[Key, Column(Order = 0)]
public int StudentID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
[Key, Column(Order = 2)]
public DateTime Date { get; set; }
public virtual Student Student { get; set; }
public virtual Course Course { get; set; }
//public ICollection<Student> Students { get; set; }
//public ICollection<Course> Courses { get; set; }
public int Grade { get; set; }
}
Also, i created Database, using LocalDb feature in VS 2013
I have 3 Tables:
Courses:
CREATE TABLE [dbo].[Courses]
(
[CourseID] INT NOT NULL PRIMARY KEY IDENTITY,
[CourseName] NVARCHAR(100) NOT NULL,
)
Students:
CREATE TABLE [dbo].[Students]
(
[StudentID] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL,
[BirthDate] DATETIME NOT NULL,
)
Relation Table StudentsToCourses:
CREATE TABLE [dbo].[StudentsToCourses]
(
[StudentID] INT REFERENCES Students(StudentID) NOT NULL,
[CourseID] INT REFERENCES Courses(CourseID) NOT NULL,
[Date] DATETIME NOT NULL,
[Grade] INT NOT NULL,
PRIMARY KEY (StudentID, CourseID, Date)
)
Unfortunately, i have no luck with this approach, i do get students' data but i don't receive data from relational table and i can't receive all related grades per student.
I searched for related topics in google and in stackoverflow , but all those topics weren't helpful for me, although the example above i found in this topic.
As I suspected, the problem is not whether or not you can have a database and a class model independently. Of course you can! All these generation tools and migration stuff only serve one goal: making life easier, help you keeping both models in sync. But you can do that job yourself just as well. The end result is always: two models that – at runtime – don't interact with each other whatsoever. (Don't interact? No, not as such. There must be a middleman, an ORM, to connect both worlds.)
The reason why you don't get data is because lazy loading does not occur. Your statement is
var listOfGrades = _context.Students.Where(s => s.Name.StartsWith("J"))
.FirstOrDefault().StudentToCourses;
This requires lazy loading, because the FirstOrDefault() statement executes the first part of the query. It renders a Student of which subsequently the StudentToCourses are accessed. But these don't load because the collection is not virtual. It should be
public virtual ICollection<StudentToCourse> StudentToCourses { get; set; }
This enables EF to override the collection in a dynamic proxy object that is capable of lazy loading.
But of course is is more efficient to get the collection in one statement, for example:
var listOfGrades = _context.Students.Include(s => s.StudentToCourses)
.Where(s => s.Name.StartsWith("J"))
.FirstOrDefault().StudentToCourses;
Yes, you can. You just need a context with no initialization strategy (so it doesn't try to create or migrate your existing database):
public class ExistingDatabaseContext : DbContext
{
public ExistingDatabaseContext()
: base("ExistingDatabaseConnectionStringName")
{
Database.SetInitializer<ExistingDatabaseContext>(null);
}
// DbSets here for your "code-first" classes that represent existing database tables
}
Just bear in mind that this context will not be capable of doing migrations or any other form of initialization, so if you have actual true code-first tables in there as well, you'll need a separate context to manage those.

Nhibernate creating object which has an object property

I'm just starting to work with NHibernate. I have two objects:
public class Supplier
{
public virtual int id{get;set;}
public virtual SupplierAddress address{get;set;}
public virtual string Name{get;set;}
}
public class SupplierAddress
{
public virtual int id{get;set;}
public virtual Supplier{get;set;}
public virtual string SupplierAddressLine{get;set;}
}
When I want to create a new Supplier I create a new object:
var supplierAddress = new SupplierAddress {
SupplierAddressLine = "someLine"
}
var supplier = new Supplier
{
Name = "someName",
SupplierAddress = SupplierAddressLine
}
Then, when i try to save using:
_session.Save(supplier);
I get the error: "Cannot insert the value NULL into column 'id'
Update 1 Mappings
for SupplierAddress
Id(x => x.Id).GeneratedBy.Identity().Column("Id");
References(x => x.Supplier).Column("SupplierId");
Map(x => x.AddressLine1).Column("AddressLine1").Not.Nullable().Length(255);
for Supplier
Id(x => x.Id).GeneratedBy.Identity().Column("Id");
References(x => x.SupplierAddress).Column("SupplierAddressId").Not.Nullable();
HasMany(x => x.SupplierAddresses).KeyColumn("SupplierId");
You should set some cascade rules on the Supplier → SupplierAddress relationship:
References(s => s.SupplierAddress)
.Column("SupplierAddressId")
.Not.Nullable()
.Cascade.All(); /* Cascade operations that happen on `Supplier` */
Otherwise, NHibernate does not know that saving the parent (Supplier) should also save the child (SupplierAddress)
Edit: I think you're actually using References incorrectly here.
In a mapping, when you say an entity References another, you're basically telling NHibernate that the other side of this relationship is a HasMany.
In your case, neither Supplier nor SupplierAddress actually has many SupplierAddresses or Suppliers, respectively.
With that in mind, you probably mean one of two things:
A SupplierAddress is shared by multiple Suppliers. This would mean that SupplierAddress actually has many Suppliers, but a Supplier only has one SupplierAddress.
In the C# class, this would mean that SupplierAddress has a collection of Suppliers (OR has no reference to Supplier at all).
In this case, your database tables would look like this:
create table [SupplierAddress]
(
[Id] int identity(1,1) primary key clustered,
[AddressLine1] nvarchar(255) not null
);
create table [Supplier]
(
[Id] int identity(1,1) primary key clustered,
[SupplierAddressId] int not null references [SupplierAddress]([Id])
)
Your C# classes would look like this:
public class Supplier
{
public virtual int Id { get; set; }
public virtual SupplierAddress SupplierAddress { get; set; }
public virtual string Name { get; set; }
}
public class SupplierAddress
{
public virtual int Id { get; set; }
public virtual string AddressLine1 { get; set; }
}
And your mappings would look like this:
public class SupplierMap : ClassMap<Supplier>
{
public SupplierMap()
{
Id(s => s.Id).GeneratedBy.Identity().Column("Id");
References(s => s.SupplierAddress)
.Column("SupplierAddressId")
.Cascade.All();
}
}
public class SupplierAddressMap : ClassMap<SupplierAddress>
{
public SupplierAddressMap()
{
Id(s => s.Id).GeneratedBy.Identity().Column("Id");
Map(s => s.AddressLine1)
.Column("AddressLine1")
.Not.Nullable()
.Length(255);
}
}
A Supplier has one SupplierAddress, and a SupplierAddress is only ever associated with one Supplier. Another way to think of this is that the entire SupplierAddress table could be logically merged into Supplier.
In this case, your database tables would look like this:
create table [SupplierAddress]
(
[Id] int identity(1,1) primary key clustered,
[AddressLine1] nvarchar(255) not null,
[SupplierId] int not null
);
create table [Supplier]
(
[Id] int identity(1,1) primary key clustered,
[SupplierAddressId] int references [SupplierAddress]([Id])
);
alter table [SupplierAddress]
add constraint [FK_SupplierAddress_Supplier]
foreign key ([SupplierId]) references [Supplier]([Id])
Your C# classes would look like this:
public class Supplier
{
private SupplierAddress supplierAddress;
public virtual int Id { get; set; }
public virtual SupplierAddress SupplierAddress
{
get { return this.supplierAddress; }
set
{
this.supplierAddress = value;
this.supplierAddress.Supplier = this;
}
}
public virtual string Name { get; set; }
}
public class SupplierAddress
{
public virtual int Id { get; set; }
public virtual string AddressLine1 { get; set; }
public virtual Supplier Supplier { get; set; }
}
And your mappings would look like this:
public class SupplierMap : ClassMap<Supplier>
{
public SupplierMap()
{
Id(s => s.Id).GeneratedBy.Identity().Column("Id");
HasOne(s => s.SupplierAddress).PropertyRef(s => s.Supplier)
.Access.CamelCaseField()
.Cascade.All();
}
}
public class SupplierAddressMap : ClassMap<SupplierAddress>
{
public SupplierAddressMap()
{
Id(s => s.Id).GeneratedBy.Identity().Column("Id");
Map(s => s.AddressLine1).Column("AddressLine1");
References(s => s.Supplier).Column("SupplierId").Unique();
}
}
Note that when Supplier.SupplierAddress is set, the address's Supplier property is set.

mapping nhibernate parent/child relationship the 'other' way round

using FluentNhibernate;
I am trying to persist a seemingly simple object model:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Config Config { get; set; }
}
public class Config
{
public int ConfigId { get; set; }
public int ProductId { get; set; }
public string ConfigField1 { get; set; }
public string ConfigField2 { get; set; }
}
and the database looks like: (not syntactically correct)
CREATE TABLE [dbo].[Products](
[ProductId] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NULL
)
CREATE TABLE [dbo].[Config](
[ConfigId] [int] IDENTITY(1,1) NOT NULL,
[ProductId] [int] NOT NULL,
[ConfigField1] [varchar](50) NULL,
[ConfigField2] [varchar](50) NULL
) ON [PRIMARY]
Out of the box fluent-nhibernate will try and map this as a foreign key on the Products table, eg:
INSERT INTO Products (Name, Config_id) VALUES (?, ?);
I don't want it to do this, rather I was hoping the mapping would insert Products first then the config second with ProductId being inserted into the Config table.
I've pulled my hair out trying overrides and reading links such as this and this but still can't get it to do what I want. I am working with existing data and code so I would rather not change the database table definitions or the domain object. There is a bit more going on than what this example paints so if we could avoid discussions on domain model design that would be great. I have a link to a spike of this project here (assumes database exists)
my current fluent mappings are:
public class ProductOverrides : IAutoMappingOverride<Product>
{
public void Override(AutoMapping<Product> mapping)
{
mapping.Id(x => x.Id).Column("ProductId");
mapping.Table("Products");
}
}
public class ConfigOverrides : IAutoMappingOverride<Config>
{
public void Override(AutoMapping<Config> mapping)
{
mapping.Id(x => x.ConfigId);
}
}
You are trying to map a one-to-one relationship as a one-to-many by mapping what would be the many side twice. That won't work. NHibernate is strict in its definition of one-to-one and requires that both sides have the same primary key, so that won't work either.
I've butted heads with this before and the best workaround I found is to model it as a standard on-to-many but only allow one item in the collection by encapsulating access to the it. In your case I would guess that Product would be the one side and Config the many.
I'm not sure if Config is used elsewhere but you could ignore ConfigId as its identity
public class Config
{
public int Id { get; set; }
public Product Product { get; set; }
public string ConfigField1 { get; set; }
public string ConfigField2 { get; set; }
}
public class ProductMap : ClassMap<Product>
{
public class ProductMap()
{
HasOne(p => p.Config);
}
}
public class ConfigMap : ClassMap<Config>
{
public class ConfigMap()
{
Id(c => c.Id, "ProductId").GeneratedBy.Foreign("Product");
References(c => c.Product, "ProductId");
Map(c => ...);
}
}
Another idea is to join and map as Component
public class ProductMap : ClassMap<Product>
{
public class ProductMap()
{
Join("Config", join =>
{
join.KeyColumn("ProductId");
join.Component(p => p.Config, c =>
{
c.Map(...);
});
}
}
}
Disadvantage is that you can not query Config directly, you have to query through Product

Categories

Resources