NHibernate Fluent Mapping a String Property to another Table's column - c#

I have a table in the database that is used only for holding some ID's descriptions:
TABLE SomeClass
- Columns
- ClassTypeID INT CONSTRAINT etc
TABLE SomeClassTypes
- ClassTypeID INT IDENTITY
- Description NVARCHAR
It's done like this so it's easy for users to insert/remove new types.
I want to get a report of all of SomeClass, but I'd like to have a string property to hold the description from the other table:
public class SomeClass
{
public virtual int SomeClassID { get; set; }
public virtual int ClassTypeID { get; set; }
public virtual string DescriptionType { get; set; }
}
public class SomeClassMap : ClassMapping<SomeClass>
{
public SomeClassMap()
{
Table("SomeClassTable");
Property(p => p.SomeClassID, map =>
{
map.Column("SomeClassID");
map.Generator(Generators.Identity);
});
Property(p => p.ClassTypeID, map => map.Column("ClassTypeID"));
//Other properties here
Property(p => p.DescriptionType, ?); //This line
}
}
How can I do this?

Try to use joined table.
public class SomeClassMap : ClassMapping<SomeClass>
{
public SomeClassMap()
{
Table("SomeClassTable");
Property(p => p.SomeClassID, map =>
{
map.Column("SomeClassID");
map.Generator(Generators.Identity);
});
Property(p => p.ClassTypeID, map => map.Column("ClassTypeID"));
//Other properties here
Join("SomeClassTypes", m =>
{
m.KeyColumn("ClassTypeId");
m.Fetch.Join();
m.Map(x => x.DescriptionType).Column("Description");
})
}
}
EDITED
If you use fluent mapping built in NHibernate, try this:
public class SomeClassMap : ClassMapping<SomeClass>
{
public SomeClassMap()
{
Table("SomeClassTable");
Property(p => p.SomeClassID, map =>
{
map.Column("SomeClassID");
map.Generator(Generators.Identity);
});
Property(p => p.ClassTypeID, map => map.Column("ClassTypeID"));
//Other properties here
Property(p => p.DescriptionType, ?); //This line
Join("SomeClassTypes", m =>
{
m.Key(k => k.Column("ClassTypeId"));
m.Fetch(FetchKind.Join);
m.Property(x => x.DescriptionType).Column("Description");
});
}
}

Related

EF Core many to many unwanted columns

I am trying to create a model which has two time many to many relation.
StockItem * - * QualityCheckDefinition
Article * - * QualityCheckDefinition
Many to many classes:
public class StockItemQualityCheckDefinition
{
public StockItem StockItem { get; set; }
public QualityCheckDefinition QualityCheckDefinition { get; set; }
public int StockItemId { get; set; }
public int QualityCheckDefinitionId { get; set; }
}
public class ArticleQualityCheckDefinition
{
public Article Article { get; set; }
public QualityCheckDefinition QualityCheckDefinition { get; set; }
public string ArticleId { get; set; }
public int QualityCheckDefinitionId { get; set; }
}
QualityCheckDefinition class:
public class QualityCheckDefinition : Entity<int>
{
public List<StockItemQualityCheckDefinition> StockItemQualityCheckDefinitions { get; set; }
public List<ArticleQualityCheckDefinition> ArticleQualityCheckDefinitions { get; set; }
}
Article class:
public class Article : Entity<string>
{
public ICollection<ArticleQualityCheckDefinition> ArticleQualityCheckDefinitions { get; set; }
}
StockItem class:
public class StockItem : Entity<int>
{
public List<StockItemQualityCheckDefinition> StockItemQualityCheckDefinitions { get; set; }
}
The mappings:
public class ArticleQualityCheckDefinitionMap : IEntityTypeConfiguration<ArticleQualityCheckDefinition>
{
public void Configure(EntityTypeBuilder<ArticleQualityCheckDefinition> builder)
{
builder.HasKey(x => new { x.ArticleId, x.QualityCheckDefinitionId });
builder.HasOne(x => x.Article)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.ArticleId)
.HasPrincipalKey(x => x.Id)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(x => x.QualityCheckDefinition)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.QualityCheckDefinitionId)
.HasPrincipalKey(x => x.Id)
.OnDelete(DeleteBehavior.Restrict);
}
}
public class StockItemQualityDefinitionMap : IEntityTypeConfiguration<StockItemQualityCheckDefinition>
{
public void Configure(EntityTypeBuilder<StockItemQualityCheckDefinition> builder)
{
builder.HasKey(x => new { x.StockItemId, x.QualityCheckDefinitionId });
builder.HasOne(x => x.StockItem)
.WithMany(x => x.StockItemQualityCheckDefinitions)
.HasForeignKey(x => x.StockItemId);
builder.HasOne(x => x.QualityCheckDefinition)
.WithMany(x => x.StockItemQualityCheckDefinitions)
.HasForeignKey(x => x.QualityCheckDefinitionId);
}
}
The relationships are configured fine and they are working. However migration creates additional unnesesary fields inside QualityCheckDefinition class. Migration wants to add both nullable StockItemId and ArticleId into QualityCheckDefinition class.
There is part of migration:
migrationBuilder.CreateTable(
name: "QualityCheckDefinitions",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
//other properties
ArticleId = table.Column<string>(nullable: true),
StockItemId = table.Column<int>(nullable: true)
},
Why does it add these unwanted keys?
PS. I am using EF Core 2.2
Where is your configuration mapping for StockItem and Article? And I think the way you used .HasPrincipalKey() inside ArticleQualityCheckDefinitionMap is not right.
Principal Key
If you want the foreign key to reference a property other than the primary key, you can use the Fluent API to configure the principal key property for the relationship. The property that you configure as the principal key will automatically be setup as an alternate key.
Your ArticleQualityCheckDefinitionMap
builder.HasOne(x => x.Article)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.ArticleId) // <--
.HasPrincipalKey(x => x.Id) // <--
.OnDelete(DeleteBehavior.Restrict);
By conventions, .HasForeignKey(x => x.ArticleId) will target the primary key of the Article entity, string Id. And then you told Entity Framework that you don't want to setup the foreign key to the primary key. You want to target another property called Id by saying .HasPrincipalKey(x => x.Id).
Huh? Aren't they the same? That's why I think Entity Framework got confused. And since you don't have a configuration for Article, Entity Framework tried its best to generate a primary key for you, called "ArticleId"?
You can fix the problem by just adding the configurations back and removing .HasPrincipalKey() there.
StockItemConfiguration
using DL.SO.EFCore.Learning.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DL.SO.EFCore.Learning.Data.Configurations
{
public class StockItemConfiguration : IEntityTypeConfiguration<StockItem>
{
public void Configure(EntityTypeBuilder<StockItem> builder)
{
builder.HasKey(x => x.Id);
builder.ToTable(nameof(StockItem));
}
}
}
ArticleConfiguration
using DL.SO.EFCore.Learning.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DL.SO.EFCore.Learning.Data.Configurations
{
public class ArticleConfiguration : IEntityTypeConfiguration<Article>
{
public void Configure(EntityTypeBuilder<Article> builder)
{
builder.HasKey(x => x.Id);
builder.ToTable(nameof(Article));
}
}
}
You might need to configure for QualityCheckDefinition as well:
QualityCheckDefinitionConfiguration
using DL.SO.EFCore.Learning.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DL.SO.EFCore.Learning.Data.Configurations
{
public class QualityCheckDefinitionConfiguration : IEntityTypeConfiguration<QualityCheckDefinition>
{
public void Configure(EntityTypeBuilder<QualityCheckDefinition> builder)
{
builder.HasKey(x => x.Id);
builder.ToTable(nameof(QualityCheckDefinition));
}
}
}
Then remove .HasPrincipalKey():
ArticleQualityCheckDefinitionConfiguration
using DL.SO.EFCore.Learning.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DL.SO.EFCore.Learning.Data.Configurations
{
public class ArticleQualityCheckDefinitionConfiguration : IEntityTypeConfiguration<ArticleQualityCheckDefinition>
{
public void Configure(EntityTypeBuilder<ArticleQualityCheckDefinition> builder)
{
builder.HasKey(x => new { x.ArticleId, x.QualityCheckDefinitionId });
builder.HasOne(x => x.Article)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.ArticleId);
builder.HasOne(x => x.QualityCheckDefinition)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.QualityCheckDefinitionId);
builder.ToTable(nameof(ArticleQualityCheckDefinition));
}
}
}
Then you should be fine?

NHibernate OneToMany Mapping By Code

I saw many exemple on the internet on Mapping relation object with nhibernate but i can't make mine works.
I have two Model for exemple:
public class Vehicule
{
public virtual int Id { get; set; }
public virtual int Brand { get; set; }
public virtual int Color { get; set; }
public virtual int UserID { get; set; }
public virtual UserModel User { get; set; }
}
public class VehiculeMap: ClassMapping<Vehicule>
{
public VehiculeMap()
{
Table("G1Vehicule");
Id(x => x.Id, map => { map.Column("id"); });
Property(x => x.Brand, map => { map.Column("brand"); });
Property(x => x.Color, map => { map.Column("color"); });
Property(x => x.UserID, map => { map.Column("user_id"); });
}
}
public class UserModel
{
public virtual int Id { get; set; }
public virtual int Username { get; set; }
}
public class UserModelMap : ClassMapping<UserModel>
{
public UserModelMap()
{
Table("Users");
Id(x => x.Id, map => { map.Column("id"); });
Property(x => x.Username, map => { map.Column("username"); });
}
}
Previously, I only displayed the UserId, but now I would like to fill my UserModel when I get a specific VehiculeModel from the database.
Here my Model relation is OneToOne.
Also for design purpose I will never query an User to get his list of vehicule, so I dont need to have a "List of VehiculeModel" in my UserModel.
If you have any hint, about how I can map this in my Map class (saw a lots of xml mapping but i would like to map it by code) It would be very appreciated.
Thanks
I finally made it work.
nothing to complicate in fact, I just forgot to add my second model (UserModelMap) in the map class list for Nhibernate.
public class VehiculeMap: ClassMapping<Vehicule>
{
public VehiculeMap()
{
Table("G1Vehicule");
Id(x => x.Id, map => { map.Column("id"); });
Property(x => x.Brand, map => { map.Column("brand"); });
Property(x => x.Color, map => { map.Column("color"); });
Property(x => x.UserID, map => { map.Column("user_id"); });
ManyToOne(x => x.User, map => {
map.Column("user_id"),
map.Fetch(FetchKind.Join),
map.notFound(NotFoundMode.Ignore)
})
}
}
Maybe it could help someone else.

TPC in Entity Framework v6

I'm trying to do a pretty simple thing in Entity Framework.
I have a product that has zero or more parameters and these parameters will be mapped to their own tables. However, I'm unable to get this to work. I've been trying to get the mappings right and then use the migrations to see what the database is supposed to look like. I know that this is very simple in NHibernate, but I'm forced against my will to use Entity Framework v6.
Background
These are my entities:
namespace Entities
{
public class EntityState
{
public int Id { get; set; }
}
public class ProductState : EntityState
{
public virtual ICollection<ProductParameterState> Parameters { get; set; }
}
public abstract class ProductParameterState : EntityState
{
}
public class ColorParameterState : ProductParameterState
{
public virtual string Color { get; set; }
}
public class SizeParameterState : ProductParameterState
{
public virtual int Size { get; set; }
}
}
I would like to store this in the following schema:
How to do this?
My attempts
Table-per-class
I tried mapping using TPC:
namespace Mappings
{
public class ProductMap : EntityTypeConfiguration<ProductState>
{
public ProductMap()
{
HasKey(x => x.Id);
Property(x => x.Name);
HasMany(x => x.Parameters);
}
}
public class ColorParameterMap : EntityTypeConfiguration<ColorParameterState>
{
public ColorParameterMap()
{
HasKey(x => x.Id);
Property(x => x.Color);
Map(x =>
{
x.ToTable("ColorParameters");
x.MapInheritedProperties();
});
}
}
public class SizeParameterMap : EntityTypeConfiguration<SizeParameterState>
{
public SizeParameterMap()
{
HasKey(x => x.Id);
Property(x => x.Size);
Map(x =>
{
x.ToTable("SizeParameters");
x.MapInheritedProperties();
});
}
}
}
But this gives the error The association 'ProductState_Parameters' between entity types 'ProductState' and 'ProductParameterState' is invalid. In a TPC hierarchy independent associations are only allowed on the most derived types..
Don't use an inheritence strategy
So I tried to remove the MapInheritedProperties, but then it wants to create an additional, and unwanted, table:
CreateTable(
"dbo.ProductParameterStates",
c => new
{
Id = c.Int(nullable: false, identity: true),
ProductState_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.ProductStates", t => t.ProductState_Id)
.Index(t => t.ProductState_Id);
I don't want this. I am able to get rid of this one by removing the Parameters property in Product, but then I'm not able to use the Parameters of a Product.
Am I asking for too much or is it possible?
You can use TPC, but the relationship must be bidirectional with explicit FK defined (which I guess is the opposite of "independent association" mentioned in the error message).
Add inverse navigation property and FK property to your base entity:
public abstract class ProductParameterState : EntityState
{
public int ProductId { get; set; }
public ProductState Product { get; set; }
}
and use the same entity configurations as in your first attempt, except for the ProductMap where you either remove the following
HasMany(x => x.Parameters);
or change it to
HasMany(e => e.Parameters)
.WithRequired(e => e.Product)
.HasForeignKey(e => e.ProductId);

Duplicate key and table field in one-to-zero-or-one relation

For a metaphor here, a person can have zero or one car, and one car belongs only to one person.
I have a database that looks like this:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Car Car { get; set; }
}
public class Car
{
public int PersonId { get; set; }
public virtual Person Person { get; set; }
public string Model { get; set; }
}
I want to have a link from the person to its car, if existent, and from the car to its person. So my EntityTypeConfigurations are like so:
public class PersonConfig : EntityTypeConfiguration<Person>
{
public PersonConfig()
{
ToTable("tblPerson");
HasKey(s => s.Id)
.Property(s => s.Id)
.HasColumnName("idPerson");
Property(s => s.Name)
.HasColumnName("strName")
.IsRequired();
HasOptional(a => a.Car)
.WithOptionalPrincipal();
}
}
public class CarConfig : EntityTypeConfiguration<Car>
{
public CarConfig()
{
ToTable("tblCar");
HasKey(s => s.PersonId)
.Property(s => s.PersonId)
.HasColumnName("idPerson");
Property(s => s.Model)
.HasColumnName("strModel")
.IsRequired();
HasRequired(a => a.Person)
.WithRequiredDependent();
}
}
I don't know what I'm getting wrong, but EF:
Creates an additional Person_Id field in tblCar
Creates two foreign keys in tblCar, one named idPerson and the other Person_Id
What am I forgetting or doing wrong?
You have to use WithRequired instead of WithOptionalPrincipal, and the relationship do not need to be configured in both sides.
public class PersonConfig : EntityTypeConfiguration<Person>
{
public TaskConfig()
{
ToTable("tblPerson");
HasKey(s => s.Id);
Property(s => s.Id)
.HasColumnName("idPerson");
Property(s => s.Name)
.HasColumnName("strName")
.IsRequired();
HasOptional(a => a.Car)
.WithRequired(s => s.Person);
}
}
public class CarConfig : EntityTypeConfiguration<Car>
{
public CarConfig()
{
ToTable("tblCar");
HasKey(s => s.PersonId)
.Property(s => s.PersonId)
.HasColumnName("idPerson");
Property(s => s.Model)
.HasColumnName("strModel")
.IsRequired();
//not necessary
//HasRequired(a => a.Person)
//.WithRequiredDependent();
}
}
What am I forgetting or doing wrong?
You are not configuring the Person - Car relationship correctly.
Let fix that. Note that you don't need to configure the relationship in both places.
Remove the following from the Person config:
HasOptional(a => a.Car)
.WithOptionalPrincipal();
and replace the following in the Car config:
HasRequired(a => a.Person)
.WithRequiredDependent();
with
HasRequired(c => c.Person)
.WithOptional(p => p.Car);

How to map a property as NOT a column in EF 4.1

I have a class:
public class classParty
{
private int _arrivedCount;
public int PartyID {get; private set;}
public DateTime PartyDate {get; private set;}
public int ArrivedCount
{
get
{
return _arrivedCount;
}
set
{
_arrivedCount = value;
}
}
}
I can map the PartyId and the PartyDate but I don't have a column for ArrivedCount (it's a moment in time count, it doesn't persist).
How do I tell EF 4.1 to stop looking for a column named "ArrivedCount"? It's not in the table. It's not going to be in the table. It's simply a property of the object and that's all.
Thanks in advance.
EDIT:
Here's the Fluent API configuration for classParty.
public class PartyConfiguration : EntityTypeConfiguration<classParty>
{
public PartyConfiguration()
: base()
{
HasKey(p => p.PartyID);
Property(p => p.PartyID)
.HasColumnName("PartyID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.IsRequired();
Property(p => p.PartyDate)
.HasColumnName("PartyDate")
.IsRequired();
ToTable("Party");
}
}
With data annotations:
[NotMapped]
public int ArrivedCount
//...
Or using Fluent API:
modelBuilder.Entity<classParty>()
.Ignore(c => c.ArrivedCount);
modelBuilder.Entity<classParty>().Ignore(x => x.ArrivedCount);

Categories