我目前正在使用 .net core 2.2 和 EntityFramework 制作一个 Web 应用程序。
我有一个模型,它有 2 个计算属性“AgeYears”和“AgeMonths”,这些属性是由类库根据属性“Birthday”计算的。
当我运行网络应用程序,并且有一个包含此模型的提取时,会抛出错误:
System.InvalidOperationException:找不到支持字段 实体类型“HorseModel”的属性“AgeMonths”,并且该属性确实 没有二传手。
我试图通过阅读几篇文章来弄清楚发生了什么 其中一个位于 Microsoft 网站上,但仅返回名字和姓氏(全名)的简单串联,另一个似乎不是最新的,因为有些属性根本不可用,例如[计算]。通常,我不会理会属性,而只是在视图或页面模型中进行计算,但愚蠢地认为这将是一个更好/更干净的想法!?
这是模型中引起问题的部分:
public DateTime Birthday { get; set; }
[NotMapped]
public int AgeYears
{
get
{
return DateTimeSpan.CompareDates(DateTime.Now, Birthday).Years;
}
}
[NotMapped]
public int AgeMonths
{
get
{
return DateTimeSpan.CompareDates(DateTime.Now, Birthday).Months;
}
}
我尝试删除数据库和迁移,并在尝试再次运行初始迁移时出现错误,尽管将 [NotMapped] 添加到属性中。
我期望发生的是,当从数据库中获取记录时,会计算这些属性,并认为不需要将它们存储在数据库中。显然,我做错了什么,我认为这与使用我自己的函数进行计算有关。
为了使其工作,我按照错误建议添加了一个支持字段,当我查看迁移时,我注意到仍然为“NotMapped”值生成列。
migrationBuilder.CreateTable(
name: "Horses",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(nullable: true),
Birthday = table.Column<DateTime>(nullable: false),
AgeYears = table.Column<int>(nullable: false),
AgeMonths = table.Column<int>(nullable: false),
Breed = table.Column<string>(nullable: true),
Height = table.Column<decimal>(nullable: false),
About = table.Column<string>(nullable: true),
ForSale = table.Column<bool>(nullable: false),
Private = table.Column<bool>(nullable: false),
OwnerId = table.Column<string>(nullable: true),
Created = table.Column<DateTime>(nullable: false),
LastEdited = table.Column<DateTime>(nullable: false),
PurhaseDate = table.Column<DateTime>(nullable: false),
Sold = table.Column<DateTime>(nullable: false),
Slug = table.Column<string>(nullable: true),
OwnershipStatus = table.Column<int>(nullable: false)
},...
现在,AgeYears 和 AgeMonths 在获取时不会被计算!
我想出的最终模型是:
public class HorseModel
{
private int _ageYears;
private int _ageMonths;
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
[NotMapped]
public int AgeYears
{
get
{
return _ageYears;
}
set
{
_ageYears = DateTimeSpan.CompareDates(DateTime.Now, Birthday).Years;
}
}
[NotMapped]
public int AgeMonths
{
get
{
return _ageMonths;
}
set
{
_ageMonths = DateTimeSpan.CompareDates(DateTime.Now, Birthday).Months;
}
}
public string Breed { get; set; }
public decimal Height { get; set; }
public string About { get; set; }
public bool ForSale { get; set; }
public bool Private { get; set; }
[ForeignKey("Owner")]
public string OwnerId { get; set; }
public ApplicationUser Owner { get; set; }
public List<HorsePicture> Pictures { get; set; }
public List<HorseYouTubeVideo> Videos { get; set; }
public DateTime Created { get; set; }
public DateTime LastEdited { get; set; }
public DateTime PurhaseDate { get; set; }
public DateTime Sold { get; set; }
public HorseProfilePicture ProfilePicture { get; set; }
public string Slug { get; set; }
public OwnershipStatus OwnershipStatus { get; set; }
}
任何指导将非常感激。
谢谢:)
决定回到这个问题,因为我最终找到了我遇到的非常相似的问题的解决方案 - 实体类中的字段需要转换的问题。我的略有不同,因为我的支持字段是不同的类型(枚举),公共 getter 和 setter 与字符串相互转换。
TLDR - 原始问题的解决方案应该是将 AgeYears 和 AgeMonths 从具有 getters 的属性更改为简单的方法,例如公共 int GetAgeYears() & 公共 int GetAgeMonths();
请注意,在字段 getter(或 setter)中进行转换是有问题的,因为 EF3 将更改行为并默认直接写入支持字段,跳过任何转换。哎呀!
...
这就是我最初遇到 EF 抛出的错误的地方,我的第一反应是创建一个空的公共 setter。我知道这听起来很愚蠢,但它实际上有效 - 我正在使用包含分组的查询的类,所以我从存储过程调用结果并按预期加载所有数据。
set
{
// Required by EF
}
当我切换到 LINQ 来测试分组的本地评估时,空 setter 完美地完成了工作,并且 EF 返回了该属性上具有默认值的对象!
采取 2,然后我只使用了一个称为转换的公共设置器
private Status _status;
public string Status
{
get { return _status; }
private set {
_status = ParseRag(value);
}
}
我什至在类中有一个小辅助方法来进行转换,硬编码为该特定的 enun:天哪!
public static RagStatus ParseRag(string value)
{
Status rag;
Enum.TryParse(value, out rag);
return rag;
}
选择3,在意识到这有多蹩脚后稍微好一点。
我的实体字段仍然是一个枚举。
private Status _status;
它现在只有一个简单的公共 getter,无需转换。
public Status Status
{
get { return _status; }
}
如果我确实需要将其作为字符串读取,我可以调用 GetStatus()
public string GetStatus()
{
return Status.ToString();
}
已在 ExtensionMethods 文件夹中创建一个单独的类作为 EnumHelper.cs,并且代码是通用的,不与特定枚举绑定。
public static class EnumHelper
{
public static T ConvertToEnum<T>(this string value)
{
if (Enum.IsDefined(typeof(T), value)) {
return (T)Enum.Parse(typeof(T), value);
}
else {
throw new ArgumentException($"{value} is not of the expected Enum");
}
}
public static bool IsEnum<T>(this string value)
{
if (Enum.IsDefined(typeof(T), value)) {
return true;
}
else {
return false;
}
}
}
最后在 dbcontext 中我指定转换如下,EF 可以按照约定找到支持字段名称:读取为字符串,并使用扩展方法写入为枚举。
entity.Property(e => e.Status)
.HasColumnName("Status")
.HasMaxLength(255)
.IsUnicode(false)
.HasConversion(
v => v.ToString(),
v => v.ConvertToEnum<Status>());
当然,当我为 dbcontext 运行单元测试时,我需要记住这种依赖性。 EnumHelper 是一个静态类,因此不适合定义为服务 - 这是另一个蠕虫!
更多信息 EF Core 支持字段 - 将属性公开为另一种类型?
这里https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions
更多关于枚举转换的信息请参见这里 Cast int to enum in C#
希望这对...某人有用!
当您的实体不遵循配置的转换时,也可能会发生这种情况,在我的例子中,我有一个名为 Subscription 的实体和子集合,它应该是 SubscriptionItervals 但我有一个拼写错误,我写了 SubscrioptionItervals。