1

我正在尝试在我的模型中创建以下约束,以便 Tag 对象的 TagType 有效。有效的 TagType 是 OperatingCompanyId 与标签网站的 OperatingCompanyId 匹配的类型。我意识到这似乎令人费解,但从商业角度来看这是有道理的:

运营公司有网站。网站包含标签。标签有一个 TagType(单数)。TagType 在运营公司中是相同的,这意味着如果一家运营公司有 20 个 TagType 和 5 个 WebSite,那么这 20 个 TagType 应该能够在所有 5 个 WebSite 中使用。我想确保标签的 TagType 不能与另一个 OperatingCompany 相关联。

在模型中创建此约束的最佳方法是什么?我需要更改我的 POCO,还是使用 Fluent API?

提前致谢!

[Table("OperatingCompanies")]
public class OperatingCompany : ConfigObject
{
    public OperatingCompany()
    {
        WebSites = new List<WebSite>();
    }

    [Required(ErrorMessage = "Name is a required field for an operating company.")]
    [MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters.")]
    public string Name { get; set; }

    public virtual ICollection<WebSite> WebSites { get; set; }
}

[Table("Websites")]
public class WebSite : ConfigObject
{
    public WebSite()
    {
        WebObjects = new List<WebObject>();
    }

    [Required(ErrorMessage = "URL is a required field for a web site.")]
    [MaxLength(100, ErrorMessage = "URL cannot exceed 100 characters for a web site.")]
    [RegularExpression(@"\b(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]", ErrorMessage = "The value entered is not a valid URL.")]
    public string Url { get; set; }

    public OperatingCompany OperatingCompany { get; set; }

    [Required(ErrorMessage = "You must associate a web site with an operating company.")]
    public Guid OperatingCompanyId { get; set; }

    [InverseProperty("Website")]
    public virtual ICollection<WebObject> WebObjects { get; set; }
}

[Table("Tags")]
public class Tag : ConfigObject
{
    [Required(ErrorMessage = "Name is a required field for a tag.")]
    [MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters for a tag.")]
    public string Name { get; set; }

    public TagType TagType { get; set; }

    [Required(ErrorMessage = "You must associate a tag with a tag type.")]
    public Guid TagTypeId { get; set; }

    public WebSite WebSite { get; set; }

    [Required(ErrorMessage = "You must associate a tag with a web site.")]
    public Guid WebSiteId { get; set; }
}

[Table("TagTypes")]
public class TagType : ConfigObject
{
    [Required(ErrorMessage = "Name is a required field for a tag.")]
    [MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters for a tag type.")]
    public string Name { get; set; }

    public OperatingCompany OperatingCompany { get; set; }

    [Required(ErrorMessage = "You must associate a tag type with an operating company.")]
    public Guid OperatingCompanyId { get; set; }
}
4

3 回答 3

2

但是...如果我了解 MVC / EF 的目的,那就是在模型中包含该业务逻辑...

你的意思是什么型号?如果你使用 ASP.NET MVC 和 EF,你会以三个有时被称为模型的区域结束:

  • EF 模型 - 即一组映射到数据库的类
  • 模型-视图-控制器-这里的模型是指控制器消耗的东西(通常是业务逻辑)以准备数据以供查看
  • 视图模型 - 在 ASP.NET MVC 中,视图模型是在控制器和视图之间交换数据的类

如果我查看您的课程,我会看到第一个和第三个模型耦合在一起(大多数时候这被认为是一种不好的做法)。您的理解是正确的,但主要是在您的课程没有代表的第二个模型方面。并不是每一个“业务逻辑”都可以用映射来表示。而且做业务逻辑不是数据层的一个点。

您的映射部分有效(标签类型仅与一家运营公司相关),但您的数据层仍然没有强制执行您的所有业务规则。数据层仍然允许网站分配来自不同运营公司的标签类型的标签,您的业务逻辑必须确保不会发生这种情况。在数据库中避免这种情况会很复杂,因为它可能需要复杂的主键并将运营公司 ID 传递给每个依赖对象。

于 2011-07-17T09:55:40.667 回答
2

强制执行此约束的一种方法是利用作为 EF 4.1 中新 DbContext API 的一部分引入的新验证功能。您可以编写自定义验证规则,以确保从该公司的有效标签类型中选择任何给定公司网站的标签类型。下面显示了它是如何完成的:

public abstract class ConfigObject
{
    public Guid Id { get; set; }
}

public class OperatingCompany : ConfigObject, IValidatableObject
{
    public string Name { get; set; }

    public virtual ICollection<WebSite> WebSites { get; set; }
    public virtual List<TagType> TagTypes { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var allTagTypes = (from w in WebSites from t in w.Tags select t.TagType);

        if (!allTagTypes.All(wtt => TagTypes.Exists(tt => tt.Id == wtt.Id)))
        {
            yield return new ValidationResult("One or more of the website's tag types don't belong to this company");
        }            
    }
}

public class WebSite : ConfigObject
{
    public string Url { get; set; }                
    public Guid OperatingCompanyId { get; set; }

    public virtual ICollection<Tag> Tags { get; set; }
    public OperatingCompany OperatingCompany { get; set; }                
}

public class Tag : ConfigObject
{
    public string Name { get; set; }
    public Guid TagTypeId { get; set; }
    public Guid WebSiteId { get; set; } 

    public TagType TagType { get; set; }               
    public WebSite WebSite { get; set; }
}

public class TagType : ConfigObject
{
    public string Name { get; set; }
    public Guid OperatingCompanyId { get; set; }

    public OperatingCompany OperatingCompany { get; set; }                
}

public class Context : DbContext
{
    public DbSet<OperatingCompany> OperatingCompanies { get; set; }
    public DbSet<WebSite> WebSites { get; set; }
    public DbSet<Tag> Tags { get; set; }
    public DbSet<TagType> TagTypes { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Tag>().HasRequired(t => t.WebSite)
                                  .WithMany(w => w.Tags)
                                  .HasForeignKey(t => t.WebSiteId)
                                  .WillCascadeOnDelete(false);
    }
}

因此,EF 将在您每次调用 DbContext.SaveChanges() 以将 OperatingCompany 对象保存到数据库时调用该验证方法,如果该方法返回任何验证错误,EF 将抛出(并中止事务)。您还可以通过调用 DbContext 类上的GetValidationErrors方法来主动检查验证错误,以检索您正在使用的模型对象中的验证错误列表。

还值得注意的是,由于您将域模型也用作 MVC 层的视图模型,因此 MVC 将识别并遵守此验证规则,您可以通过查看控制器中的ModelState来检查验证结果。所以它真的在两个地方得到检查,一次在你的表示层由 MVC 和一次在后端由 EF。

希望这可以帮助。

于 2011-07-19T03:41:31.517 回答
0

如果我是你,我会使用业务层来过滤 Tagtype,而不是在数据库中做这样的约束。对我来说,这种方法可能更容易。

于 2011-07-15T18:28:48.943 回答