Fluent NHibernate - Error with HasMany in new version - visual-studio-2010

I Upgrade my NHibernate for 3.1.0.4000 and Fluent for 1.2.0.712 and have some problems with HasMany...
my entities:
public class MateriaPrima
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
public virtual DateTime Date { get; set; }
public virtual decimal Price { get; set; }
}
public class Product
{
public virtual int Id { get; set; }
public virtual IList<ProductMateriaPrima> ListMateriaPrima { get; set; }
public virtual string Description { get; set; }
public virtual decimal Price { get; set; }
public virtual DateTime Date { get; set; }
public Product()
{
this.ListaMateriaPrima = new List<ProductMateriaPrima>();
}
}
public class ProductMateriaPrima
{
public virtual int Id { get; set; }
public virtual Product Product {get;set;}
public virtual MateriaPrima MateriaPrima { get; set; }
public virtual decimal PrecoCusto {get;set;}
}
And Maps:
public class MateriaPrimaMap : ClassMap<MateriaPrima>
{
public MateriaPrimaMap()
{
Id(m => m.Id).Length(11).Not.Nullable();
Map(m => m.Description).Length(90).Not.Nullable();
Map(m => m.Date).Not.Nullable();
Map(m => m.Price).Not.Nullable();
}
}
public class ProductMateriaPrimaMap : ClassMap<ProductoMateriaPrima>
{
public ProductMateriaPrimaMap()
{
Id(c => c.Id).Length(11);
Map(c => c.Price).Not.Nullable();
References(c => c.MateriaPrima).Column("IdMateriaPrima").Not.LazyLoad();
References(c => c.Product).Column("IdProduct").Not.LazyLoad();
}
}
public class ProdutoMap : ClassMap<Produto>
{
public ProdutoMap()
{
Id(m => m.Id).Length(11).Not.Nullable();
Map(m => m.Description).Length(90).Not.Nullable();
Map(m => m.Price).Length(10);
Map(m => m.Date).Length(12);
Map(m => m.Active).Not.Nullable();
HasMany(x => x.ListaMateriaPrima)
.Table("ProdutoMateriaPrima")
.KeyColumn("IdProduto")
.KeyColumn("IdMateriaPrima")
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
When i try to search, i got the error: {"Unknown column 'listamater0_.MateriaPrima_id' in 'field list'"}
This error don´t happen when i had the old version of Nhibernate and Fluent...
Someone know what´s happening?
Thanks for the help...

This error happend because you didn't set collumn name of Id property, default pattern for id is: entityname_id.
And now in ProductMateriaPrimaMap class you wrote:
References(c => c.MateriaPrima).Column("IdMateriaPrima")
This mean that in you MateriaPrimaMap class you should have Id with collumn name: IdMateriaPrima, but default you have name: MateriaPrima_Id.
I can't say where the problem is exactly because you didn't show the query. But i know that this error happend when Id column name is diffrent from Reference column name.

Related

Linq Issue dealing with null values in select

I have the following entities in my solution
public class UtilityAccount : IObjectWithState
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UtilityAccountID { get; set; }
public string Account { get; set; }
[ForeignKey("Person")]
public Guid PersonID { get; set; }
public virtual Person Person { get; set; }
public string ForeignID { get; set; }
[NotMapped]
public ObjectState ObjectState { get; set; }
public virtual ICollection<Utility> Utilities { get; set; }
public UtilityAccount()
{
Utilities = new List<Utility>();
}
}
public class Utility : IObjectWithState
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UtilityID { get; set; }
[ForeignKey("UtilityAccount")]
public Guid UtilityAccountID { get; set; }
public virtual UtilityAccount UtilityAccount { get; set; }
public Guid? ServiceAddressID { get; set; }
[ForeignKey("ServiceAddressID")]
public virtual Address ServiceAddress { get; set; }
[NotMapped]
public ObjectState ObjectState { get; set; }
public double CurrentBalance { get; set; }
public double? PendingPaymentTotal { get; set; }
public string ForeignID { get; set; }
[ForeignKey("UtilityType")]
public Guid UtilityTypeID { get; set; }
public virtual UtilityType UtilityType { get; set; }
public virtual ICollection<UtilityBill> UtilityBills { get; set; }
public virtual ICollection<IncomingUtilityPayment> IncomingPayments { get; set; }
public Utility()
{
UtilityBills = new List<UtilityBill>();
IncomingPayments = new List<IncomingUtilityPayment>();
}
}
public class IncomingUtilityPayment : IObjectWithState
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid IncomingPaymentID { get; set; }
public string ForeignID { get; set; }
[ForeignKey("Utility")]
public Guid UtilityID { get; set; }
public virtual Utility Utility { get; set; }
public DateTime PaymentDate { get; set; }
public IncomingPaymentStatus IncomingPaymentStatus { get; set; }
public double? UtilityAmount { get; set; }
public double? ConvenienceFee { get; set; }
public double? TotalAmount { get; set; }
public string AuthCode { get; set; }
public string AuthReference { get; set; }
public string TenderType { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PaymentIdent { get; set; }
[NotMapped]
public ObjectState ObjectState { get; set; }
}
My problem is that I am trying to use Linq to retrieve information about a UtilityAccount and I am running into issues with the IncomingPayments for a Utility. Below is the select statement I am trying to use.
returnVal = repo.AllIncluding(o => o.Person, o => o.Utilities, o => o.Utilities.Select(p => p.UtilityType), o => o.Person.BillingAddress, o => o.Utilities.Select(p => p.ServiceAddress), o => o.Utilities.Select(p => p.IncomingPayments.Where(q => q.IncomingPaymentStatus == IncomingPaymentStatus.Pending || q.IncomingPaymentStatus == IncomingPaymentStatus.Processed )));
Everything ran fine until I added this clause to the statement.
o => o.Utilities.Select(p => p.IncomingPayments.Where(q => q.IncomingPaymentStatus == IncomingPaymentStatus.Pending || q.IncomingPaymentStatus == IncomingPaymentStatus.Processed ))
I think my issue ends up being something I am writing wrong in my Linq clause. The error I am getting is
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
I can use the following statement with no issues
o => o.Utilities.Select(p => p.IncomingPayments)
as soon as I add the where clause in I get the error
I'm not familiar with EntityFramework nor linq-to-entities, but if it's just like linq-to-object you can:
add a .Where(p => p.IncomingPayments != null) before chaining with your .Select() like this
o.Utilities.Where(p => p.IncomingPayments != null)
.Select(p => p.IncomingPayments.Where(q => q.IncomingPaymentStatus == IncomingPaymentStatus.Pending || q.IncomingPaymentStatus == IncomingPaymentStatus.Processed))
The result will be a nested IEnumerable, i.e. IEnumerable<IEnumerable<IncomingUtilityPayment>>
If you actually need a IEnumerable<IncomingUtilityPayment> then .SelectMany() come in to play.
o.Utilities.Where(p => p.IncomingPayments != null)
.SelectMany(p => p.IncomingPayments)
.Where(q => q.IncomingPaymentStatus == IncomingPaymentStatus.Pending || q.IncomingPaymentStatus == IncomingPaymentStatus.Processed)
Hope this help

Data Annotation for foreign key relationship

I have two classes
public class Project
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int ManagerID { get; set; }
public int CoordID { get; set; }
[ForeignKey("ManagerID")]
public virtual Employee Manager { get; set; }
[ForeignKey("CoordID")]
public virtual Employee Coord { get; set; }
}
public class Employee
{
[Key]
public int EmpID { get; set; }
public string Name { get; set; }
[InverseProperty("ManagerID")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("CoordID")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
The ManagerID and CoordID map to the EmpID column of the Employee table.
I keep getting an error for Invalid Columns becauce EF is not able to map correctly. I think it is looking for wrong column.
I think InverseProperty is used to refer to the related navigation property, not the foreign key, e.g.
public class Employee
{
[Key]
public int EmpID { get; set; }
public int Name { get; set; }
[InverseProperty("Manager")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("Coord")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
Also, is there a reason why your names are ints and not strings?
Best guess would be to use fluent API in your context via OnModelCreating. By renaming the column, EF can't figure out the original object to map so it's confused. However, Fluent API allows you to manually specify the map using something like the following:
public class MyContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Project> Projects { get; set; }
protected override OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasRequired(x => x.Manager)
.WithMany(x => x.ManagerProjects)
.HasForeignKey(x => x.ManagerID);
modelBuilder.Entity<Project>()
.HasRequired(x => x.Coord)
.WithMany(x => x.CoordProjects)
.HasForeignKey(x => x.CoordID);
}
}

cant insert complex object to database using entity frame work

I am developing an asp.net mvc application, which has these enity classes:
public class Person
{
public int PersonID { get; set; }
public string PersonPicAddress { get; set; }
public virtual List<Person_Local> PersonLocal { get; set; }
}
public class Person_Local
{
public int PersonID { get; set; }
public int CultureID { get; set; }
public string PersonName { get; set; }
public string PersonFamily { get; set; }
public string PersonAbout { get; set; }
public virtual Culture Culture { get; set; }
public virtual Person Person { get; set; }
}
public class Culture
{
public int CultureID { get; set; }
[Required()]
public string CultureName { get; set; }
[Required()]
public string CultureDisplay { get; set; }
public virtual List<HomePage> HomePage { get; set; }
public virtual List<Person_Local> PersonLocak { get; set; }
}
I defined an action with [Httppost] attribute, which accepts complex object from a view.
Here is the action :
[HttpPost]
public ActionResult CreatePerson([Bind(Prefix = "Person")]Person obj)
{
AppDbContext da = new AppDbContext();
//Only getting first PersonLocal from list of PersonLocals
obj.PersonLocal[0].Person = obj;
da.Persons.Add(obj);
da.SaveChanges();
return Jsono(...);
}
But when it throws error as below :
Exception:Thrown: "Invalid column name 'Culture_CultureID'." (System.Data.SqlClient.SqlException)
A System.Data.SqlClient.SqlException was thrown: "Invalid column name 'Culture_CultureID'."
And the insert statement :
ADO.NET:Execute Reader "insert [dbo].[Person_Local]([PersonID], [PersonName], [PersonFamily], [PersonAbout], [Culture_CultureID])
values (#0, #1, #2, #3, null)
select [CultureID]
from [dbo].[Person_Local]
where ##ROWCOUNT > 0 and [CultureID] = scope_identity()"
The command text "insert [dbo].[Person_Local]([PersonID], [PersonName], [PersonFamily], [PersonAbout], [Culture_CultureID])
values (#0, #1, #2, #3, null)
select [CultureID]
from [dbo].[Person_Local]
where ##ROWCOUNT > 0 and [CultureID] = scope_identity()" was executed on connection "Data Source=bab-pc;Initial Catalog=MainDB;Integrated Security=True;Application Name=EntityFrameworkMUE", building a SqlDataReader.
Where is the problem?
Edited:
Included EntityConfigurations Code:
public class CultureConfig : EntityTypeConfiguration<Culture>
{
public CultureConfig()
{
HasKey(x => x.CultureID);
Property(x => x.CultureName);
Property(x => x.CultureDisplay);
ToTable("Culture");
}
}
public class PersonConfig : EntityTypeConfiguration<Person>
{
public PersonConfig()
{
HasKey(x => x.PersonID);
Property(x=>x.PersonPicAddress);
ToTable("Person");
}
}
public class Person_LocalConfig : EntityTypeConfiguration<Person_Local>
{
public Person_LocalConfig()
{
HasKey(x => x.PersonID);
HasKey(x => x.CultureID);
Property(x=>x.PersonName);
Property(x => x.PersonFamily);
Property(x => x.PersonAbout);
ToTable("Person_Local");
}
}
Try to remove fields CultureID and PersonID from Person_Local class. Because you already has field Person and Culture
It looks like your schema is out of sync with your model. Make sure you understand EF Code first schema update features, which are described in this blog. If you need more sophisticated schema migration, there are some other approaches in answers to this question.

Creating a Many-to-Many Relationship in EF4 Code First

I am working on a project in ASP.NET MVC using EF4 Code-First for the modeling. I have the following model classes:
public class ComicBook
{
public long ComicBookID { get; set; }
public string IssueTitle { get; set; }
public int IssueNumber { get; set; }
public DateTime PublishDate { get; set; }
public IList<ComicBookPerson> Writers { get; set; }
public IList<ComicBookPerson> Pencillers { get; set; }
public IList<User> CollectingUsers { get; set; }
}
public class ComicBookPerson
{
public long ComicBookPersonID { get; set; }
public string Name { get; set; }
public IList<ComicBook> WorkedOnComicBooks { get; set; }
}
public class User
{
[Key]
public long UserID { get; set; }
public string Email { get; set; }
public IList<ComicBook> CollectedBooks { get; set; }
}
And the following DbContext:
public class ComicContext : DbContext
{
public DbSet<ComicBook> ComicBooks { get; set; }
public DbSet<ComicBookPerson> ComicBookPerson { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Writers)
.WithMany(w => w.WorkedOnComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("WriterID")
.ToTable("ComicBook_Writers"));
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Pencillers)
.WithMany(p => p.WorkedOnComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("PencillerID")
.ToTable("ComicBook_Penciller"));
}
}
The rules that need to apply are:
Users can have many ComicBooks in their collection
ComicBooks can be in many users' collections
Each ComicBook can have 1 or more Writers
Each ComicBook can have 1 or more Pencillers
A ComicBookPerson can be a Writer or a Penciller on any book
A ComicBookPerson can work on many books, as a Writer, Penciller, or both
The many-to-many schema between Users and ComicBooks is created fine. It is the many-to-many between ComicBooks and ComicBookPersons that is breaking. The error message I get is:
Schema specified is not valid. Errors:
(15,6) : error 0040: Type ComicBook_Writers is not defined in namespace Comics.Models (Alias=Self).
I basically want two join tables, one called ComicBook_Writers and one called ComicBookPencillers. Both tables will contain a ComicBookID and a ComicBookPersonID. Is this possible in EF Code First?
You need two collection properties in ComicBookPerson class that relates to writers and pencillers.
public class ComicBookPerson
{
public long ComicBookPersonID { get; set; }
public string Name { get; set; }
public IList<ComicBook> WroteComicBooks { get; set; }
public IList<ComicBook> PenciledComicBooks { get; set; }
}
Model mapped as
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Writers)
.WithMany(w => w.WroteComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("WriterID")
.ToTable("ComicBook_Writers"));
modelBuilder.Entity<ComicBook>()
.HasMany(b => b.Pencillers)
.WithMany(p => p.PenciledComicBooks)
.Map(t => t.MapLeftKey("ComicBookID")
.MapRightKey("PencillerID")
.ToTable("ComicBook_Penciller"));
}
I think this model is close to be right, but still missing something.
The ComicBookPerson can be both writer and penciller, that means you still need a third list in your class ComicBookPerson:
public IList<ComicBook> WroteComicBooks { get; set; }
public IList<ComicBook> PenciledComicBooks { get; set; }
public IList<ComicBook> WrotPencComicBooks { get; set; }
Am I right?

Deletion issue using fluent nhibernate in mvc 3

I've a layer name MVCTemplate.Common, having two folders Entities and Mappings. In Entities User.cs and Role.cs is as under respectively:
using System;
using System.Collections.Generic;
namespace MVCTemplate.Common.Entities
{
public class User
{
public virtual Guid UserID { get; set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
public virtual string FullName { get; set; }
public virtual string Email { get; set; }
public virtual TimeSpan LastLogin { get; set; }
public virtual bool IsActive { get; set; }
public virtual DateTime CreationDate { get; set; }
public virtual IList<Role> UserInRoles { get; set; }
public User()
{
UserInRoles = new List<Role>();
}
}
}
using System.Collections.Generic;
namespace MVCTemplate.Common.Entities
{
public class Role
{
public virtual int? RoleID { get; set; }
public virtual string RoleName { get; set; }
public virtual bool IsActive { get; set; }
public virtual string Description { get; set; }
public virtual IList<User> Users { get; set; }
//public virtual IList<RolePermission> RolePermission { get; set; }
public Role()
{
Users = new List<User>();
}
public virtual void AddUsers(User _user)
{
_user.UserInRoles.Add(this);
Users.Add(_user);
}
}
}
Now, In Mappings folder their mappings files is as under:
using FluentNHibernate.Mapping;
using MVCTemplate.Common.Entities;
namespace MVCTemplate.Common.Mappings
{
public class RoleMap : ClassMap<Role>
{
public RoleMap()
{
Table("tblRoles");
Id(role => role.RoleID).GeneratedBy.Identity();
Map(role => role.RoleName).Not.Nullable();
Map(role => role.IsActive).Not.Nullable();
Map(role => role.Description).Not.Nullable();
HasManyToMany(role => role.Users).Cascade.All().Table("tblUserInRoles");
//HasMany(role => role.User);
//HasMany(role => role.RolePermission);
}
}
}
using FluentNHibernate.Mapping;
using MVCTemplate.Common.Entities;
namespace MVCTemplate.Common.Mappings
{
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("tblUsers");
Id(user => user.UserID).GeneratedBy.Guid();
Map(user => user.UserName).Not.Nullable();
Map(user => user.Password).Not.Nullable();
Map(user => user.FullName).Not.Nullable();
Map(user => user.Email).Not.Nullable();
//Map(user => user.LastLogin).Nullable();
Map(user => user.IsActive).Not.Nullable();
Map(user => user.CreationDate).Not.Nullable();
HasManyToMany(user => user.UserInRoles).Cascade.All().Table("tblUserInRoles");
//HasMany(user => user.UserInRoles);
}
}
}
Now, from controller, I got specific user object in user object and now want to delete it, the simple code is as under:
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using MVCTemplate.BussinessLayer.Facade;
using MVCTemplate.Common.Entities;
namespace MVCTemplate.Web.Controllers
{
public class HomeController : Controller
{
private UserManagement _userManagement;
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
_userManagement = new UserManagement();
var oUser = _userManagement.GetUserBy(new Guid("4fb5856a-58d9-4b78-8d08-bce645bc93c7"));
oUser.UserInRoles = new List<Role>();
_userManagement.Delete(oUser);
return View();
}
public ActionResult About()
{
return View();
}
}
}
Now, I just want to delete that user as this user is not exist in any role and there is no entry of it in its junction table(UserInRole). I initialize its Role collection to empty for Count=0, but after calling deleting method it throw an error with sqlQuery which is as under:
could not delete collection: [MVCTemplate.Common.Entities.User.UserInRoles#4fb5856a-58d9-4b78-8d08-bce645bc93c7][SQL: DELETE FROM tblUserInRoles WHERE User_id = #p0]
Note that in Junction table tblUserInRole there no field of that name User_id as mentioned in error, due to which in the inner exception it is telling that column name User_id not exist. But in junction table I have only two field 1) UserID and 2) RoleID.
Any suggestion how to resolve it?
looks like a mapping error to me. Change:
HasManyToMany(user => user.UserInRoles).Cascade.All().Table("tblUserInRoles");
To
HasManyToMany(user => user.UserInRoles)
.Cascade.All()
.Table("tblUserInRoles")
.ParentKeyColumn("UserID")
.ChildKeyColumn("RoleID");

Resources