2014-11-21 35 views
0

我有3个对象Vehicle,Car和MotorCycle。 Vehicle是Car和MotorCycle的抽象基类。RavenDb,关于abstcts类实现的属性的查询

public abstract class Vehicle 
{ 
    public String Color {get;set}; 
    public Int Price {get;set}; 
    public DateTime ReleaseDate {get;set}; 
} 

public class Car : Vehicle 
{ 
    public Int EnginePower {get;set}; 
    public Int TrunkSpace {get;set}; 
    public bool GearBoxType {get;set}; 
    public Int Seats {get;set}; 
} 

public class MotorCycle : Vehicle 
{ 
    public Int EnginePower {get;set}; 
    public String Type{get;set}; 
} 

我想在RavenDb的同一份文件中存储所有车辆(汽车,摩托车......)。所以我使用这个惯例,它的工作原理:

documentStore.Conventions.FindTypeTagName = 
    type => type.IsSubclassOf(typeof(Vehicle)) ? 
     DocumentConvention.DefaultTypeTagName(typeof(Vehicle)) : 
     DocumentConvention.DefaultTypeTagName(type); 

现在我想为我的所有车辆的请求做索引。但我想请求车辆属性和实现(汽车,MotorCycle)属性。请求

实施例I将:

  • 所有车辆用颜色= “红色”,EnginePower> 100
  • 所有车辆用颜色= “红色”,EnginePower> 100,座椅= 5
  • 具有type =“滑板车”
  • 等所有车辆....

而结果只有一个采集驱动车辆类型,以便通过RealeaseDate的。 如何在C#和linq中执行这样的查询?

回答

1

RavenDB会将实体保存为它们的派生类型并根据这些类型构建任何自动索引。

你需要做的是创建一个新的索引,索引所有不同类型的基类中的属性,然后从该索引中进行选择。

public class VehicleIndex : AbstractMultiMapIndexCreationTask 
{ 
public VehicleIndex() 
{ 
this.AddMap<Car>(vehicle => from v in vehicle select new { v.Price , v.Color , v.ReleaseDate }); 
this.AddMap<Motorcycle>(vehicle => from v in vehicle select new { v.Price , v.Color , v.ReleaseDate }); 
} 
} 

我已经使用接口编写了更深入的博客文章,但是完全相同的技巧适用于抽象基类。请参阅blog post here

它实际上是一种非常强大的技术,可以充分利用Raven的无模式特性。

+0

只是一个简短的提示,如果您想通过EnginePower查询,您需要将该属性移动到基类并将其包含在索引中。 – 2014-11-21 18:14:17

+0

感谢您的回复,但我不想移动Property EnginePower。我可以从我的单个文档集合(多种类型)查询RavenDb studio中的儿童类属性。在C#中不可能做到这一点? – k4st0r42 2014-11-22 16:52:46

+0

然后你需要使用一个接口并建立索引。在RavenDB中要记住的事情就是一切都是索引,你从来不会像数据库那样真正“查询”它。 – 2014-11-23 20:47:57

1

我碰到这个线程,并想更正迄今发布在这里的评论/答案。

可能创建一个所有继承索引。

一般来说,最好将所有继承保存在单独的集合中。换句话说,不要把汽车当作一辆汽车在乌鸦里,而是作为一辆汽车。

然后,您可以使用MultiMapIndex对不同的遗产之间的相似性查询,同时也保留创建一个特定的继承一个单独的索引选项,在需要时。

得到你需要,只需将结果保存为一个新的类型,其中包含想要的所有属性索引(甚至是创建新的基于子类的输入)

public class VehicleIndex : AbstractMultiMapIndexCreationTask 
{ 
    public class Mapping 
    { 
     public string Price { get; set; } 
     public string Color { get; set; } 
     public string ReleaseDate { get; set; } 
     public int? EnginePower { get; set;} 
    } 

    public VehicleIndex() 
    { 
     this.AddMap<Car>(vehicle => from v in vehicle select new Mapping() 
     { 
      Price = v.Price, 
      Color = v.Color, 
      ReleaseDate = v.ReleaseDate, 
      EnginePower = v.EnginePower 
     }); 
     this.AddMap<Motorcycle>(vehicle => from v in vehicle select new Mapping() 
     { 
      Price = v.Price, 
      Color = v.Color, 
      ReleaseDate = v.ReleaseDate, 
      EnginePower = v.EnginePower 
     }); 
    } 
} 

我的结果将EnginePower保存为int?,以便您可以向该课程添加没有EnginePower的孩子并将其存储为null。这允许你通过做过滤摩托车和汽车where(a => a.EnginePower.HasValue && (a.EnginePower > 50 || a.EnginePower < 100))

编辑:我已经添加了自定义过载,只是扫描从T派生的类的整个解决方案,并将它们添加到索引作为新地图。这意味着如果添加一个新的孩子,你不必手动将它们添加到索引定义,但只需重新编译索引,你就可以走了。

public void AddMapForPolymorphic<TBase>(Expression<Func<IEnumerable<TBase>, IEnumerable>> expr) 
    { 
     AddMap(expr); 
     var children = Modules.Modules.Instance.GetModules() 
      .SelectMany(a => AssemblyHelper.GetClassesWithBaseType<TBase>(a.GetType().Assembly) 
      ); 

     var addMapGeneric = GetType() 
      .GetMethod("AddMap", BindingFlags.Instance | BindingFlags.NonPublic); 

     foreach (var child in children) 
     { 
      if (!child.HasAttribute<IgnorePolymorpAttribute>()) 
      { 
       var genericEnumerable = typeof(IEnumerable<>) 
        .MakeGenericType(child); 

       var delegateType = typeof(Func<,>) 
        .MakeGenericType(genericEnumerable, typeof(IEnumerable)); 

       var lambdaExpression = Expression.Lambda(delegateType, expr.Body, Expression 
        .Parameter(genericEnumerable, expr.Parameters[0].Name)); 

       addMapGeneric 
        .MakeGenericMethod(child).Invoke(this, new[] { lambdaExpression }); 
      } 
     } 
    }