NHibernate: Load base class objects only

2020-04-17 06:39发布

Any kind of help is welcome. Even if you can say (based upon your experience) that using an ORM for such a huge hierarchy is insane :).

Backgroud My model layer has a pretty huge class hierarchy i.e. there are around 200 classes. The good/bad thing with hierarchy is that all of them have the same base class. The maximum distance between the base and leaf classes is 7 and the maximum number classes at any level in hierarchy is 80. I am using nHibernate to save/load data from persistent storage.

Problem The queries generated by nHibernate are pretty in efficient. e.g if I want to select ids of objects based upon some filter on a property in the base class, NHibernate will try to join all the tables in hierarchy/Union them depending which mapping strategy do I choose i.e. table per sub class or table per class hierarchy.

I understand that nHibernate does not which type of object until it can scan all the relevant tables. But what if I am only interested in the base class data at the moment. How to force nHibernate to load only the base class objects.

To illustrate my problem, here is a simplified version

public class Vehicle
{
    public virtual Guid Identifier { get; set; }
    public virtual int WheelsCount { get; set; }
    public virtual Make Make { get; set; }
    public virtual Model Model { get; set; }
}

public class Bike : Vehicle
{
    public Bike()
    {
        WheelsCount = 2;
    }

    public virtual bool IsDirtBike { get; set; }
}

public class Car : Vehicle
{
    public Car()
    {
        WheelsCount = 4;
    }

    public virtual bool IsFourWheelDrive { get; set; }

    public virtual string Title { get; set; }
    public virtual string Description { get; set; }
}

public class Make
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Model> Models { get; set; }
}

public class Model
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual Make Make { get; set; }
}

And the mappings are as follows

public class VehicleMap : ClassMap<Vehicle>
{
    public VehicleMap()
    {
        Id(x => x.Identifier).GeneratedBy.Guid();
        Map(x => x.WheelsCount);

        References(x => x.Make).Column("MakeId");
        References(x => x.Model).Column("ModelId");

        Table("Vehicle");
        Polymorphism.Explicit();
        UseUnionSubclassForInheritanceMapping();
    }
}

public class BikeMap : SubclassMap<Bike>
{
    public BikeMap()
    {
        Map(x => x.IsDirtBike);
        Table("Bike");
        // Abstract();
    }
}

public class CarMap : SubclassMap<Car>
{
    public CarMap()
    {
        Map(x => x.Title);
        Map(x => x.Description);
        Map(x => x.IsFourWheelDrive);
        Table("Car");
       // Abstract();
    }
}

public class MakeMap : ClassMap<Make>
{
    public MakeMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);
        HasMany(x => x.Models)
            .KeyColumn("MakeId");
        Table("Make");
    }
}

public class ModelMap : ClassMap<Model>
{
   public ModelMap()
   {
       Id(x => x.Id);
       Map(x => x.Name);
       References(x => x.Make)
           .Column("MakeId");
       Table("Model");
   }
}

Now if run the following query to load four wheeled vehicles, NHibernate will join vehicles, car and bike table. Whereas all I need right now is only the data stored in Vehicle table

List<Vehicle> vehicles = session.Query < Vehicle > ().Where(v => v.WheelsCount > 2).ToList();

Does anyone know how can I force nHibernate just load the data the is currently needed i.e. if it can return only vehicle objects instead of Car/Bike? With just a couple of tables in you schema you can overlook these queries by nHibernate but it really hurts when you have 200 tables :(.

P.S. In case there is a fault with model, please ignore that. This is not the real model. The actual model as stated earlier is much bigger. This model is there to illustrate the problem.

1条回答
可以哭但决不认输i
2楼-- · 2020-04-17 06:52

NHibernate has to join the tables to decide which type to return. otherwise polymorphism would be broken. Also it would be much harder to handle egde cases like abstract base class and so on.

Project only the properties you need and you are good to go

var vehicledatas = session.Query<Vehicle>()
    .Where(v => v.WheelsCount > 2)
    .Select(v => new { v.Id, v.WheelCount, v.Price })
    .ToList();

If you need absolutly want only the base class then map it seperate for this use case

public class AlternateVehicleMap : VehicleMap
{
    public AlternateVehicleMap()
    {
        EntityName("IAbsolutlyWantOnlyTheBaseClass");
        Readonly();   // to make sure noone messes up
    }
}

List<Vehicle> vehicles = session.CreateCriteria<Vehicle>("IAbsolutlyWantOnlyTheBaseClass").Add(Expression.Gt("WheelsCount", 2).List<Vehicle>();
查看更多
登录 后发表回答