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.
Related
I have a situation where I need to map a sub-collection of items within an object to a collection of items in another object. I am essentially trying to flatten the object for use by a consuming system.
Given the following entity classes:
public class PersonEntity
{
public int Id { get; set; }
public virtual ICollection<OutcomeEntity> Outcomes { get; set; }
}
public class OutcomeEntity
{
public int Id { get; set; }
public bool Outcome { get; set; }
public virtual ICollection<GradeEntity> Grades { get; set; }
public PersonEntity Person { get; set; }
}
public class GradeEntity
{
public int Id { get; set; }
public string Grade { get; set; }
public string MarkersComment { get; set; }
public OutcomeEntity Outcome { get; set; }
}
I need to map the OutcomeEntity and GradeEntity to the following flattened structure where there can be many outcomes, containing many different grades:
public class PersonDTO
{
public int PersonId { get; set; }
public virtual ICollection<GradeDTO> Grades { get; set; }
}
public class GradeDTO
{
public int OutcomeId { get; set; }
public int GradeId { get; set; }
public string Grade { get; set; }
public string MarkersComment { get; set; }
}
Basically, for every Outcome in the collection, I want to iterate over the grades within it and create a new object (GradeDTO).
I have attempted to create a basic map, but I simply cannot get my head around the sub-properties.
To create one collection from many you can use SelectMany extension method. With this method and the following configuration AutoMapper will create PersonDto from PersonEntity.
Mapper.Initialize(cfg =>
{
cfg.CreateMap<GradeEntity, GradeDTO>()
.ForMember(dto => dto.GradeId, x => x.MapFrom(g => g.Id))
.ForMember(dto => dto.OutcomeId, x => x.MapFrom(g => g.Outcome.Id));
cfg.CreateMap<PersonEntity, PersonDTO>()
.ForMember(dto => dto.PersonId, x => x.MapFrom(p => p.Id))
.ForMember(dto => dto.Grades, x => x.MapFrom(p => p.Outcomes.SelectMany(o => o.Grades)));
});
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
Below are my entities. One ContractBill can have multiple SubscriberInvoices.
public class ContractBill
{
public virtual Int32 ContractID { get; set; }
public virtual Int32 CustomerID { get; set; }
public virtual string ContractStatus { get; set; }
public virtual Customer Customer { get; set; }
public virtual IList<SubscriberInvoice> InvoiceList { get; set; }
public ContractBill() { }
}
public class ContractBillMap : ClassMap<ContractBill>
{
public ContractBillMap()
{
Table("GT_CONTRACT");
Id(x => x.ContractID, "GF_CONT_ID").GeneratedBy.Increment();
Map(x => x.CustomerID, "GF_CUST_ID");
Map(x => x.ContractStatus, "GF_CONTRACT_STATUS");
HasMany(x => x.InvoiceList).KeyColumn("GF_CONT_ID").Not.LazyLoad();
}
}
public class SubscriberInvoice : Object
{
virtual public String InvoiceID { get; set; }
virtual public Int32 ContractID { get; set; }
virtual public Int32 CustomerID { get; set; }
virtual public String BillPeriod { get; set; }
virtual public Double Amount { get; set; }
virtual public Double MinimumAmount { get; set; }
virtual public DateTime PayByDate { get; set; }
virtual public String IsPaid { get; set; }
virtual public Double AmountPaid { get; set; }
public SubscriberInvoice()
{
}
}
public class SubscriberInvoiceMap : ClassMap<SubscriberInvoice>
{
public SubscriberInvoiceMap()
{
Table("GT_BILL");
CompositeId()
.KeyProperty(x => x.InvoiceID, "GF_BILL_ID")
.KeyProperty(x => x.ContractID, "GF_CONT_ID")
.KeyProperty(x => x.BillPeriod, "GF_BILL_PERIOD")
Map(x => x.Amount, "GF_AMOUNT");
Map(x => x.MinimumAmount, "GF_MINIMUM_AMOUNT");
Map(x => x.PayByDate, "GF_PAY_BY_DATE");
Map(x => x.IsPaid, "GF_PAID");
Map(x => x.AmountPaid, "GF_AMOUNT_PAID");
}
}
//Get distinct BillContract having more than two non-paid Subscriber Invoices.
ICriteria criteria = session.CreateCriteria(typeof(ContractBill), "cb1");
string aliasName = "invoice";
string prefix = aliasName + ".";
criteria = criteria.CreateAlias("InvoiceList", aliasName);
criteria = criteria.Add(Expression.Eq(prefix + "IsPaid", "N"));
// below detached query will give the count of non paid invoice list
DetachedCriteria crit = DetachedCriteria.For(typeof(ContractBill))
.CreateAlias("InvoiceList", "InvoiceList")
.SetProjection(Projections.ProjectionList()
.Add(Projections.RowCount()))
.Add(Property.ForName("ContractID").EqProperty("cb1.ContractID"));
//Non paid invoice Count = 2
criteria = criteria.Add(Restrictions.Gt(Projections.SubQuery(crit), 2));
//Not giving proper reasult when being used with paging
//criteria = Criteria.SetResultTransformer(Transformers.DistinctRootEntity);
criteria =criteria.SetFirstResult(0).SetMaxResults(10);
//Tried with below detached query, but not getting distinct record
DetachedCriteria crit1 = DetachedCriteria.For(typeof(ContractBill))
.Add(Restrictions.EqProperty ("ContractID", "cb1.ContractID"))
.CreateCriteria("InvoiceList")
.SetProjection(Projections.Distinct(Projections.Id()));
criteria = criteria.Add(Subqueries.PropertyIn("cb1.ContractID", crit1));
IList<ContractBill> matchingObjects = criteria.List<ContractBill>();
Above code is not giving distinct result. Please suggest how to get the distinct ContractBill records.
For displaying records with paging, I need to get the total record count for above criteria also.
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>();
Hi i am using CTP5 to map between two entities like that:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool IsManager { get; set; }
public decimal Credit { get; set; }
public int CreditAlertCount { get; set; }
public decimal TelPrice { get; set; }
public decimal CellPrice { get; set; }
public DateTime InsertDate { get; set; }
public IList<string> PhoneList { get; set; }
public int UserTypeId { get; set; }
public virtual UserType UserType { get; set; }
}
public class UserType
{
public int Id { get; set; }
public int UserLevel { get; set; }
public string TypeDescription { get; set; }
}
//here is configurations
public class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
HasKey(c => c.Id);
Property(c => c.Id).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("ID");
Property(c => c.InsertDate).HasDatabaseGenerationOption(DatabaseGenerationOption.Computed).HasColumnName("INSERT_DATE");
Property(c => c.IsManager).HasDatabaseGenerationOption(DatabaseGenerationOption.Computed).HasColumnName("IS_MANAGER");
Property(c => c.UserName).HasMaxLength(25).IsRequired().HasColumnName("USER_NAME");
Property(c => c.Password).HasMaxLength(25).IsRequired().HasColumnName("USER_PASSWORD");
Property(c => c.CellPrice).IsRequired().HasColumnName("CELL_PRICE");
Property(c => c.TelPrice).IsRequired().HasColumnName("TEL_PRICE");
Property(c => c.CreditAlertCount).IsRequired().HasColumnName("CREDIT_ALERT_COUNT");
Property(c => c.Credit).IsRequired().HasColumnName("CREDIT");
Property(c => c.UserTypeId).IsOptional().HasColumnName("USER_TYPE_ID");
/*relationship*/
HasRequired(p => p.UserType).WithMany().IsIndependent().Map(m => m.MapKey(p => p.Id, "USER_TYPE_ID"));
ToTable("CRMC_USERS", "GMATEST");
}
}
public class UserTypeConfig : EntityTypeConfiguration<UserType>
{
public UserTypeConfig()
{
/*Identity*/
HasKey(c => c.Id);
Property(c => c.Id).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("ID");
/*simple scalars*/
Property(s => s.TypeDescription).IsRequired().HasColumnName("DESCRITPION");
Property(s => s.UserLevel).IsRequired().HasColumnName("USER_LEVEL");
ToTable("CRMC_USER_TYPES", "GMATEST");
}
}
What do i do wrong my User.UserType = null?
How to hell do i map this to work!?
I am dying here for 3 days to work it off.
I'm using DevArt Connection 6.058... some thing
Oracle 10g, C# EntityFramework 4.0
You've setup a required association between User and UserType, therefore you cannot have a User object without a UserType (i.e. User.UserType == null). To be able to do that you need to make 2 changes to your object model and fluent API:
1.Change the type of UserTypeId property to int?:
public int? UserTypeId { get; set; }
2.Remove the code from your fluent API that reads:
HasRequired(p => p.UserType).WithMany().IsIndependent().Map(m => m.MapKey...
You don't need any of those stuff. Everything will be configured by Code First based on convention for you.