【问题标题】:Custom navigational properties自定义导航属性
【发布时间】:2014-12-17 18:10:17
【问题描述】:

假设我们在数据库中有一个表 Car,如下所示:

Id | Brand | Model | Color | Description
 1 |  23   |  6    |  005  | Beautiful car

还有一个名为元数据的表格,其中包含有关汽车的不同信息。

Id | Type  | Key | Value
 1 | Brand |   6 | Ford
 2 | Brand |  22 | BMW
 3 | Brand |  23 | Audi
 4 | Model |   5 | Focus
 5 | Model |   6 | A4
 6 | Model |   7 | 325
 7 | Color | 005 | Black
 8 | Color | 019 | Blue

如您所见,Type & Key 组合在表中应该是唯一的,可以被视为外键。

我完全理解规范化数据库的概念。在我们的例子中,规范化 Metadata 表会在应用程序的其他部分引入复杂性,我无法理解,这就是我们尝试这种方式的原因。

Code First 类 CarMetadata 如下所示

public class Car
{
    public int Id { get; set; }
    public string BrandId { get; set; }
    public string ModelId { get; set; }
    public string ColorId { get; set; }
}

public class Metadata
{
    public int Id { get; set; }
    public string Type { get; set; }
    public string Key { get; set; }
    public string Value { get; set; }
}

CarMetadata 的配置类

public class CarMap : EntityTypeConfiguration<Car>
{
    this.HasKey(t => t.Id);
    this.Property(t => t.BrandId).IsRequired().HasMaxLength(6);
    this.Property(t => t.ModelId).IsRequired().HasMaxLength(6);
    this.Property(t => t.ColorId).IsRequired().HasMaxLength(6);

    this.ToTable("Car");
    this.Property(t => t.Id).HasColumnName("Id");
    this.Property(t => t.BrandId).HasColumnName("Brand");
    this.Property(t => t.ModelId).HasColumnName("Model");
    this.Property(t => t.ColorId).HasColumnName("Color");
}

public class MetadataMap : EntityTypeConfiguration<Metadata>
{
    this.HasKey(t => t.Id);
    this.Property(t => t.Type).IsRequired().HasMaxLength(6);
    this.Property(t => t.Key).IsRequired().HasMaxLength(6);
    this.Property(t => t.Value).IsRequired().HasMaxLength(6);

    this.ToTable("Metadata");
    this.Property(t => t.Id).HasColumnName("Id");
    this.Property(t => t.Type).HasColumnName("Type");
    this.Property(t => t.Key).HasColumnName("Key");
    this.Property(t => t.Value).HasColumnName("Value");
}

还有一个DbContext

public class CarContext : DbContext
{
    public DbSet<Car> Cars { get; set; }
    public DbSet<Metadata> Metadata { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new CarMap());
        modelBuilder.Configurations.Add(new MetadataMap());
    }
}

检索前 10 辆汽车的方法

public List<Car> GetAllCars()
{
    using (CarContext context = new CarContext())
    {
        return context.Cars.ToList();
    }
}

假设我将导航属性添加到 Car

public class Car
{
    public int Id { get; set; }

    public string BrandId { get; set; }
    public virtual Metadata Brand { get; set; }

    public string ModelId { get; set; }
    public virtual Metadata Model { get; set; }

    public string ColorId { get; set; }
    public virtual Metadata Color { get; set; }
} 

在调用 GetAllCars() 时,确保实现这些属性的最优雅、最有效的方法是什么?

【问题讨论】:

  • 看看指定[ForeignKey]属性? (或者,您可以在 fluent 定义中指定导航属性)。
  • 现实生活中有多少种不同的元数据?
  • @GertArnold 大约 30 岁。

标签: c# entity-framework entity-framework-6


【解决方案1】:

在这种情况下,我认为您不能拥有“真正的”导航属性,因为我看不出您如何在数据库中定义这种关系和/或使用流畅的 API 将其映射到代码中。 (有人可能会在这里纠正我)。

在这种情况下,我会将它们定义为未映射并在您的 GetAllCars() 方法中填充它们。所以,Car 类看起来像这样:

public class Car
{
    public int Id { get; set; }

    public string BrandId { get; set; }
    [NotMapped]
    public Metadata Brand { get; set; }

    public string ModelId { get; set; }
    [NotMapped]
    public Metadata Model { get; set; }

    public string ColorId { get; set; }
    [NotMapped]
    public Metadata Color { get; set; }
}

而且,方法是:

public List<Car> GetAllCars()
{
    using (CarContext context = new CarContext())
    {
        var returnList = new List<Car>();

        foreach(var car in context.Cars)
        {
            car.Brand = context.Metadata
                               .Where(x => x.Key == car.BrandId && x.Type == "Brand")
                               .FirstOrDefault();

            car.Model = context.Metadata
                               .Where(x => x.Key == car.ModelId && x.Type == "Model")
                               .FirstOrDefault();

            car.Color = context.Metadata
                               .Where(x => x.Key == car.ColorId && x.Type == "Color")
                               .FirstOrDefault();

            returnList.Add(car);
        }

        return returnList;
    }
}

【讨论】:

  • 我希望避免遍历集合并单独实现每个属性。
  • 即使 EF 对这种情况有所了解,foreach 也会隐含地发生。你会感觉更好,因为你不会看到它;)如果你能够在数据库中指定关系,EF 可以生成 SQL 将所有数据一次性带入,但没有关系 @ 987654326@ 必须以一种或另一种方式发生。但是,您可以在数据库中创建视图或存储过程,然后使用 EF 查询/执行它。
  • 我喜欢创建视图的想法。谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-21
  • 2015-02-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多