NHibernate OneToMany Mapping By Code - c#

I saw many exemple on the internet on Mapping relation object with nhibernate but i can't make mine works.
I have two Model for exemple:
public class Vehicule
{
public virtual int Id { get; set; }
public virtual int Brand { get; set; }
public virtual int Color { get; set; }
public virtual int UserID { get; set; }
public virtual UserModel User { get; set; }
}
public class VehiculeMap: ClassMapping<Vehicule>
{
public VehiculeMap()
{
Table("G1Vehicule");
Id(x => x.Id, map => { map.Column("id"); });
Property(x => x.Brand, map => { map.Column("brand"); });
Property(x => x.Color, map => { map.Column("color"); });
Property(x => x.UserID, map => { map.Column("user_id"); });
}
}
public class UserModel
{
public virtual int Id { get; set; }
public virtual int Username { get; set; }
}
public class UserModelMap : ClassMapping<UserModel>
{
public UserModelMap()
{
Table("Users");
Id(x => x.Id, map => { map.Column("id"); });
Property(x => x.Username, map => { map.Column("username"); });
}
}
Previously, I only displayed the UserId, but now I would like to fill my UserModel when I get a specific VehiculeModel from the database.
Here my Model relation is OneToOne.
Also for design purpose I will never query an User to get his list of vehicule, so I dont need to have a "List of VehiculeModel" in my UserModel.
If you have any hint, about how I can map this in my Map class (saw a lots of xml mapping but i would like to map it by code) It would be very appreciated.
Thanks

I finally made it work.
nothing to complicate in fact, I just forgot to add my second model (UserModelMap) in the map class list for Nhibernate.
public class VehiculeMap: ClassMapping<Vehicule>
{
public VehiculeMap()
{
Table("G1Vehicule");
Id(x => x.Id, map => { map.Column("id"); });
Property(x => x.Brand, map => { map.Column("brand"); });
Property(x => x.Color, map => { map.Column("color"); });
Property(x => x.UserID, map => { map.Column("user_id"); });
ManyToOne(x => x.User, map => {
map.Column("user_id"),
map.Fetch(FetchKind.Join),
map.notFound(NotFoundMode.Ignore)
})
}
}
Maybe it could help someone else.

Related

EF Code first, how can you map a bidirectional one to one or zero relationship in EF?

I have two classes a User and an Image table:
public class User
{
public Guid? ImageId {get; set;}
public Image Image {get; set;}
}
public class Image
{
public Guid? UserId {get; set;}
public User User {get; set;}
}
Both the user and the image can exist on their own without the other entity, but if they do have a relation, a user can only be associated with one image, and an image can only have user, if they are not null. How do I map this? Currently I have:
public UserMapping()
{
HasOptional(x => x.ProfileImage).WithOptionalPrincipal(x =>
x.User).Map(x => x.MapKey("UserId"));
}
And nothing on the ImageMapping since from other answers it was stated don't map the relationship twice or it freaks out. However, the migration file ends up generating an additional User_Id property on the Image table then:
CreateTable(
"dbo.Images",
c => new
{
Id = c.Guid(nullable: false),
UserId = c.Guid(),
User_Id = c.Guid(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Users", t => t.User_Id)
.Index(t => t.User_Id);
Which is wrong. How can I do the mapping appropriately?
EDIT: I've also found thishttp://stackoverflow.com/questions/21082085/entity-framework-optional-1-to-1-relation-on-both-ends and tried what is shown in the original question that claims to work, but it doesn't, still creates User_Id.
Look at this page for more details.
Basically a one to one relationship is that in which the PK of the principal entity passes as PK and FK in the dependent entity. As far I can see here you need to map two entities as optional between each other and that's not really possible in EF, at least not as One to Zero-Or-One.
I understand that you want both sides to be optional but turns out that EF needs to know which one of your entities is the principal. So here is a way to change the relationships. I'd suggest you to define your principal entity in the relationship and the other become optional. E.g:
User as the principal:
//Your entities
public class Image
{
public Guid UserId { get; set; }
public virtual User User { get; set; }
}
public class User
{
public Guid UserId { get; set; }
public virtual Image Image { get; set; }
}
//Mappings:
public class ImageMappingConfiguration : EntityTypeConfiguration<Image>
{
public ImageMappingConfiguration()
{
HasKey(x => x.UserId);
}
}
public class UserMappingConfiguration : EntityTypeConfiguration<User>
{
public UserMappingConfiguration()
{
HasKey(x => x.UserId);
HasOptional(x => x.Image)
.WithRequired(x => x.User);
}
}
You'll get this after add-migration:
public override void Up()
{
CreateTable(
"dbo.Images",
c => new
{
UserId = c.Guid(nullable: false)
})
.PrimaryKey(t => t.UserId)
.ForeignKey("dbo.Users", t => t.UserId)
.Index(t => t.UserId);
CreateTable(
"dbo.Users",
c => new
{
UserId = c.Guid(nullable: false)
})
.PrimaryKey(t => t.UserId);
}
See the primary key of User passing as PK and FK to Image? That's the way EF handles One to Zero-Or-One relations.
UPDATE! Two One-To-Many relationships with Unique Constraints.
Let's give a try to this approach. Let me know if this works for you.
public sealed class Image
{
public Image()
{
Users = new List<User>();
}
public Guid Id { get; set; }
public Guid? UserId { get; set; }
public List<User> Users { get; set; }
public User User { get; set; }
}
public sealed class User
{
public User()
{
Images = new List<Image>();
}
public Guid Id { get; set; }
public Guid? ImageId { get; set; }
public List<Image> Images { get; set; }
public Image Image { get; set; }
}
//Mappings:
public class ImageMappingConfiguration : EntityTypeConfiguration<Image>
{
public ImageMappingConfiguration()
{
HasKey(x => x.Id);
Property(x => x.UserId)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_ImageMustBeUnique")
{
IsUnique = true
}));
HasMany(x => x.Users)
.WithOptional(x => x.Image)
.HasForeignKey(x => x.ImageId)
.WillCascadeOnDelete(true);
}
}
public class UserMappingConfiguration : EntityTypeConfiguration<User>
{
public UserMappingConfiguration()
{
HasKey(x => x.Id);
Property(x => x.ImageId)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_UserMustBeUnique")
{
IsUnique = true
}));
HasMany(x => x.Images)
.WithOptional(x => x.User)
.HasForeignKey(x => x.UserId);
}
}
Usage:
//test adding a user and an image.
var user = new User
{
Id = Guid.NewGuid(),
};
var image = new Image
{
Id = Guid.NewGuid(),
};
using (var ctx = new Context())
{
ctx.Users.Add(user);
ctx.Images.Add(image);
ctx.SaveChanges();
//associate them
user.Images.Add(image);
image.Users.Add(user);
ctx.SaveChanges();
//try to add a second image to the user
var image2 = new Image
{
Id = Guid.NewGuid()
};
try
{
user.Images.Add(image2);
ctx.SaveChanges();
}
catch (DbUpdateException ex)
{
Console.WriteLine(ex);
}
}

Duplicate key and table field in one-to-zero-or-one relation

For a metaphor here, a person can have zero or one car, and one car belongs only to one person.
I have a database that looks like this:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Car Car { get; set; }
}
public class Car
{
public int PersonId { get; set; }
public virtual Person Person { get; set; }
public string Model { get; set; }
}
I want to have a link from the person to its car, if existent, and from the car to its person. So my EntityTypeConfigurations are like so:
public class PersonConfig : EntityTypeConfiguration<Person>
{
public PersonConfig()
{
ToTable("tblPerson");
HasKey(s => s.Id)
.Property(s => s.Id)
.HasColumnName("idPerson");
Property(s => s.Name)
.HasColumnName("strName")
.IsRequired();
HasOptional(a => a.Car)
.WithOptionalPrincipal();
}
}
public class CarConfig : EntityTypeConfiguration<Car>
{
public CarConfig()
{
ToTable("tblCar");
HasKey(s => s.PersonId)
.Property(s => s.PersonId)
.HasColumnName("idPerson");
Property(s => s.Model)
.HasColumnName("strModel")
.IsRequired();
HasRequired(a => a.Person)
.WithRequiredDependent();
}
}
I don't know what I'm getting wrong, but EF:
Creates an additional Person_Id field in tblCar
Creates two foreign keys in tblCar, one named idPerson and the other Person_Id
What am I forgetting or doing wrong?
You have to use WithRequired instead of WithOptionalPrincipal, and the relationship do not need to be configured in both sides.
public class PersonConfig : EntityTypeConfiguration<Person>
{
public TaskConfig()
{
ToTable("tblPerson");
HasKey(s => s.Id);
Property(s => s.Id)
.HasColumnName("idPerson");
Property(s => s.Name)
.HasColumnName("strName")
.IsRequired();
HasOptional(a => a.Car)
.WithRequired(s => s.Person);
}
}
public class CarConfig : EntityTypeConfiguration<Car>
{
public CarConfig()
{
ToTable("tblCar");
HasKey(s => s.PersonId)
.Property(s => s.PersonId)
.HasColumnName("idPerson");
Property(s => s.Model)
.HasColumnName("strModel")
.IsRequired();
//not necessary
//HasRequired(a => a.Person)
//.WithRequiredDependent();
}
}
What am I forgetting or doing wrong?
You are not configuring the Person - Car relationship correctly.
Let fix that. Note that you don't need to configure the relationship in both places.
Remove the following from the Person config:
HasOptional(a => a.Car)
.WithOptionalPrincipal();
and replace the following in the Car config:
HasRequired(a => a.Person)
.WithRequiredDependent();
with
HasRequired(c => c.Person)
.WithOptional(p => p.Car);

NHibernate mapping by code error message could not execute query

I am new to c# and NHibernate so please forgive me if this question is out of line.
I am working on mapping a table in Nhibernate by code i keep getting this error:
could not execute query
I have creates following classes
class PoliceData
{
virtual public int policyNumber { get; set; }
virtual public String product { get; set; }
virtual public String Navn { get; set; }
virtual public String Adresse { get; set; }
virtual public String Husnr { get; set; }
virtual public String Postnr { get; set; }
virtual public String By { get; set; }
virtual public String Lattitude { get; set; }
virtual public String Longitude { get; set; }
virtual public String Cell100M { get; set; }
virtual public String Cell1KM { get; set; }
virtual public String Cell10KM { get; set; }
}
class PoliceDataMap : ClassMapping<PoliceData>
{
public PoliceDataMap()
{
Table("policeDataView");
Lazy(true);
Property(x => x.policyNumber, map => map.NotNullable(true));
Property(x => x.product, map => map.NotNullable(true));
Property(x => x.Navn, map => map.NotNullable(true));
Property(x => x.Adresse, map => map.NotNullable(true));
Property(x => x.Husnr, map => map.NotNullable(true));
Property(x => x.Postnr, map => map.NotNullable(true));
Property(x => x.By, map => map.NotNullable(true));
Property(x => x.Lattitude, map => map.NotNullable(true));
Property(x => x.Longitude, map => map.NotNullable(true));
Property(x => x.Cell100M, map => map.NotNullable(true));
Property(x => x.Cell1KM, map => map.NotNullable(true));
Property(x => x.Cell10KM, map => map.NotNullable(true));
}
}
I'm running the following query
public DbFactory()
{
using (ISession session = OpenSession())
{
IList<PoliceData> policedata = session.Query<PoliceData>().Where(p => p.policyNumber == 053126703).ToList();
//IList<Pet> pets = query.List<Pet>();
// Console.Out.WriteLine("pets.Count = " + pets.Count);
// pets.ToList().ForEach(p => Console.WriteLine(p.PetName));
// Console.Read();
}
}
It ends in exception with the following message
could not execute query:
[ select policedata0_.id as id0_, policedata0_.policyNumber as policyNu2_0_, policedata0_.product as product0_, policedata0_.Navn as Navn0_, policedata0_.Adresse as Adresse0_, policedata0_.Husnr as Husnr0_, policedata0_.Postnr as Postnr0_, policedata0_.Bynavn as Bynavn0_, policedata0_.Lattitude as Lattitude0_, policedata0_.Longitude as Longitude0_, policedata0_.Cell100M as Cell11_0_, policedata0_.Cell1KM as Cell12_0_, policedata0_.Cell10KM as Cell13_0_ from policeDataView policedata0_ where policedata0_.policyNumber=#p0 ]
It seems to me that NhiberNate want a Id column even though there are none in the table.
So i did try to create a Id in the code by adding this to class PoliceData
virtual public int Id { get; set; }
and adding this to PoliceDataMap
Id(x => x.id, map => map.Generator(Generators.Identity));
Now im getting compile error:
the name 'map' does not exits in the current context
What could I do to solve this, Does NHibernate need a column defined in the map class by
map.Generator(Generators.Identity));
What does it do ?
Any mapped entity must have mapped ID, so you have to provide some. But in case that you have ID like this:
virtual public int Id { get; set; }
Mapping should be
//Id(x => x.id, map => map.Generator(Generators.Identity));
Id(x => x.Id, map => map.Generator(Generators.Identity));
Also check the
Mapping-by-Code - Id, NaturalId
snippet how to mapp ID:
Id(x => x.Id, m =>
{
m.Column("id");
m.Generator(Generators.Native, g => g.Params(new
{
// generator-specific options
}));
m.Length(10);
m.Type(new Int32Type());
m.Access(Accessor.Field);
});

NHibernate Fluent Mapping a String Property to another Table's column

I have a table in the database that is used only for holding some ID's descriptions:
TABLE SomeClass
- Columns
- ClassTypeID INT CONSTRAINT etc
TABLE SomeClassTypes
- ClassTypeID INT IDENTITY
- Description NVARCHAR
It's done like this so it's easy for users to insert/remove new types.
I want to get a report of all of SomeClass, but I'd like to have a string property to hold the description from the other table:
public class SomeClass
{
public virtual int SomeClassID { get; set; }
public virtual int ClassTypeID { get; set; }
public virtual string DescriptionType { get; set; }
}
public class SomeClassMap : ClassMapping<SomeClass>
{
public SomeClassMap()
{
Table("SomeClassTable");
Property(p => p.SomeClassID, map =>
{
map.Column("SomeClassID");
map.Generator(Generators.Identity);
});
Property(p => p.ClassTypeID, map => map.Column("ClassTypeID"));
//Other properties here
Property(p => p.DescriptionType, ?); //This line
}
}
How can I do this?
Try to use joined table.
public class SomeClassMap : ClassMapping<SomeClass>
{
public SomeClassMap()
{
Table("SomeClassTable");
Property(p => p.SomeClassID, map =>
{
map.Column("SomeClassID");
map.Generator(Generators.Identity);
});
Property(p => p.ClassTypeID, map => map.Column("ClassTypeID"));
//Other properties here
Join("SomeClassTypes", m =>
{
m.KeyColumn("ClassTypeId");
m.Fetch.Join();
m.Map(x => x.DescriptionType).Column("Description");
})
}
}
EDITED
If you use fluent mapping built in NHibernate, try this:
public class SomeClassMap : ClassMapping<SomeClass>
{
public SomeClassMap()
{
Table("SomeClassTable");
Property(p => p.SomeClassID, map =>
{
map.Column("SomeClassID");
map.Generator(Generators.Identity);
});
Property(p => p.ClassTypeID, map => map.Column("ClassTypeID"));
//Other properties here
Property(p => p.DescriptionType, ?); //This line
Join("SomeClassTypes", m =>
{
m.Key(k => k.Column("ClassTypeId"));
m.Fetch(FetchKind.Join);
m.Property(x => x.DescriptionType).Column("Description");
});
}
}

One-Many via NHibernate Loquacious mapping by code or xml

I want to implement a one-to-many relationship between a person and car, and have CRUD operations on both person and car. Brief CRUD and relationships:
Update
A person has many cars
CRUD operations on both person and car via person object.
Deleting a person will delete all s/his cars
Ability to perform CRUD operation on someone's cars, either via person object or car object.
Is it possible via ORM, in particular NHibernate?
Classes like below:
public class PersonSet
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<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; }
}
Mapping is shown below:
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.All);
c.Lazy(CollectionLazy.NoLazy);
// c.Inverse(true);
}
, 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);
});
}
}
The problem I have is that if I update one car and try to save it on a person object, it doesn't change.
Update
I want to find out if it is possible, and where my mapping above is wrong. Any idea on either xml version or Loquacious would also be appreciated.
There should be a PersonId foreign key on table Car.
I don't know if this would solve your problem, but in a ManyToOne mapping the Unique and NotNullable methods should be applied at the column level.
ManyToOne(x => x.Person, m =>
{
m.Column(c =>
{
c.Name("PersonId");
c.NotNullable(true);
});
m.Cascade(Cascade.None);
});

Categories