I have following model
public class Car : Entity<int>
{
public virtual int Id{ get; set; }
...
public virtual Engine Engine { get; set; }
}
and I'm using nhibernate mapping by code approach
public class CarMap : ClassMapping<Car>
{
public CarMap()
{
Id(x => CarId, m => m.Generator(Generators.Identity));
// how map reference Engine?
**// edit**
HasOne(x=>x.Engine, m=>{}) // is this good enough?
}
}
how map Engine in this CarMap object?
You need a little more information in the question, but here's a couple of options.
Is this really a one to one relationship? One to one relationships are somewhat unique in that both sides of the relationship tend to share the same Id. Like David Osborne said you most likely want a One to Many relationship. But you you want it to be bi-directional? i.e. you can navigate down from the Engine to all the cars that may have that engine or up from the car to a specific engine. i.e. engine is Chrysler Hemi engine 5.7L and it is in the cars, Ram Pickup, Dodge Durango, Dodge Charger.
Then you may want to map the objects like this
public class Engine : Entity<int>
{
public Engine()
{
Cars = new List<Car>();
}
public virtual int Id { get; protected set; }
public virtual decimal Displacement { get; set; }
//more properties
public virtual IList<Car> Cars { get; }
public virtual void AddCar(Car car)
{
if (Cars.Contains(car)) return;
Cars.Add(car);
}
public virtual void RemoveCar(Car car)
{
if (!Cars.Contains(car)) return;
Cars.Remove(car);
}
}
public class Car : Entity<int>
{
public virtual int Id { get; set; }
public virtual Engine Engine { get; set; }
}
So if you are mapping the Engine you need to define the Cars mapping list this
Bag(x => x.Cars, map =>
{
map.Key(k => k.Column(col => col.Name("EngineId")));
map.Cascade(Cascade.All | Cascade.DeleteOrphans); //optional
},
action => action.OneToMany());
and the other side of the relationship like this
ManyToOne(x => x.Engine, map =>
{
map.Column("EngineId");
map.NotNullable(true); // if you require the Engine to be in a car
});
If you just want a one way mapping from the car to the engine, just remove all the references to the Cars list and delete the Bag mapping.
Using Fluent NH, I would use References(), which has a mapping-by-code equivalent of ManyToOne() apparently.
Related
I have a complex domain model which has many relationships with other entities in the system.
For the purpose of editing this model I want to set up a view model that simplifies things. I want to break up the model into smaller logical chunks with edit screens for each part instead of trying to represent the whole model on one screen.
In the domain model where I have a one is to many relationship it is represented like this:
public partial class CbItemsContent
{
public CbItemsContent()
{
this.cbItemsContentRegulators = new HashSet<cbItemsContentRegulator>();
}
public int ItemContentId { get; set; }
public int ItemID { get; set; }
......
public virtual CbItem CbItem { get; set; }
public virtual ICollection<cbItemsContentRegulator> cbItemsContentRegulators { get; set; }
}
cbItemsContentRegulator is another set of entities that are owned by CbItemsContent (the model shown above)
I would like to replace the Collection of cbItemsContentRegulators in my model with a simplified viewModel called ItemContentRegulatorsViewModel like this:
public class ItemContentRegulatorsViewModel
{
public int ItemContentId { get; set; }
public int[] RegulatorIds { get; set; }
}
which reduces the relationship to the ItemContent parent ID and an int array if Regulator IDs
is what I am trying to do possible?
How do I Map My collection of CbItemsContentRegulators to an int[] of Ids
Assuming that you're using Entity Framework, you should change the view model to
public class ItemContentRegulatorsViewModel
{
public int ItemContentId { get; set; }
public IList<int> RegulatorIds { get; set; } // IList
}
No you can define the mapping:
var profile = Mapper.CreateProfile("SomeName");
profile.CreateMap<CbItemsContent,ItemContentRegulatorsViewModel>()
.ForMember(dest => dest.RegulatorIds,
m => m.MapFrom(src => src.cbItemsContentRegulators.Select(c => c.RegulatorId)));
Now you can use it in a query like:
context.CbItemsContents.ProjectTo<ItemContentRegulatorsViewModel>()
With RegulatorIds as an array this would throw an exception that ToArray is not recognized.
I think this is possible in nhiberate, but my question is about Entity Framework.
In my database model - which I cannot modify - I have redundant columns that I would like to store in different classes.
Example :
public class DateParams
{
public DateTime CreationDate { get; set; }
public DateTime ModificationDate { get; set; }
// some methods
}
public class Localization
{
public String EnglishLabel { get; set; }
public String FrenchLabel { get; set; }
// some methods
}
And then I would use them in some of my models :
public class Account // Localization && DateParams
{
public int ID { get; set; }
public String Name { get; set; }
public Localization Localization { get; set; }
public DateParams DateParams { get; set; }
}
public class Lead // DateParams only
{
public int ID { get; set; }
public String Name { get; set; }
public DateParams DateParams { get; set; }
}
What I would like to achieve is having something like this
public class LocalizationMap : EntityTypeConfiguration<Localization>
{
public LocalizationMap()
{
Property(e => e.EnglishLabel).HasColumnName("en");
Property(e => e.FrenchLabel).HasColumnName("fr");
}
}
public class AccountMap : EntityTypeConfiguration<Account>
{
public AccountMap()
{
HasKey(x => x.ID);
Property(e => e.Name).HasColumnName("Name");
HasSubMapping(new LocalizationMap());
HasSubMapping(new DateParamsMap());
ToTable("Account");
}
}
I could use inheritance to solve this, but C# does not allow multiple inheritance.
I'm not going to make you happy.
There is an EF feature called Table Splitting. As the name suggests, this allows us to map (split) one database table to multiple classes in the conceptual model. In your case, the mappings for Account would look like this:
class AccountMap : EntityTypeConfiguration<Account>
{
public AccountMap()
{
ToTable("Account");
HasKey(x => x.ID);
HasRequired(a => a.DateParams).WithRequiredPrincipal();
HasRequired(a => a.Localization).WithRequiredPrincipal();
}
}
class DateParamsMap : EntityTypeConfiguration<DateParams>
{
public DateParamsMap()
{
ToTable("Account");
}
}
class LocalizationMap : EntityTypeConfiguration<Localization>
{
public LocalizationMap()
{
ToTable("Account");
}
}
But this immediately shows the problem: the table name "Account" in the type configurations is hard coded. There's no way to reuse the satellite classes DateParams and Localization for multiple types. And, before you try, EF won't accept generics like DateParams<T>.
Which is sad, because all other options I can think of are ugly, or clunky at best:
Create subclasses of DateParams and Localization (and accompanying configurations) for any entity that needs them.
Just add the properties to all types and work with projections as much as possible (because I assume the whole point of this effort is to reduce the number of properties you're going to query).
Use one context hosting the main types without these properties and a second context hosting the satellite types (again, to help querying less properties easily). But unfortunately, you can only join the instances from both contexts in memory, i.e. LINQ to objects.
Create a third satellite class, combining both smaller classes, and use these three classes as base types.
You can achieve this by using complex types. These map to table columns named like complextypeName_propertyName but this behaviour can be changed by overwriting OnModelCreating(DbModelBuilder modelBuilder) in DbContext like described in Entity Framework - Reuse Complex Type
For your example:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Localization>();
modelBuilder.Entity<Account>().Property(x => x.Localization.EnglishLabel).HasColumnName("en");
modelBuilder.Entity<Account>().Property(x => x.Localization.FrenchLabel).HasColumnName("fr");
// et cetera
}
In my code I have an three main entities:
1. Company
2. Staff
3. Position
A staff can have several positions in various companies.
I want to retrieve all staff associated to a specific company.
In code I would do something like:
public partial class Company
{
public virtual IEnumerable<Position> Positions { get; protected set; }
public virtual IEnumerable<Staff> Staffs
{
get { return Positions.Select(x => x.Staff); }
}
}
class CompanyMap : ClassMap<Company>
{
public CompanyMap()
{
Id(x => x.Id)
.Column("CompanyId")
.GeneratedBy.Identity();
Map(x => x.Name)
.Not.Nullable();
HasMany(x => x.Positions)
.KeyColumn("CompanyId")
.AsBag();
}
}
Pb: In this solutionm I will load all positions associated to a company and then all staff associated to each position... In terms of performance it's not very good i guess...
I'm pretty sure there is a better way to perform this join directly in the CompanyMap class.
Could you help me to do that?
Thank you,
Sebastien
How about altering data model ? It seems that Staff is always in a given Position collection at the given Company level. This suggest following model
public partial class Staff
{
public virtual IEnumerable<CompanyPosition> Positions { get; protected set; }
}
public class Position
{
//...
}
public class Company
{
//...
}
public class CompanyPosition
{
public virtual Company Company {get;set;}
public virtual IEnumerable<Position> {get;set;}
}
With this you can do straightforward mapping and your query ends up similar to this
var data = session.Query<Staff>().Where(s=>s.CompanyPosition.Company == company);
I have following tables
Car(CarId, Name,...)
CarPartLink(CarId, PartId)
Part(PartId, Name)
SubPartLink(Parent_PartId, Child_PartId) where both parent and child comes from Part table
I want Car object to have list of Parts including the SubParts, here Car does not have direct relationship with Part its Subparts and neither Part has a direct relation with Subparts.
i.e.
class Car
{
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Parts> AllParts { get; set; } //This should include all the parts and its subparts assuming subparts are only one level deep
}
How to make the map for the same in Fluent NHibernate?
Edit 1:
If it is not possible in Fluent NHibernate but possible in NHibernate mapping then also fine with me.
I am using Fluent NHibernate version: 1.4.0.1 and NHibernate version: 3.3.3
Edit 2:
I am also fine if I get only the subparts, or the id's of subparts in the map.
Edit 3:
Each vehicle( here in the example mentioned as Car) has more than 1 million parts and subparts combined, out of which user would be actually be using few 100 parts depending on the conditions. e.g. Get all parts that are weighing 100 kg or get all parts that are of type "Screw" etc. I will be needing these data in read only mode.
There is already a fine tutorial on relationship mappings in the FluentNH wiki. I suggest you read the guide, or even better, follow it step-by-step. Assuming the following entities:
public class Car
{
public virtual int CarId { get; set; }
public virtual string Name { get; set; }
public virtual IList<Part> AllParts {get; set;}
public Car()
{
AllParts = new List<Part>();
}
}
public class Part
{
public virtual int PartId { get; set; }
public virtual string Name { get; set; }
public virtual IList<Car> AllCars {get; set;}
//never tried mapping a many-to-many on the same entity, but this should work...
public virtual IList<Part> ParentParts {get; set;}
public virtual IList<Part> SubParts {get; set;}
public Part()
{
AllCars = new List<Car>();
ParentParts = new List<Part>();
SubParts = new List<Part>();
}
}
Your mapping will probably be something like this:
public class CarMap : ClassMap<Car>
{
public CarMap()
{
Id(x => x.CarId);
Map(x => x.Name);
HasManyToMany(x => x.AllParts)
//depending on your logic, you would either set .Inverse here or in the PartMap
.Table("CarPartLink")
.ParentKeyColumn("CarId")
.ChildKeyColumn("PartId")
.Cascade.All();
}
}
public class PartMap : ClassMap<Part>
{
public PartMap()
{
Id(x => x.PartId);
Map(x => x.Name);
HasManyToMany(x => x.AllCars)
.Table("CarPartLink")
.ParentKeyColumn("PartId")
.ChildKeyColumn("CarId")
.Cascade.All();
HasManyToMany(x => x.ParentParts)
.Table("SubPartLink")
.ParentKeyColumn("Parent_PartId")
.ChildKeyColumn("Child_PartId")
.Inverse() //saving done from the child side of the relationship, right?
.Cascade.All();
HasManyToMany(x => x.SubParts)
.Table("SubPartLink")
.ParentKeyColumn("Child_PartId")
.ChildKeyColumn("Parent_PartId")
.Cascade.All();
}
}
If the above doesn't work, let me know. Apparently there is a bug in some versions of FNH where you will need to employ a special workaround. You can find the workaround in this question (look for self-submitted answer by the OP), which is based on the same scenario as yours (many-many on the same entity).
EDIT: If you want to obtain all the parts and subparts for a Car, you will need to recursively access the SubParts for every single Part in your Car.AllParts collection.
Car Car1 = new Car();
//code to get car object
IList<Part> AllParts = new List<Part>();
GetAllParts(Car.AllParts, ref AllParts); //call a recursive method to add all the parts and subparts to the list
//do something with the list
public void GetAllParts(IList<Part> parentList, ref IList<Part> partsList)
{
foreach (Part part in parentList)
{
if (!partsList.Contains(part)) //validate if the list already contains the part to prevent replication
partsList.Add(part); //add this part to the list
if (part.SubParts.Count > 0) //if this part has subparts
GetSubParts(part.SubParts, ref partsList); //add all the subparts of this part to the list too
}
}
Edit2: This blog post seems to be exactly what you need...
session.CreateQuery(
"select parts from Car as car " +
"join car.AllParts as parts join fetch parts.SubParts where ...")
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Employee>();
Which one is better and why?
[Table("Bar")]
public class Bar {
[Key]
public Int32 BarId { get; set; }
public String BarName { get; set; }
public IEnumerable<Foo> Foos { get; set; }
}
or
public class BarMap : EntityTypeConfiguration<Bar> {
public BarMap() {
this.HasMany(t => t.Foos)
.WithMany(t => t.Bars)
.Map(m => {
m.ToTable("FooBarRelationships");
m.MapLeftKey("TheBarId");
m.MapRightKey("TheFooId");
});
}
}
I know the first one is called DataAnnotations but don't know how can we call the second type.
The second is called Fluent API (fluent configuration).
From my point of view, I would prefer the second option which can separate lots of Attributes out of models. It would make the models cleaner.