EntityFramework 核心计算属性错误:InvalidOperation 无支持字段

问题描述 投票:0回答:2

我目前正在使用 .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; }
}

任何指导将非常感激。

谢谢:)

c# sqlite asp.net-core entity-framework-core
2个回答
5
投票

决定回到这个问题,因为我最终找到了我遇到的非常相似的问题的解决方案 - 实体类中的字段需要转换的问题。我的略有不同,因为我的支持字段是不同的类型(枚举),公共 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#

希望这对...某人有用!


0
投票

当您的实体不遵循配置的转换时,也可能会发生这种情况,在我的例子中,我有一个名为 Subscription 的实体和子集合,它应该是 SubscriptionItervals 但我有一个拼写错误,我写了 SubscrioptionItervals。

© www.soinside.com 2019 - 2024. All rights reserved.