实体类型' Uri'需要定义主键

时间:2018-03-27 09:20:15

标签: c# entity-framework asp.net-core

我引用了一个通用模型,因此我无法控制其中的属性。假设它看起来像这样:

  public class Message
  {
      public Guid Id { get; set; }
      public string Sender { get; set; }
      public Uri Uri { get; set; }
  }

UriSystem.Uri

在我的上下文中,我会覆盖OnModelCreating以设置主键:

  public DbSet<Message> Messages { get; set; }

  protected override void OnModelCreating(ModelBuilder modelBuilder)
  {
      modelBuilder.Entity<Message>()
          .HasKey(i => i.Id);
  }

然后我在我的数据库初始化程序中运行Initialize

  public void Initialize()
  {
      _logger.Information("Ensuring database is created.");

      _messageContext.Database.Migrate();
  }

但是得到以下错误:

The entity type 'Uri' requires a primary key to be defined.

有关我如何解决这个问题的任何提示?

编辑:

显然这是known issue

2 个答案:

答案 0 :(得分:3)

实体只能包含两种属性类型(EF可以使用):

  1. 映射到原始SQL类型(string,int,bool,...)的类型。
  2. EF中定义的实体类型。
  3. 由于Uri未映射到原始SQL类型,因此EF会尝试处理它,就好像它是一个实体一样。

    实体存储在单独的表中。要在表中存储实体,它需要唯一的ID。

    有很多方法可以解决这个问题。我建议的方法维护Uri,但阻止EF尝试使用它。

    第1步 - 制作Uri [NotMapped]

    这使得EF 忽略了 Uri属性,因为它无法正确处理它。

    [NotMapped]
    public Uri Uri { get; set; }
    

    第2步 - 创建一个EF 应该处理的字符串属性,而不是Uri。

    字符串值派生自(现在隐藏的)Uri属性,但EF没有看到。它只能看到一个可用的字符串属性。

    public String URL 
    {
        get
        {
            return this.Uri.AbsoluteUri;
        }
        set
        {
            this.Uri = new Uri(value);
        }
     }
    

    这应该可以解决问题。在代码中,您可以直接使用Uri属性(就像您已经在做的那样),但是您已经设置了实体,因此EF使用URL字符串属性。 在EF检索实体并设置其属性(包括URL字符串属性)之后,还会隐式创建Uri属性,您可以继续使用它。

答案 1 :(得分:3)

对于EF Core 2.0及更早版本,@ Flater的答案是正确的。

对于EF Core 2.1,有更优雅和通用的解决方法,无需使用转换代码来污染模型:Value Converters

在你的情况下:

public class UriConverter : ValueConverter<Uri, string>
{
    public static UriConverter Instance = new UriConverter();

    private UriConverter() : base(value => ToUriString(value), value => ToUri(value)) { }

    private static string ToUriString (Uri uri) => uri?.ToString();

    private static Uri ToUri(string url) => Uri.TryPrase(url, out Uri uri)?uri:null;
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Message>().Property(e => e.Uri)
        .HasConversion(UriConverter.Instance);
}

或者对于这个非常简单的情况,没有辅助类:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Message>().Property(e => e.Uri)
        .HasConversion(
            uri => uri?.ToString(),
            url => Uri.TryPrase(url, out Uri uri)?uri:null
        );
}
相关问题