I have this very simple class
public class ProductAttributeValuePortal
{
public virtual int ID { get; set; }
public virtual Domain.Entity.Portals.ProductPortal Product { get; set; }
public virtual Attribute Attribute { get; set; }
public virtual string Value { get; set; }
}
with this very simple map
public ProductAttributeValueMap ()
{
Table("DM.dbo.ProductAttributeValues");
Id(x => x.ID, "ProductAttributeValue_id");
References(x => x.Product);
References(x => x.Attribute);
Map(x => x.Value);
}
Each time i make a insert NHibernate makes a Select of the attribute like :
NHibernate: INSERT INTO MachineData.dbo.ProductAttributeValues (Value, Product_id,
Attribute_id) VALUES (#p0, #p1, #p2); select SCOPE_IDENTITY();#p0 = '6745', #p1 = 39, #p2 = 'BSTD'
NHibernate: SELECT attribute_.Attribute_id, attribute_.Name as Name21_, attribute_.AttributeType as Attribut3_21_, attribute_.TagName as TagName21_, attribute_.MapTo as MapTo21_ FROM MachineShared.dbo.Attributes attribute_ WHERE attribute_.Attribute_id=#p0;#p0 = 'DLB'
What am i doing wrong. And where do i find some really uptodate books about nhibernate/Fluent nhibernate
How is ID assigned? If it is identity, then NHibernate has to go back to the DB to get the ID field.
Related
Using NHibernate 5.2, I'm getting an ObjectNotFoundByUniqueKeyException due to erroneous queries being executed with an invalid key when I do the following:
var session = sessionFactory.OpenSession();
var employee = new Employee(){
UserId = "joe",
Certifications = new List<Certification>()
};
employee.Certifications.Add(new Certification() { Employee = employee});
var id = session.Save(employee);
session.Flush();
session.Clear();
var emp = session.Get<Employee>(id);
foreach(var e in emp.Certifications)
{
Console.WriteLine(e.Id);
}
Queries executed
NHibernate: INSERT INTO Employee (UserId) VALUES (#p0); select SCOPE_IDENTITY();#p0 = 'joe' [Type: String (4000:0:0)]
NHibernate: INSERT INTO Certification (UserId) VALUES (#p0); select SCOPE_IDENTITY();#p0 = 'joe' [Type: String (4000:0:0)]
NHibernate: SELECT userquery_0_.Id as id1_0_0_, userquery_0_.UserId as userid2_0_0_ FROM Employee userquery_0_ WHERE userquery_0_.Id=#p0;#p0 = 1 [Type: Int32 (0:0:0)]
NHibernate: SELECT certificat0_.UserId as userid2_1_1_, certificat0_.Id as id1_1_1_, certificat0_.Id as id1_1_0_, certificat0_.UserId as userid2_1_0_ FROM Certification certificat0_ WHERE certificat0_.UserId=#p0;#p0 = 1 [Type: Int32 (0:0:0)]
NHibernate: SELECT userquery_0_.Id as id1_0_0_, userquery_0_.UserId as userid2_0_0_ FROM Employee userquery_0_ WHERE userquery_0_.UserId=#p0;#p0 = '1' [Type: String (4000:0:0)]
I'm expecting #p0 = 'joe' in the final two queries, not 1 and '1'. Can anyone see a problem with the mappings below that would explain this behavior?
Classes / Mappings
public class Employee
{
public virtual int Id { get; set; }
public virtual string UserId { get; set; }
public virtual ICollection<Certification> Certifications { get; set; }
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employee");
Id(x => x.Id);
Map(x => x.UserId);
HasMany(x => x.Certifications)
.KeyColumn("UserId")
.Cascade.All();
}
}
public class Certification
{
public virtual int Id { get; set; }
public virtual Employee Employee { get; set; }
}
public class CertificationMap : ClassMap<Certification>
{
public CertificationMap()
{
Table("Certification");
Id(x => x.Id);
References(x => x.Employee)
.Column("UserId")
.PropertyRef(x => x.UserId);
}
}
Collection is not using primary key of Employee, it is another property instead - PropertyRef. And we must inform one-to-many mapping as well as we do for many-to-one
HasMany(x => x.Certifications)
.KeyColumn("UserId")
.Cascade.All()
// use property, not the ID, when searching for my items
.PropertyRef("UserId")
;
A fact, that this mapping was missing, was causing the ID to be used (the magic 1) when NHibernate was loading collection
I would like to databind the foreign key property Product.CategoryId to a Devexpess Lookupedit in Windows Forms Application.
So
lookEditCategory.DataBindings
.Add(new Binding("EditValue", Product, "CategoryId ", true,
DataSourceUpdateMode.OnPropertyChanged));
lookEditCategory.Properties.Columns.Clear();
lookEditCategory.Properties.NullText = "";
lookEditCategory.Properties.DataSource = CatCol;
lookEditCategory.Properties.ValueMember = "CategoryId";
lookEditCategory.Properties.DisplayMember = "CategoryName";
var col = new LookUpColumnInfo("CategoryName") { Caption = "Type" };
lookEditCategory.Properties.Columns.Add(col);
The problem is that Nhibernate does not expose the foreign key Product.CategoryId. Instead my entity and mapping are like this
public partial class Product
{
public virtual int ProductId { get; set; }
[NotNull]
[Length(Max=40)]
public virtual string ProductName { get; set; }
public virtual bool Discontinued { get; set; }
public virtual System.Nullable<int> SupplierId { get; set; }
[Length(Max=20)]
public virtual string QuantityPerUnit { get; set; }
public virtual System.Nullable<decimal> UnitPrice { get; set; }
public virtual System.Nullable<short> UnitsInStock { get; set; }
public virtual System.Nullable<short> UnitsOnOrder { get; set; }
public virtual System.Nullable<short> ReorderLevel { get; set; }
private IList<OrderDetail> _orderDetails = new List<OrderDetail>();
public virtual IList<OrderDetail> OrderDetails
{
get { return _orderDetails; }
set { _orderDetails = value; }
}
public virtual Category Category { get; set; }
public class ProductMap : FluentNHibernate.Mapping.ClassMap<Product>
{
public ProductMap()
{
Table("`Products`");
Id(x => x.ProductId, "`ProductID`")
.GeneratedBy
.Identity();
Map(x => x.ProductName, "`ProductName`")
;
Map(x => x.Discontinued, "`Discontinued`")
;
Map(x => x.SupplierId, "`SupplierID`")
;
Map(x => x.QuantityPerUnit, "`QuantityPerUnit`")
;
Map(x => x.UnitPrice, "`UnitPrice`")
;
Map(x => x.UnitsInStock, "`UnitsInStock`")
;
Map(x => x.UnitsOnOrder, "`UnitsOnOrder`")
;
Map(x => x.ReorderLevel, "`ReorderLevel`")
;
HasMany(x => x.OrderDetails)
.KeyColumn("`ProductID`")
.AsBag()
.Inverse()
.Cascade.None()
;
References(x => x.Category)
.Column("`CategoryID`");
}
}
}
I cannot add the property CategoryID in my Product entity and mapping because then it will be mapped twice.
Is there any solution?
Yes. Do NOT use your domain entities in the UI.
Sometimes your UI doesn't need (and shouldn't be aware of) all the properties of your domain objects.
Other times, it needs DTOs that contain data from different domain sources (for example- a list of CourseNames for the Student screen), or, like in your case- it needs the data to be represented in a slightly different way.
So the best way would be to create your DTOs with all (and only) the properties needed by the UI.
See this SO question for further details.
To be clear, I am looking to map one entire object and all its properties to different copies of basically the same table. My searches show me how to split an object's properties across multiple tables, but that is not what I am trying to accomplish.
Here is my object model (stripped down):
class Customer
{
public Guid CustomerGuid { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
class Address
{
public Guid AddressGuid { get; set; }
public string Line1 { get; set; }
public string State { get; set; }
}
class Application
{
public Guid ApplicationGuid { get; set; }
public Address Address { get; set; }
public DateTime SubmittedDate { get; set; }
}
The problem is that I need the Address to act sort like a component, but be saved into two separate tables: CustomerAddress and ApplicationAddress, as such:
table Customer
(
CustomerGuid
Name
)
table Application
(
ApplicationGuid
SubmittedDate
)
table CustomerAddress
(
CustomerGuid
Line1
State
)
table ApplicationAddress
(
ApplicationGuid
Line1
State
)
I know I can accomplish one of the mappings using as one-to-one (HasOne) for say Customer to CustomerAddress, but then how can I do the same thing with Application to ApplicationAddress?
for customer and analog for Application
class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.Id, "CustomerGuid").GeneratedBy.GuidComb();
Map(x => x.Name);
Join("CustomerAddress", join =>
{
join.KeyColumn("CustomerGuid");
join.Component(x => x.Address, c =>
{
c.Map(x => x.Line1);
c.Map(x => x.State);
});
});
}
}
I have two tables, one UserDetails and another UserRoles. I mapped them in the following way. With this insert operation went fine but when i try to get the list of roles from user object, an exception is throwing. What i identified is, the SQL generated by Hibernate has USERCODE column as User_id in whare clause. How to over come this problem. Please help me.
User Class:
public virtual string Code { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string MiddleName { get; set; }
.......
public virtual IList<UserRole> Roles { get; set; }
public virtual IList<UserResource> Resources { get; set; }
User Map:
Table("USER_DETAILS");
Schema("PROJECTDOCS");
Id(x => x.Code).Column("CODE").GeneratedBy.Assigned();
Map(x => x.FirstName).Column("FIRSTNAME").Not.Nullable().Length(100);
Map(x => x.LastName).Column("LASTNAME").Length(100).Not.Nullable();
Map(x => x.MiddleName).Column("MIDDLENAME").Length(100);
...
HasMany(x => x.Roles).Inverse().Cascade.All();
UserRole Class:
public virtual long UserRoleID { get; set; }
public virtual User UserCode { get; set; }
public virtual long RoleID { get; set; }
public virtual string CreatedBy { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual string UpdatedBy { get; set; }
public virtual DateTime UpdatedDate { get; set; }
UserRole Map:
Table("USER_ROLES");
Schema("PROJECTDOCS");
Id(x => x.UserRoleID).Column("URID").GeneratedBy.Increment();
Map(x => x.RoleID).Column("ROLEID").Not.Nullable();
Map(x => x.CreatedBy).Column("CREATEDBY").Not.Nullable().Length(36);
Map(x => x.CreatedDate).Column("CREATEDDATE").Not.Nullable().Default(DateTime.Now.ToString());
Map(x => x.UpdatedBy).Column("UPDATEDBY").Not.Nullable().Length(36);
Map(x => x.UpdatedDate).Column("UPDATEDDATE").Not.Nullable().Default(DateTime.Now.ToString());
References(x => x.UserCode).Column("USERCODE");
The final SQL generated was:
[SQL: SELECT roles0_.User_id as User8_1_, roles0_.URID as URID1_, roles0_.URID as URID2_0_, roles0_.ROLEID as ROLEID2_0_, roles0_.CREATEDBY as CREATEDBY2_0_, roles0_.CREATEDDATE as CREATEDD4_2_0_, roles0_.UPDATEDBY as UPDATEDBY2_0_,
roles0_.UPDATEDDATE as UPDATEDD6_2_0_, roles0_.USERCODE as USERCODE2_0_ FROM PROJECTDOCS.USER_ROLES roles0_ WHERE roles0_.User_id=?]
The bolded column name is what going worng.
Thanks in advance,
Pradeep
It looks like you are missing the key column on your HasMany to UserRoles in your UserClass. You need to probably do this:
HasMany(x => x.Roles).KeyColumn("URID").Inverse().Cascade.All();
Using fluentnhibernate i am having a problem with the link table insertion.
Here is my entities
public partial class Item
{
public virtual int Id
{
get;
set;
}
public virtual string Description
{
get;
set;
}
public virtual IList<Category> Categories
{
get;
set;
}
}
public partial class Category
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual string Description
{
get;
set;
}
public virtual IList<Item> Items
{
get;
set;
}
}
Here is my mappings.
public class ItemMapping : ClassMap<Item>
{
public ItemMapping()
{
Table("Item");
Schema("dbo");
Id(x => x.Id);
Map(x => x.Description);
HasManyToMany(x => x.Categories)
.ChildKeyColumn("Item_id")
.ParentKeyColumn("Category_id")
.Table("CategoriesToItems")
.AsSet();
}
}
public class CategoryMapping : ClassMap<Category>
{
public CategoryMapping()
{
Table("Category");
Schema("dbo");
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.Name);
HasManyToMany(x => x.Items)
.ChildKeyColumn("Category_id")
.ParentKeyColumn("Item_id")
.Table("CategoriesToItems")
.AsSet();
}
}
Here is how i add it to collection in my mvc page
var category = CategoryTask.Query(x => x.Id == post.Category).FirstOrDefault();
var item = new Item
{
Categories = new List<Category> { category },
Tags = tags
};
ItemTasks.Save(item);
My question is why it doesnt add the relations in my link table "CategoriesToItems". The table is already in the database with Category_Id (FK, int, not null) and Item_Id (FK, int, not null).
Where is the problem? why it doesnt add it to relation table?
It's hard to say what's really wrong when we can't see what your ItemTasks.Save does under the covers. Are you wrapping your save in a transaction? If not, you should be.
You should call Session.Flush() just before the transaction.Commit() as well.
I am not certain if the problem has been solved, but it looks similar to my problem (fluentnhibernate hasmanytomany same identifier exception).
Also, it looks like your parent and child key columns are backward.