I am trying to map a lookup table to an enum using:
FluentNhibernate 1.3.0.733
NHibernate 3.3.1.4000
I am not able to load objects. If I remove the enum mapping I can load objects.
Code:
Order order = session.Get<Order>(id);
Error:
Provided id of the wrong type. Expected: Order+OrderStatus, got System.Int32
Object:
public class Order
{
public enum OrderStatus
{
PaymentPending = 0
}
public virtual int Id { get; set; }
public virtual Customer Customer { get; set; }
public virtual Address Address { get; set; }
public virtual IList<OrderLine> OrderLines { get; set; }
public virtual OrderStatus Status { get; set; }
public virtual DateTime Created { get; set; }
public Order()
{
OrderLines = new List<OrderLine>();
}
}
Mapping: (I have cut the mapping down to these fields for testing)
public OrderMapping()
{
Table("orders");
Id(x => x.Id);
Id(x => x.Status, "state_id").CustomType<Order.OrderStatus>().Not.Nullable();
References(x => x.Address).Cascade.All().Column("address_id");
References(x => x.Customer).Cascade.All().Column("customer_id");
}
Tables:
CREATE TABLE [order_states] (
[id] INTEGER NOT NULL PRIMARY KEY,
[state] NVARCHAR(50) NOT NULL
);
CREATE TABLE [orders] (
[id] INTEGER NOT NULL PRIMARY KEY,
[customer_id] INTEGER NOT NULL,
[address_id] INTEGER NOT NULL,
[state_id] INTEGER NOT NULL,
[created] DATE,
FOREIGN KEY(customer_id) REFERENCES customers(id),
FOREIGN KEY(address_id) REFERENCES addresses(id),
FOREIGN KEY(state_id) REFERENCES order_states(id)
);
What am I doing wrong?
Caused by a silly error.
I had the the status mapped as an id and not as a property.
Correct mapping:
public OrderMapping()
{
Table("orders");
Id(x => x.Id);
Map(x => x.Status, "state_id").CustomType<Order.OrderStatus>().Not.Nullable();
References(x => x.Address).Cascade.All().Column("address_id");
References(x => x.Customer).Cascade.All().Column("customer_id");
}
Related
I want to map a class that has a property of type ICollection<> using NHibernate mapping by code. The code below works. But I don't like the extra Person property within CarSet to make the mapping work.
public class PersonSet
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<CarSet> Cars { get; set; }
}
public class CarSet
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual PersonSet Person { get; set; }
}
public class PersonSetMap : ClassMapping<PersonSet>
{
public PersonSetMap()
{
Id(x => x.Id, m=>m.Generator(Generators.Identity));
Property(x=>x.Name);
Set(x => x.Cars, c =>
{
c.Key(k =>
{
k.Column("PersonId");
});
c.Cascade(Cascade.Persist);
c.Lazy(CollectionLazy.NoLazy);
}, r =>
{
r.OneToMany();
}
);
}
}
public class CarSetMap : ClassMapping<CarSet>
{
public CarSetMap()
{
Id(x => x.Id, m => m.Generator(Generators.Identity));
Property(x => x.Name);
ManyToOne(x => x.Person, m =>
{
m.Column("PersonId");
m.Cascade(Cascade.None);
m.NotNullable(true);
});
}
}
public void Save(){
using (var session = Cfg.Session)
using (var tx = session.BeginTransaction())
{
PersonSet John = new PersonSet { Name = PersonName.John };
John.Cars = new List<CarSet> {
new CarSet { Name = CarnName.BMW,Person = John},
new CarSet { Name = CarnName.BM,Person = John }};
session.Save(entity);
tx.Commit();
}
}
The code above generates SQL script below:
create table PersonSet (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
primary key (Id)
)
create table CarSet (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
PersonId INT not null,
primary key (id)
)
alter table CarSet
add constraint FKF967D6489A220265
foreign key (PersonId)
references PersonSet
What I want is to generate SQL script with difference shown below, and keep the rest the same:
create table CarSet (
Name NVARCHAR(255) null,
PersonId INT not null,
)
Ideally I want the CarSet like this instead:
public class CarSet
{
public virtual int PersonId { get; set; }
public virtual string Name { get; set; }
}
Any idea?
map Cars as ComponentCollection
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Car> Cars { get; set; }
}
class Car
{
public virtual Person Owner { get; set; }
public virtual string Name { get; set; }
}
public class PersonMap : ClassMapping<Person>
{
public PersonMap()
{
Id(x => x.Id, m => m.Generator(Generators.Identity));
Property(x => x.Name);
Set(x => x.Cars, c =>
{
c.Key(k => k.Column("PersonId"));
c.Cascade(NHibernate.Mapping.ByCode.Cascade.Persist);
c.Lazy(CollectionLazy.NoLazy);
}, r =>
{
r.Component(c =>
{
c.Parent(x => x.Owner);
c.Property(x => x.Name);
});
});
}
}
Your ideal solution isn't possible. To use a CarSet table without it's own ID column it has to be a component, but component sets can't have nullable columns. If it's ok for you to mark Name as not-null you can adapt the solution Firo posted.
If that's not ok you can at least solve your first request to remove the Person property. Just delete the property and mark the key column in your set mapping as not-nullable. CarSet will still be an entity (and therefore have it's own ID) but you don't need the reference to PersonSet in code.
Btw, why are your classes postfixed by Set? Just naming them Person and Car would be much better since they only represent one person or car, not a collection of them.
What is wrong with my class mappings below?
public class Foo
{
public Foo()
{
ActualCurve = new List<Point>();
TargetCurve = new List<Point>();
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Point> ActualCurve { get; set; }
public virtual IList<Point> TargetCurve { get; set; }
}
public class Point
{
public virtual int Id { get; set; }
public virtual double X { get; set; }
public virtual double Y { get; set; }
}
public class FooMapping() : ClassMap<Foo>
{
Table("Foos");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name).Not.Nullable();
HasMany(x => x.ActualCurve ).Cascade.All();
HasMany(x => x.TargetCurve ).Cascade.All();
}
public class PointMapping() : ClassMap<Point>
{
Table("Points");
Not.LazyLoad();
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.X).Not.Nullable();
Map(x => x.Y).Not.Nullable();
}
These mappings produce
CREATE TABLE [dbo].[Foos](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar] NOT NULL,
[RecipeRevisionId] [int] NOT NULL
CREATE TABLE [dbo].[Points](
[Id] [int] IDENTITY(1,1) NOT NULL,
[X] [float] NOT NULL,
[Y] [float] NOT NULL,
[FooId] [int] NOT NULL
Essentially, the problem is that when I am pulling a persisted Foo object back out of the database, the Foo.ActualCurve and Foo.TargetCurve lists both get populated with the combined contents of both lists. There clearly isn't a column that keys which set of points belong to the correct curve within Foo, but I am not sure how to change the mapping to maintain the two distinct sets of Points.
I think you will need to specify a separate column for the references:
HasMany(x => x.ActualCurve ).Cascade.All().KeyColumn("ActualCurveId");
HasMany(x => x.TargetCurve ).Cascade.All().KeyColumn("TargetCurveId");
EDIT: Column->KeyColumn
I've been sitting here for an hour trying to figure this out...
I've got 2 tables (abbreviated):
CREATE TABLE TRUST
(
TRUSTID NUMBER NOT NULL,
ACCTNBR VARCHAR(25) NOT NULL
)
CONSTRAINT TRUST_PK PRIMARY KEY (TRUSTID)
CREATE TABLE ACCOUNTHISTORY
(
ID NUMBER NOT NULL,
ACCOUNTNUMBER VARCHAR(25) NOT NULL,
TRANSAMT NUMBER(38,2) NOT NULL
POSTINGDATE DATE NOT NULL
)
CONSTRAINT ACCOUNTHISTORY_PK PRIMARY KEY (ID)
I have 2 classes that essentially mirror these:
public class Trust
{
public virtual int Id {get; set;}
public virtual string AccountNumber { get; set; }
}
public class AccountHistory
{
public virtual int Id { get; set; }
public virtual Trust Trust {get; set;}
public virtual DateTime PostingDate { get; set; }
public virtual decimal IncomeAmount { get; set; }
}
How do I do the many-to-one mapping in FluentNHibernate to get the AccountHistory to have a Trust? Specifically, since it is related on a different column than the Trust primary key of TRUSTID and the column it is referencing is also named differently (ACCTNBR vs. ACCOUNTNUMBER)???? Here's what I have so far - how do I do the References on the AccountHistoryMap to Trust???
public class TrustMap : ClassMap<Trust>
{
public TrustMap()
{
Table("TRUST");
Id(x => x.Id).Column("TRUSTID");
Map(x => x.AccountNumber).Column("ACCTNBR");
}
}
public class AccountHistoryMap : ClassMap<AccountHistory>
{
public AccountHistoryMap()
{
Table("TRUSTACCTGHISTORY");
Id (x=>x.Id).Column("ID");
References<Trust>(x => x.Trust).Column("ACCOUNTNUMBER").ForeignKey("ACCTNBR").Fetch.Join();
Map(x => x.PostingDate).Column("POSTINGDATE");
);
I've tried a few different variations of the above line but can't get anything to work - it pulls back AccountHistory data and a proxy for the Trust; however it says no Trust row with given identifier.
This has to be something simple. Anyone?
Thanks in advance.
You need to use property-ref:
public class AccountHistoryMap : ClassMap<AccountHistory>
{
public AccountHistoryMap()
{
Table("TRUSTACCTGHISTORY");
Id (x=>x.Id).Column("ID");
References(x => x.Trust, "ACCOUNTNUMBER").PropertyRef("ACCTNBR").Fetch.Join();
Map(x => x.PostingDate).Column("POSTINGDATE");
}
}
I am creating a NHibenate application with one to many relationship. Like City and State data.
City table
CREATE TABLE [dbo].[State](
[StateId] [varchar](2) NOT NULL primary key,
[StateName] [varchar](20) NULL)
CREATE TABLE [dbo].[City](
[Id] [int] primary key IDENTITY(1,1) NOT NULL ,
[State_id] [varchar](2) NULL refrences State(StateId),
[CityName] [varchar](50) NULL)
My mapping is follows
public CityMapping()
{
Id(x => x.Id);
Map(x => x.State_id);
Map(x => x.CityName);
HasMany(x => x.EmployeePreferedLocations)
.Inverse()
.Cascade.SaveUpdate();
References(x => x.State)
//.Cascade.All();
//.Class(typeof(State))
//.Not.Nullable()
.Cascade.None()
.Column("State_id");
}
public StateMapping()
{
Id(x => x.StateId)
.GeneratedBy.Assigned();
Map(x => x.StateName);
HasMany(x => x.Jobs)
.Inverse();
//.Cascade.SaveUpdate();
HasMany(x => x.EmployeePreferedLocations)
.Inverse();
HasMany(x => x.Cities)
// .Inverse()
.Cascade.SaveUpdate();
//.Not.LazyLoad()
}
Models are as follows:
[Serializable]
public partial class City
{
public virtual System.String CityName { get; set; }
public virtual System.Int32 Id { get; set; }
public virtual System.String State_id { get; set; }
public virtual IList<EmployeePreferedLocation> EmployeePreferedLocations { get; set; }
public virtual JobPortal.Data.Domain.Model.State State { get; set; }
public City(){}
}
public partial class State
{
public virtual System.String StateId { get; set; }
public virtual System.String StateName { get; set; }
public virtual IList<City> Cities { get; set; }
public virtual IList<EmployeePreferedLocation> EmployeePreferedLocations { get; set; }
public virtual IList<Job> Jobs { get; set; }
public State()
{
Cities = new List<City>();
EmployeePreferedLocations = new List<EmployeePreferedLocation>();
Jobs = new List<Job>();
}
//public virtual void AddCity(City city)
//{
// city.State = this;
// Cities.Add(city);
//}
}
My Unit Testing code is below.
City city = new City();
IRepository<State> rState = new Repository<State>();
Dictionary<string, string> critetia = new Dictionary<string, string>();
critetia.Add("StateId", "TX");
State frState = rState.GetByCriteria(critetia);
city.CityName = "Waco";
city.State = frState;
IRepository<City> rCity = new Repository<City>();
rCity.SaveOrUpdate(city);
City frCity = rCity.GetById(city.Id);
The problem is , I am not able to insert record. The error is below.
"Invalid index 2 for this SqlParameterCollection with Count=2."
But the error will not come if I comment State_id mapping field in the CityMapping file. I donot know what mistake is I did. If do not give the mapping Map(x => x.State_id); the value of this field is null, which is desired. Please help me how to solve this issue.
Few remarks:
Remove this State_id property from the City class and the mapping. You already have a State property so it makes no sense in your object model.
Those Jobs and EmployeePreferedLocations properties in the State class don't have any related columns/tables in your database (at least the one you've shown here) while you have mappings for them.
New to NHibernate. Having trouble wrapping my head around how to map this legacy table.
CREATE TABLE [dbo].[CmnAddress](
[addressId] [int] NOT NULL,
[objectType] [varchar](63) NULL,
[objectId] [int] NULL,
[addressType] [varchar](7) NULL,
[recordStatus] [char](1) NULL,
[fromDate] [int] NULL,
[toDate] [int] NULL,
[onStreet] [varchar](254) NULL,
[atStreet] [varchar](254) NULL,
[unit] [varchar](30) NULL,
[city] [varchar](254) NULL,
[state] [varchar](30) NULL,
[zipCode] [varchar](30) NULL,
)
There is also a "CmnPerson" table that I have mapped to a Person class. I need the Person class to contain a list of Addresses, where the objectType column contains "CmnPerson" and the objectId field matches my Person.Id ("CmnPerson.personId") field.
I am also later going to have to create a Contact class that also contains a list of Addresses where the objectType column contains "CmnContact".
I'm having a very tough time figuring out if I should be using the Any mapping or class-hierarchy-per-table with discrimination on subcolumns? Or if either of those will even meet my needs.
Can anyone show my how to map this Address class? Fluent config would be preferable.
ADDED INFO:
The following classes and mappings almost work, but the Addresses list return ALL rows from the CmnAddress table with a matching objectId, regardless of the objectType field's value. I think I could use an ApplyFilter on the HasMany mapping for Person.Addresses, but that doesn't seem like the "right" way.
More added info: I was able to resolve this last issue by chaining AlwaysSelectWithValue() on after the call to DiscriminateSubClassesOnColumn(...)
public class Person
{
public virtual int Id { get; private set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual string MiddleName { get; set; }
public virtual string Gender { get; set; }
public virtual IList<PassClient> PassClients { get; set; }
public virtual IList<PersonAddress> Addresses { get; set; }
}
public class PersonMap : ClassMap<Person>
{
public PersonMap() {
Table("CmnPerson");
Id(x => x.Id).Column("personId");
Map(x => x.LastName);
Map(x => x.FirstName);
Map(x => x.MiddleName);
Map(x => x.Gender);
HasMany(x => x.PassClients).KeyColumn("personId");
HasMany(x => x.Addresses).KeyColumn("objectId");
}
}
abstract public class Address
{
public virtual int Id { get; private set; }
public virtual string StreetNo { get; set; }
public virtual string OnStreet { get; set; }
public virtual string Unit { get; set; }
public virtual string City { get; set; }
public virtual string State { get; set; }
public virtual string ZipCode { get; set; }
}
public class PersonAddress : Address {
public virtual Person Person { get; set; }
}
public class AddressMap : ClassMap<Address>
{
public AddressMap() {
Table("CmnAddress");
Id(x => x.Id).Column("addressId");
Map(x => x.StreetNo);
Map(x => x.OnStreet);
Map(x => x.Unit);
Map(x => x.City);
Map(x => x.State);
Map(x => x.ZipCode);
DiscriminateSubClassesOnColumn("objectType").AlwaysSelectWithValue();
}
}
public class PersonAddressMap : SubclassMap<PersonAddress>
{
public PersonAddressMap() {
DiscriminatorValue("CmnPerson");
References(x => x.Person).Column("objectId");
}
}
You can not use the Any-mapping when it's a has many relation. It can be used, when an address can point to different kinds of objects - like a person, an order or other things not related.
To map the hierarachy you can do it like this:
public class CmnAddressMap : ClassMap<CmnAddress>
{
public CmnAddressMap()
{
Id(x => x.addressId);
Map(x => x...);
DiscriminateSubClassesOnColumn("objectType");
}
}
public class PersonAdressMap : SubclassMap<PersonAddress>
{
public PersonAdressMap()
{
DiscriminatorValue("objectType1");
}
}
public class ContactAdressMap : SubclassMap<ContactAddress>
{
public ContactAdressMap()
{
DiscriminatorValue("objectType2");
}
}
Have an abstract CmnAddress with all the fields (map all the fields in the CmnAdressMap for example) and two subclasses named PersonAddress and ContactAddress.
And the person should then have a collection like IList which should mapped with HasMany. And you should be done.
Sorry, am not familiar with fluent mappings, however, one way to do it would be:
Create an Address abstract class with properties corresponding to all the columns in your table except for objectType
Create a PersonAddress class which extends Address
Create a ContactAddress class which extends Address
Map all the columns of CmnAddress to properties of the Address class in the normal way, except for objectType which you declare as the discriminator column
Map PersonAddress as a subclass of Address with discriminator value "CmnPerson"
Map ContactAddress as a subclass of Address with a discriminator value of "CmnContact"
In code, your Person class would hold a list of PersonAddresses and your Contact class would hold a list of ContactAddresses
Address class
public class Address
{
public virtual int Id { get; set; }
public virtual Person Person { get; set; }
// etc.
}
Person class
public class Person
{
public Person()
{
Addresses = new List<Address>();
}
public virtual int Id { get; set; }
// etc.
public virtual IList<Address> Addresses { get; set; }
}
AddressMap
public class AddressMap : ClassMap<Address>
{
public AddressMap()
{
Table("CmnAddress");
Id(x => x.Id).Column("addressId");
// etc.
References(x => x.Person);
}
}
To separate the difference between Address => Person and Address => Contact, you'll want to read up on NHibernate's polymorphism and discriminate sub classes based on column.