【问题标题】:How can I use a generic type with entity framework core?如何将泛型类型与实体框架核心一起使用?
【发布时间】:2017-10-19 08:33:09
【问题描述】:

如果我有一个看起来像这样的域模型:

public class Foo<T> {
    public Guid Id { get; set; }
    public string Statement { get; set; }
    public T Value { get; set; }
}

我想将它用于内置数据类型(字符串、整数等)以及日期。 我想像这样使用它:

var foo = new Foo<string>();
foo.Value = "Hey";

如何使用 EF Core 将其保存到数据库中?

我想数据库表看起来像

| Id | Statement | ValueAsString | ValueAsDecimal | ValueAsDate | ValueAsInt | 
| 1  | NULL      | "Hey"         |                |             |            |
| 2  | NULL      |               | 1.1            |             |            |

【问题讨论】:

  • 查看我对我的回答的最新评论。然后你会得到多少列......?然后让它更抽象,并为您的代码创建一些转换逻辑。

标签: c# generics ef-core-2.0


【解决方案1】:

你应该还有一堂课。您的课程Foo 应该是抽象的。 所以你会得到“:

public abstract class Foo<T> {
    public Guid Id { get; set; }
    public string Statement { get; set; }
    public T Value { get; set; }
}

那么您的实现类将是:

public class Orders: Foo<Order> {
}

现在你有了你的Orders 类和你可以存储的泛型类型。

【讨论】:

  • 感谢您的回答 :D 有其他方法吗?感觉它打败了泛型的意义?
  • 为什么会有这种感觉?是不是整个想法,所以你不必为相同的事情为每个(域)模型输入每个字母..?你的数据库会是什么样子呢?还是您的 DTO 或其他模型?
  • 我必须为每种我想使用 Foo 的类型创建一个类?如果我说我只想将它用于基本类型,那会更容易吗?
  • to use with every type,你说的好像是错的..?你也可以坚持这个类型(你的意思是 foo?),但是,你怎么知道foo 是什么?
  • 这不是为每个子类型创建一个表吗?
【解决方案2】:

如果您想在与您的问题类似的单个表中将不同的值类型持久保存到数据库中,您可以这样做:

public interface IHasValue<T> {
    T Value { get; set; }
}

public abstract class Foo {
    public Guid Id { get; set; }
    public string Statement { get; set; }
}

public class Foostring : Foo, IHasValue<string> {
    string Value { get; set; }
}

public class FooInt : Foo, IHasValue<int> {
    int Value { get; set; }
}

在您的DbContext 类中添加属性:

public DbSet<FooString> FooStrings { get; set: }
public DbSet<FooInt> FooInts { get; set; }

您可以在DbContextOnModelCreating 方法中设置表的列名:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // include the base class so a single table is created for the hierarchy
    // rather than a table for each child class
    modelBuilder.Entity<Foo>().ToTable("Foos");

    // Specify the column names or you will get weird names
    modelBuilder.Entity<FooString>().Property(entity => entity.Value)
        .HasColumnName("ValueAsString");
    modelBuilder.Entity<FooInt>().Property(entity => entity.Value)
        .HasColumnName("ValueAsInt");
}

此代码将生成一个表Foos,其中包含IdStatementDiscriminatorValueAsStringValueAsInt 列。有关Discrimiator 列的更多信息,请访问here

您仍然需要为要用于T 的每个类型/列创建一个类,我认为您无法解决这个问题。

【讨论】:

  • 上面的代码实际上创建了2个表:FoostringFooInt。如果您强制 ef 在模型构建器中为两个表指定相同的名称,您将收到错误消息。要使其正常工作,您必须使用table splitting
  • 我设法通过在模型中包含Foo 基类来使其与单个表一起工作(可以通过为Foo 类创建DbSet 或指定它在OnModelCreating 方法中)。
  • @somethingRandom Foo DbSet 有泛型吗?如果是这样,你传递了什么?
  • @BeniaminoBaggins 不,你为Foo 类创建一个DbSet,像这样public DbSet&lt;Foo&gt; Foos { get; set: }Foo 类不是通用的,所以你不会向它传递任何东西。话虽如此,我发现在OnModelCreating 方法中手动创建Foos 表更有意义,因为您可能永远不会直接访问该表(Value 属性无法从该DbSet 访问)。我已经更新了答案,将提供有关如何实现此功能的更多信息。
猜你喜欢
  • 2020-09-28
  • 1970-01-01
  • 2021-01-10
  • 1970-01-01
  • 2011-11-17
  • 1970-01-01
  • 2019-05-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多