Entity Framework 在插入数据之前检查唯一性

3

你好,我有一个类似于 Invoice 的类型:

public class Invoice : IEntity, IValidatableObject
    {
        public virtual int Id { get; set; }

        [Required(ErrorMessage = "Invoice Number is a required field.")]
        [Display(Name = "Invoice Number:")]
        public virtual string InvoiceNumber { get; set; }

        [Required(ErrorMessage = "Invoice Date is a required field.")]
        [Display(Name = "Invoice Date:")]
        [DataType(DataType.Date)]
        public DateTime? InvoiceDate { get; set; }

        [Required(ErrorMessage = "Organisation is a required field.")]
        [Display(Name = "Organisation:")]
        public int OrganisationId { get; set; }

        [Required(ErrorMessage = "Region is a required field.")]
        [Display(Name = "Region:")]
        public virtual int? AreaId { get; set; }

        [Required(ErrorMessage = "Total (Exc. GST) is a required field.")]
        [Display(Name = "Total (Exc. GST):")]
        public decimal? TotalExcludingGst { get; set; }

        [Required(ErrorMessage = "Total (Inc. GST) is a required field.")]
        [Display(Name = "Total (Inc. GST):")]
        public decimal? TotalIncludingGst { get; set; }
        public virtual string CreatedByUserName { get; set; }
        public virtual DateTime CreatedDateTime { get; set; }
        public virtual string LastModifiedByUserName { get; set; }
        public virtual DateTime? LastModifiedDateTime { get; set; }

        // Navigation properties
        public virtual Area Area { get; set; }
        public virtual Organisation Organisation { get; set; }

        public virtual ICollection<InvoiceLine> InvoiceLines { get; set; }

        #region IValidatableObject Members

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if ((TotalExcludingGst + (TotalExcludingGst * .15m)) != TotalIncludingGst) {
                yield return new ValidationResult("The total (exc. Gst) + Gst does not equal the total (inc. Gst).");
            }
        }

        #endregion

我想要做的是,在插入或更新时,确保OrgansationInvoiceNumber的组合是唯一的。

我正在考虑类似以下的解决方案:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
            {
                var repository = new Repository<Invoice>();

                if(!repositoy.CheckUnique(Id)) {
                    yield return new ValidationResult("The combination of Organisation and Invoice number is already in use");
                }
            }

这是一种不良的实践吗?在模型内部实例化存储库?
有更好的方法吗?

捕获异常并处理有什么问题呢?这样只有在需要时才会对数据库进行额外的调用。 - Jeremy Holovacs
你希望你的问题得到什么样的答案?更好的方法是在存储库内保存之前进行检查。或者只需保存,然后处理异常,就像@Jeremy建议的那样。 - mare
但是这会填充ValidationSummary吗? - AnonyMouse
我同意,在数据库中添加UIX,然后捕获异常。因为如果值不唯一,你并没有做出任何决定,只是抛出一个错误,所以先检查没有任何价值。 - RPM1984
1个回答

4
您的解决方案在多用户场景下不能正常工作。因为在检查一个 ID 是否存在并保存更改之间,可能会插入另一条具有相同 ID 的记录。
您可以在表上创建一个 唯一约束。这是确保不创建重复项的安全方式。
当前版本的 EF 没有对 唯一约束 进行建模或支持。但是,您可以捕获特定的异常并检查错误消息。然后显示错误信息。
try
{
    //updation logic
    context.SaveChanges();
}
catch (System.Data.DataException de)
{
    Exception innerException = de;
    while (innerException.InnerException != null)
    {
        innerException = innerException.InnerException;
    }

    if (innerException.Message.Contains("Unique_constraint_name"))
    {
        ModelState.AddModelError(string.Empty, "Error Message");
        return;
    }

    ModelState.AddModelError(string.Empty, "Error Message");

    return View();
}

如果您正在使用ASP.NET Web表单,您可以查看这个答案

谢谢,这似乎是最好的选择。尽管我可以看到微软更改消息文本会导致其不起作用。如果有某种UniqueConstraintException就更好了。目前你的方法是行得通的。 - acarlon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接