【问题标题】:Error on updating entity with a computed column使用计算列更新实体时出错
【发布时间】:2016-03-31 09:32:32
【问题描述】:

我想更新具有计算列的实体。不幸的是,我收到了SqlException

SqlException:无法在 OUTPUT 子句中引用列“inserted.OpenCount”,因为列定义包含子查询或引用执行用户或系统数据访问的函数。如果一个函数不是模式绑定的,则默认情况下它会执行数据访问。考虑从列定义中删除子查询或函数,或从 OUTPUT 子句中删除列。

当我尝试这样做时:

[HttpGet("{id:int}/[action]")]
public async Task<IActionResult> SetPublished(int id, string returnUrl)
{
    // load article
    var article = await Data.Articles.SingleOrDefaultAsync(r => r.Id == id);

    if (article == null)
        return HttpNotFound();

    // toggle published 
    if (!article.IsPublished)
        article.IsPublished = true;
    else
        article.IsPublished = false;

    await Data.SaveChangesAsync();

    // redirect
    if (returnUrl != null)
        return LocalRedirect(returnUrl);

    return RedirectToAction(nameof(Index));
}

我确实尝试使用[NotMapped] 属性排除此列,但这没有帮助。

有没有办法在不改变数据库结构的情况下解决这个问题?

这是该列的声明方式:

[Table("Articles")]
public class Article : IdEntity
{
    /// <summary>
    /// Gets or sets publish date and time.
    /// </summary>
    [DataType(DataType.DateTime)]
    [Display(Name = "Дата публикации")]
    public virtual DateTime Date { get; set; }

    /// <summary>
    /// Gets or sets article name.
    /// </summary>
    [Display(Name = "Название")]
    [Required(ErrorMessage = "Введите название")]
    [MaxLength(50, ErrorMessage = "Название должно быть не более 50 символов")]
    public virtual string Name { get; set; }

    /// <summary>
    /// Gets or sets URL name.
    /// </summary>
    [Display(Name = "Название в URL")]
    [MaxLength(50, ErrorMessage = "Название в URL должно быть не длиннее 50 символов")]
    [RegularExpression(RegexConstants.UrlName, ErrorMessage = "Название в URL может содержать только символы латинского алфавита, цифры, символы тире и подчеркивания")]
    public virtual string UrlName { get; set; }

    /// <summary>
    /// Gets or sets article full text.
    /// </summary>
    [Display(Name = "Текст")]
    [Required(ErrorMessage = "Введите текст")]
    public virtual string Text { get; set; }

    /// <summary>
    /// Gets or sets if article is published. If not when one should not be available on public website.
    /// </summary>
    [Display(Name = "Опубликована")]
    public virtual bool IsPublished { get; set; }

    /// <summary>
    /// Gets nubmer of article opens.
    /// </summary>
    [NotMapped]
    [Display(Name = "Количество открытий")]
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public virtual int OpenCount { get; protected set; }

【问题讨论】:

  • 一点注意:第二个if 可以替换为article.IsPublished = !article.IsPublished; 你能发布Article 实体的表定义吗?计算列是如何声明的?
  • @Dennis,你是对的,但请让我们讨论一下这个主题。
  • 1) 为什么使用[NotMapped]?如果属性映射到计算列,它仍然被映射; 2) 你能发布 table 定义,而不是类定义吗?
  • 丹尼斯,让我们在 30 分钟后转到聊天室吗?
  • NotMapped 因为我想避免跟踪此列,我希望这能解决问题,但事实并非如此。

标签: c# sql entity-framework ef-code-first


【解决方案1】:

我已经为这个问题苦苦挣扎了好几天。我的研究表明,在这一点上,有两种方法可以将字段标识为 EF 的“计算”字段。

  1. 对于个别更新(Add()、Addrange()、通过 DbContext 更新),EF 不会保存,而只会检索该列的值。
  2. 对于批量更新,EF.BulkExtensions 将混合使用 Fluent 配置和列上的数据注释。设置 PropertiesToExclude 或 PropertiesToInclude 仍会尝试从在将数据批量加载到 TEMP 表后触发的 MERGE 查询中输出值,从而导致相同的异常。

https://docs.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations https://github.com/borisdj/EFCore.BulkExtensions/blob/master/EFCore.BulkExtensions/TableInfo.cs

我最终添加了一个 SPROC 来解决计算列的计算问题,并简单地将其用作任何其他绑定到 DB 模型属性的方法。

我确信触发器也可以工作,尽管它们不是我的首选

【讨论】:

  • 这只是部分相关,除此之外,太粗略以至于没有真正的意义。 OP 的问题是该属性是[NotMapped][DatabaseGenerated(DatabaseGeneratedOption.Computed)]。这没有意义,因为设置DatabaseGenerated 与不映射属性相反。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-11
  • 1970-01-01
  • 2012-06-29
相关资源
最近更新 更多