我引用了一个通用模型,因此我无法控制其中的属性。假设它看起来像这样:
public class Message
{
public Guid Id { get; set; }
public string Sender { get; set; }
public Uri Uri { get; set; }
}
Uri
为System.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。
答案 0 :(得分:3)
实体只能包含两种属性类型(EF可以使用):
由于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
);
}