I am a newbie to Fluent Nhibernate and .NET. So please let me know if my question is not clear.
I have 3 tables in app as below
Table A
AID(PK), CreatedDate(PK), ZID, AFirstname, ALastname, AAddress, AZipCode
Table B
CID(PK), AID(PK), Date(PK), Field1, Field2
Table C
CID (PK), Cname
Please correct me if I am wrong. My Model Classes look like below.
public class A {
public A() {}
public virtual long AID { get; set; }
public virtual int ZID { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual string AFirstName { get; set; }
public virtual string ALastName { get; set; }
public virtual string AZip { get; set; }
public virtual string AAddress { get; set; }
}
MapperClass for Class A
public class AMap : ClassMap<A> {
public AMap() {
Table("A");
CompositeId().KeyProperty(x => x.AID, "AID").KeyProperty(x => x.CreatedDate, "CreatedDate");
Map(x => x.ZID).Column("ZID").Not.Nullable();
Map(x => x.AFirstName).Column("AFirstname").Not.Nullable();
Map(x => x.ALastName).Column("ALastname").Not.Nullable();
//Same as above for Address and ZipCode
}
}
My B Model Class looks like below
public class B{
public B() { }
public virtual long AID { get; set; }
public virtual int CID { get; set; }
public virtual DateTime Date { get; set; }
public virtual string Field1 {get; set; }
public virtual string Field2 {get; set; }
}
Mapper Class for Class B
public class BMap : ClassMap<B> {
public BMap() {
Table("B");
CompositeId().KeyProperty(x => x.AID, "AID").KeyProperty(x => x.CID, "CID").KeyProperty(x => x.Date, "Date");
Map(x => x.Field1).Column("Field1").Not.Nullable();
//Same for Field2
}
}
My Table C Model Class looks like below.
public class C{
public C() { }
public virtual int C{ get; set; }
public virtual string Cname{ get; set; }
}
MapperClass for Class C
public class C: ClassMap<C> {
public C() {
Table("C");
Id(x => x.CID).GeneratedBy.Identity().Column("CID");
Map(x => x.CName).Column("Cname").Length(64);
}
}
Now many might argue that the table designs are not proper. But nothing can be done at this point and definitely this kind of design may degrade the performance but I guess we have to live with this for a little while.
Now I want some help in writing the mapping for all the three tables. Also can i use Criteria API to write a query for this to join all the 3 tables.
In my Application I get the ZID from another function. Now depending on the ZID i need to grab AFirstName, ALastName, Field1,Field2, CName. How can this be possible?
Please help. Also let me know if the problem is not clear.
Thanks in Advance.
replace the ids with references to the original properties so you can navigate them
public class B
{
public virtual A A { get; set; }
public virtual C C { get; set; }
public virtual string Field1 {get; set; }
public virtual string Field2 {get; set; }
}
public class BMap : ClassMap<B>
{
public BMap()
{
Table("B");
CompositeId()
.KeyReference(x => x.A, "AID", "Date")
.KeyReference(x => x.C, "CID");
Map(x => x.Field1, "Field1").Not.Nullable();
Map(x => x.Field2, "Field2").Not.Nullable();
}
}
// query for it
var results = session.CreateCritera<B>()
.JoinAlias("A", "a")
.JoinAlias("C", "c")
.SetProjection(Projections.List()
.Add(Projections.Property("Field1"), "Field1")
.Add(Projections.Property("Field2"), "Field2")
.Add(Projections.Property("a.CreatedDate"), "Date")
.Add(Projections.Property("c.Name"), "CName"))
.SetResulttransformer(Transformers.AliasToBean<YourDto>())
.List<YourDto>();
Related
I'm have a issue,
i'm studyng nhibernate with c# and .net core, but i dont know how to procede on this case.
i create my table with 2FK:
public class Venda
{
public Venda()
{
VendaId = Guid.NewGuid();
DataVenda = DateTime.Now;
}
public virtual Guid VendaId { get; set; }
public virtual Guid BombomId { get; set; }
public virtual Guid ClienteId { get; set; }
public virtual DateTime DataVenda { get; set; }
public virtual IList<Bombom> Bomboms { get; set; }
public virtual IList<Cliente> Clientes { get; set; }
}
My migration is
[FluentMigrator.Migration(2)]
public class CreateTableVenda : FluentMigrator.Migration
{
public override void Up()
{
Create.Table("Venda")
.WithColumn("VendaId").AsGuid().NotNullable().PrimaryKey().Indexed()
.WithColumn("DataVenda").AsDateTime().NotNullable()
.WithColumn("BombomId").AsGuid().NotNullable().Indexed()
.WithColumn("ClienteId").AsGuid().NotNullable().Indexed();
Create.ForeignKey().FromTable("Venda").ForeignColumn("BombomId")
.ToTable("Bombom").PrimaryColumn("BombomId");
Create.ForeignKey().FromTable("Venda").ForeignColumn("ClienteId")
.ToTable("Cliente").PrimaryColumn("ClienteId");
}
public override void Down()
{
Delete.Table("Venda");
}
}
But my mapping, i'm have issues, because i want to LAZYLOAD the map entity together when i get
public class VendaMap : ClassMap<Venda>
{
public VendaMap()
{
Id(x => x.VendaId);
Map(x => x.DataVenda);
Map(x => x.BombomId).Column("Bombom").Access.CamelCaseField();
Map(x => x.ClienteId);
LazyLoad();
}
}
Could someone help me please?
I have an entity as Plan with multiple sub-plans (children), each of which could be null.
For the PlanDto, I am trying to load up a list of all children rather than having a separate property for each child like the entity.
I have already achieved it manually through a foreach loop but now I am trying to do it via AutoMapper, which is failing for some reason.
Entities:
public class Plan
{
public virtual int Id { get; set; }
public DateTime Date { get; set; }
public virtual PlanDetail PlanChild1 { get; set; }
public virtual ObservationCare PlanChild2 { get; set; }
}
public class PlanDetail
{
public virtual int Id { get; set; }
public virtual Plan Plan { get; set; }
public virtual string Description { get; set; }
}
public class ObservationCare
{
public virtual int Id { get; set; }
public virtual Plan Plan { get; set; }
public virtual string Description { get; set; }
}
DTOs:
public class PlanDto: EntityDto
{
public DateTime Date { get; set; }
public IEnumerable<ChildPlan> ChildPlan { get; set; }
}
public class ChildPlan : EntityDto
{
public ChildPlanType Type { get; set; }
}
public enum ChildPlanType
{
PlanDetail,
ObservationCare
}
AutoMapper config:
configuration.CreateMap<Plan, PlanDto>();
configuration.CreateMap<PlanDetail, ChildPlan>()
.ForMember(dto => dto.Type, options => options.MapFrom(p => ChildPlanType.PlanDetail));
configuration.CreateMap<ObservationCare, ChildPlan>()
.ForMember(dto => dto.Type, options => options.MapFrom(p => ChildPlanType.ObservationCare));
Mapping attempt:
var output = new List<PlanDto>();
var plans = await _planRepository.GetAll().ToList();
foreach (var plan in plans)
{
output.Add(ObjectMapper.Map<PlanDto>(plan));
}
I do not know why ChildPlan DTOs in the output list are always null!
You have to specify the mapping for PlanDto.ChildPlan:
configuration.CreateMap<Plan, PlanDto>()
.ForMember(dto => dto.ChildPlan,
options => options.MapFrom(
p => new object[] { p.PlanChild1, p.PlanChild2 }.Where(c => c != null)));
If you are using Entity Framework Core, you have to use eager-loading:
var plans = await _planRepository.GetAll()
.Include(p => p.PlanChild1)
.Include(p => p.PlanChild2)
.ToList();
There's also a simpler and more efficient way to map a list:
var output = ObjectMapper.Map<List<PlanDto>>(plans);
I was wondering if someone could help me. I want to perform SQL INNER JOIN operation by using NHibernate. First of all let me introduce you to the structure of my database.
I have the following parameters into my C# method: int documentId, int userId, int folderId. My main goal is to get RoleDocumentValueEntity. By using USER_ID and FOLDER_ID I can get the unique FolderUserRoleEntity. I am looking for the ROLE_ID. I want to join ROLE_DOCUMENT_VALUE and FOLDER_USER_ROLES tables by ROLE_ID where DOCUMENT_ID is equal to specific value which I have as a parameter of C# method.
My entities:
public class RoleDocumentValueEntity
{
public virtual int Id { get; set; }
public virtual RoleEntity Role { get; set; }
public virtual DocumentEntity Document { get; set; }
public virtual string Text { get; set; }
}
public class RoleEntity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<UserRoleEntity> UserRoles { get; set; }
public virtual IList<FolderUserRoleEntity> FolderUserRoles { get; set; }
}
public class FolderEntity
{
public virtual int Id { get; set; }
public virtual UserEntity User { get; set; }
public virtual IList<DocumentEntity> Documents {get; set;}
}
public class UserEntity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class UserRoleEntity
{
public virtual int Id { get; set; }
public virtual int UserId { get; set; }
public virtual RoleEntity Role { get; set; }
}
public class FolderUserRoleEntity
{
public virtual int Id { get; set; }
public virtual int UserId { get; set; }
public virtual RoleEntity Role { get; set; }
public virtual FolderEntity Folder { get; set; }
}
Mappings:
public class RoleDocumentValueMap : BaseDatabaseMap<RoleDocumentValueEntity>
{
public RoleDocumentValueMap()
{
this.Table("ROLE_DOCUMENT_VALUE");
Id(x => x.Id, "ROLE_DOCUMENT_VALUE_ID");
// Relationships
References(x => x.Role)
.Column("ROLE_ID")
.Cascade.None();
References(x => x.Document)
.Column("DOCUMENT_ID")
.Cascade.None();
}
}
public class RoleMap : BaseDatabaseMap<RoleEntity>
{
public RoleMap()
{
this.Table("ROLES");
Id(x => x.Id, "ROLE_ID");
HasMany(x => x.UserRoles)
.KeyColumn("ROLE_ID")
.Cascade.All()
.Inverse();
HasMany(x => x.FolderUserRoles)
.KeyColumn("ROLE_ID")
.Cascade.All()
.Inverse();
}
}
public class UserRoleMap : BaseDatabaseMap<UserRoleEntity>
{
public UserRoleMap()
{
this.Table("USER_ROLES");
this.Id(x => x.Id, "USER_ROLE_ID");
Map(x => x.UserId).Column("USER_ID");
References(x => x.Role).Column("ROLE_ID").Cascade.None().Fetch.Join();
}
}
public class FolderUserRoleMap : BaseDatabaseMap<FolderUserRoleEntity>
{
public FolderUserRoleMap()
{
this.Table("FOLDER_USER_ROLES");
Id(x => x.Id, "FOLDER_USER_ROLE_ID");
Map(x => x.UserId).Column("USER_ID");
References(x => x.Folder).Column("FOLDER_ID").Cascade.None().Fetch.Join();
References(x => x.Role).Column("ROLE_ID").Cascade.None().Fetch.Join().Not.LazyLoad();
}
}
public class FolderMap : BaseDatabaseMap<FolderEntity>
{
public FolderMap()
{
this.Table("FOLDERS");
Id(x => x.Id, "FOLDER_ID");
HasMany(x => x.Documents)
.Cascade
.AllDeleteOrphan()
.Inverse()
.KeyColumn("FOLDER_ID");
}
}
public class DocumentMap : BaseDatabaseMap<DocumentEntity>
{
public DocumentMap()
{
this.Table("DOCUMENTS");
Id(x => x.Id, "DOCUMENT_ID");
// Relationships
References(x => x.Folder)
.Column("FOLDER_ID")
.Cascade.None();
}
}
INNER JOIN
I manually created SQL query which I want to get by using NHibernate.
SELECT ROLE_DOCUMENT_VALUE.*
FROM ROLE_DOCUMENT_VALUE
INNER JOIN FOLDER_USER_ROLES ON FOLDER_USER_ROLES.ROLE_ID = ROLE_DOCUMENT_VALUE.ROLE_ID
AND FOLDER_USER_ROLES.FOLDER_ID = ?
AND FOLDER_USER_ROLES.USER_ID = ?
AND ROLE_DOCUMENT_VALUE.DOCUMENT_ID = ?;
It looks like my NHibernate criteria should look like this one:
var result = this.Session.CreateCriteria<RoleDocumentValueEntity>()
.CreateCriteria("FolderUserRoles", "fr")
.Add(Restrictions.Eq("fr.UserId", userId))
// etc
.List();
But this criteria cannot be performed because RoleDocumentValueEntity doesn't have such property as FolderUserRoles.
Please, could you provide me what is the best practice in this case? How can I create the SQL query which I want by using NHibernate?
you could use linq support for Nhibernate to achieve this easily,
from doc in Session.Query<RoleDocumentValueEntity>()
join role in Session.Query<FolderUserRoleEntity>()
on doc.Role equals role.Role
First of all I have these two models to store a post in two tables one for shared data and the other contains cultured data for English and Arabic
public class Post
{
public int Id { set; get; }
public bool Active { get; set; }
public bool Featured { get; set; }
public virtual ICollection<PostContent> Contents { get; set; }
}
public class PostContent
{
public int Id { set; get; }
public string Title { get; set; }
public string Summary { get; set; }
public string Details { get; set; }
[StringLength(2)]
public string Culture { get; set; }
public int PostId { get; set; }
[InverseProperty("PostId")]
public virtual Post Post{ set; get; }
}
Mapping
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Posts");
}
}
public class PostContentMap : EntityTypeConfiguration<PostContent>
{
public PostContentMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(p => p.Post).WithMany(p => p.Contents).HasForeignKey(p=>p.PostId);
ToTable("PostContents");
}
}
I have two questions
1- Is these models are connected properly. Is there something else I need to do ?
2- I need to select all Posts with their contents where the culture of the content 'en' for example. I used this:
var res = context.Posts.Include(p => p.Contents.Single(c => c.Culture.Equals("en")));
and have this error:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.Parameter name: path
If you know you are not going to support more than two cultures then I would just add to your Post class.
public class Post
{
public Post()
{
Contents = new List<PostContent>();
}
public int Id { set; get; }
public bool Active { get; set; }
public bool Featured { get; set; }
public int? EnglishContentId { get;set;}
public int? ArabicContentId { get;set;}
PostContent EnglishContent {get;set;}
PostContent ArabicContent {get;set;}
}
public class PostContent
{
public int Id { set; get; }
public string Title { get; set; }
public string Summary { get; set; }
public string Details { get; set; }
[StringLength(2)]
public string Culture { get; set; }/*This property is not required*/
}
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Posts");
HasOptional(p => p.EnglishContent).WithMany().HasForeignKey(p=>p.EnglishContentId);
HasOptional(p => p.ArabicContent).WithMany().HasForeignKey(p=>p.ArabicContentId);
}
}
public class PostContentMap : EntityTypeConfiguration<PostContent>
{
public PostContentMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("PostContents");
}
}
The Above design will simplify your design and queries, will improve the performance alot.
But if you might have to support more cultures then you got the design and mapping right.
As far as EF 5, include does not allow filters, but I am not sure about EF 6.0
atleast you can get all posts that have english contents as follows
Add using System.Data.Entity;
var res = context.Posts.Include(p => p.Contents).Where(c => c.Contents.Any(cp=>cp.Culture.Equals("en")));
I'm having problems with one to many mapping with composite key on both sides.
Here is what I have so far:
public class OneSide
{
public virtual int A { get; set; }//-A,B,C,D together form primary key
public virtual int B { get; set; }
public virtual int C { get; set; }
public virtual int D { get; set; }
public virtual int OtherData { get; set; }
public virtual IList<ManySide> Ls { get; set; }
}
public class OneSideMap : ClassMap<OneSide>
{
public OneSideMap()
{
LazyLoad();
CompositeId().KeyProperty(x => x.A).KeyProperty(x => x.B).KeyProperty(x => x.C).KeyProperty(x => x.D);
Map(x => x.OtherData).Not.Nullable();
HasMany(x => x.Ls).KeyColumns.Add("A", "B", "C", "D").Inverse().Cascade.All();
}
}
public class ManySide
{
public virtual OneSide OneSide { get; set; }
public virtual int A { get; set; }//-A,B,C,D,E together form primary key
public virtual int B { get; set; }
public virtual int C { get; set; }
public virtual int D { get; set; }
public virtual int E { get; set; }
public virtual int OtherData2 { get; set; }
}
public class ManySideMap : ClassMap<ManySide>
{
public ManySideMap()
{
LazyLoad();
CompositeId().KeyReference(x => x.A).KeyReference(x => x.B).KeyReference(x => x.C).KeyReference(x => x.D).KeyProperty(x => x.E);
Map(x => x.OtherData2).Not.Nullable();
References(x => x.OneSide).Columns("A", "B", "C", "D").Cascade.All();
}
}
Here is database structure:
table: OneSide:
int A;
int B;
int C;
int D;
int OtherData;
table: ManySide:
int A;
int B;
int C;
int D;
int E;
int OtherData2;
I know it is not correct and I'm now out of ideas what is wrong as I have started learning NHibernate like few hours ago. Can somebody point me out what is wrong in my code ?
the reference to oneSide must be the keyreference because you can not map the columns twice.
public class ManySideMap : ClassMap<ManySide>
{
public ManySideMap()
{
LazyLoad();
CompositeId()
.KeyReference(x => x.OneSide, "A", "B", "C", "D")
.KeyProperty(x => x.E);
Map(x => x.OtherData2).Not.Nullable();
}
}
Accessing column B from ManySide is as simple as manysideObj.OneSide.B and as long as you only access One side properties forming the primary key it it doesn't even have to load OneSide.