【问题标题】:EF Code First CTP 5: Validation of an Entity That Depends Other Entities (eg Unique Constraints)EF Code First CTP 5:验证依赖于其他实体的实体(例如唯一约束)
【发布时间】:2011-07-22 07:16:55
【问题描述】:

在使用基于 EF Code First 实现 ORM 时创建的 POCO 处理依赖于其他域实体的域实体验证时,有哪些最佳实践?

这是我正在尝试解决的情况:我有一个代表客户端计算机的类,并且该类的一个属性代表计算机的 IP。我需要它是独一无二的,但我找不到执行该约束的优雅解决方案。目前我在更新/插入实体的服务层中执行此操作。

-- 更新--

我知道 EF 不支持唯一约束,并且我已经将约束添加到数据库表中,但我宁愿在访问数据库之前捕获约束。我一直在寻找一种更好的方法来处理通常依赖于其他实体的验证,并以唯一约束为例。

-- 2010 年 3 月 28 日更新--

作为参考,这里是我目前处理 IP 唯一约束的方式(_unitOfWork 是 SqlMessageUnitOfWork 类型:基本上它围绕着我正在使用的 DBContext,为所有相关表公开 IDbSet):

public class ClientService : IClientService
{
    public ValidationResult InsertClient(ClientDTO clientDTO)
    {
        var existingClient = _unitOfWork.Clients.Where(x => x.IP == clientDTO.IP).SingleOrDefault();

        if (existingClient != null)
        {           
            return new ValidationResult("IP already in Use.", new[] { "IP" });
        }
        else
        {
            var newclient = new Client();
            ClientEntityMapper.MapToEntity(clientDTO, newclient, _unitOfWork.Terminals);
            _unitOfWork.Clients.Add(newclient);
            _unitOfWork.Commit();
        }

        return ValidationResult.Success;
    }
    ...

    private IUnitOfWork _unitOfWork;

    public ClientService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }
}

public interface IUnitOfWork
{
    IDbSet<Message> Messages { get; }
    IDbSet<Terminal> Terminals { get; }
    IDbSet<Client> Clients { get; }
    IDbSet<MessageDisplayInstance> MessageDisplayInstances { get; }
    void Commit();
}

public class SqlMessageUnitOfWork : IUnitOfWork
{
    readonly VisualPagingDbContext _context;

    public SqlMessageUnitOfWork()
    {
        _context = new VisualPagingDbContext();
    }

    public void Commit()
    {
        _context.SaveChanges();
    }

    public IDbSet<Message> Messages
    {
        get { return _context.Messages; }
    }

    public IDbSet<Terminal> Terminals
    {
        get { return _context.Terminals; }
    }

    public IDbSet<Client> Clients
    {
        get { return _context.Clients; }
    }

    public IDbSet<MessageDisplayInstance> MessageDisplayInstances
    {
        get { return _context.MessageDisplayInstances; }
    }
}

【问题讨论】:

  • 您没有展示您当前处理验证的方式,所以我们不知道您期望什么更好的方式。
  • 我说我在服务层处理。我将通过输入实际代码来更新我的问题。
  • 我不认为您可以在不查询数据库的情况下验证您的依赖属性。我在 BaseRepository 类中将 IsValid() 方法定义为虚拟方法,并在其他存储库中覆盖它,从而在服务层中的单个方法中进行所有验证。
  • 我同意,虽然我忘记了这个问题的细节,但我真的不再使用这样的服务类了。对我来说,他们隐藏了所有肮脏的小秘密,当他们吐出基于存储库/域/服务洋葱的架构时,没有人谈论这些秘密。您无法回避这样一个事实,即验证(或一般的业务逻辑)有时取决于其他实体的状态,这往往会被推入“服务层”。对我来说,它直接属于业务模型,所以我认为它违反了层级责任。

标签: .net entity-framework ef-code-first


【解决方案1】:

EF 根本不支持唯一约束 (they are planned for future versions),但这并不意味着您不能将它们添加到数据库表中(例如在自定义初始化程序中)。您描述的是需要查询数据库的验证。这是一项业务验证,必须在需要时实施和执行。这不是 EF 会为您处理的事情。顺便提一句。 CTP5 是过时的版本。使用EF 4.1 instead

【讨论】:

  • 感谢您让我知道 4.1 已经发布。
【解决方案2】:

如果您需要设置唯一约束,您可以通过运行 sql 命令来完成。

因此,无论您在哪里进行域模型配置,都可以添加一行来执行 sql。所以在你的情况下,以下应该做的工作:

string script = "ALTER TABLE <table-name> ADD CONSTRAINT UniqueElement UNIQUE (<column-name>)";
context.Database.SqlCommand(script);

【讨论】:

  • 我的问题不是如何创建独特的约束,而是如何在我的应用程序代码中更好地建模它们。
【解决方案3】:

为了向我的实体验证添加唯一性检查,我在上下文中覆盖了 ValidateEntity 并添加了以下内容:

if (entityEntry.Entity is Role &&
            (entityEntry.State == EntityState.Added || entityEntry.State == EntityState.Modified))
        {
            var role = entityEntry.Entity as Role;

            if (!string.IsNullOrEmpty(role.ShortName))
            {
                if (
                    Roles.Any(
                        p => p.ShortName.ToLower() == role.ShortName.ToLower() && !p.RoleID.Equals(role.RoleID)))
                {
                    result.ValidationErrors.Add(new DbValidationError("ShortName", "Role Short Name already exists"));
                    return result.ValidationErrors.Count > 0 ? result : base.ValidateEntity(entityEntry, items);
                }
                //Remeber to check local collection - otherwise we could end up saving two or more at once that are duplicated
                if (
                    Roles.Local.Count(p => p.ShortName.ToLower() == role.ShortName.ToLower() && !p.Equals(role)) > 0)
                {
                    result.ValidationErrors.Add(new DbValidationError("ShortName", "Role Short Name already exists"));
                    return result.ValidationErrors.Count > 0 ? result : base.ValidateEntity(entityEntry, items);
                }
            }
        }

如果需要获取EF在数据库中创建约束/索引等,那么请看:

Entity Framework Code First Fluent Api: Adding Indexes to columns

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-15
    • 2011-12-22
    • 1970-01-01
    • 2012-12-31
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多