{XOR}两个可选主体的约束

时间:2017-04-29 05:55:32

标签: c# entity-framework

以下是我的元模型。

metamodel

根据上图,C的实例必须属于委托人A或B,而不是两者。

我如何正确强制执行该操作? 目前我在C上有以下属性:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace EF_CodeFirst_XOR
{
    using System.Data.Entity;

    public class XorDemo : DbContext
    {
        public XorDemo()
            : base("name=XorDemo")
        {
        }
        public virtual DbSet<Root> Roots { get; set; }
        public virtual DbSet<A> As { get; set; }
        public virtual DbSet<B> Bs { get; set; }
        public virtual DbSet<C> Cs { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<C>()
                .HasOptional(e => e.A)
                .WithOptionalPrincipal(e => e.C);

            modelBuilder.Entity<C>()
                .HasOptional(e => e.B)
                .WithOptionalPrincipal(e => e.C);
        }
    }

    public class Root
    {
        public int Id { get; set;}

        [Required]
        public A A { get; set; }

        [Required]
        public B B { get; set; }
    }

    public class A
    {
        [Key, ForeignKey(nameof(Root))]
        public int Id { get; set; }

        public virtual C C { get; set; }

        [Required]
        public virtual Root Root { get; set; }
    }

    public class B
    {
        [Key, ForeignKey(nameof(Root))]
        public int Id { get; set; }

        public virtual C C { get; set; }

        [Required]
        public virtual Root Root { get; set; }
    }

    public class C
    {
        public int Id { get; set; }


        public virtual A A { get; set; }
        public virtual B B { get; set; }

        [Required]
        [NotMapped]
        [Obsolete(message:"Internal. Only for validation!")]
        public string Constraint_A_Xor_B
        {
            get => A != null ^ B != null ? "Valid" : null;
            set { }
        }

    }
}

通过在XOR约束失败时返回null来使[Required]约束无效,从而完成工作。我认为为此目的在课堂上添加一个属性是不合适的。

可以通过其他方式强制执行约束吗?

为了完整起见,以下是整个代码。

class Program
{
    static void Main(string[] args)
    {
        var ctx = new XorDemo();

        Root r = new Root();

        r.A = new A {Root = r};

        C Ca = new C();
        r.A.C = Ca;
        Ca.A = r.A;


        r.B = new B {Root = r};
        C Cb = new C {B = r.B};

        ctx.Roots.Add(r);
        ctx.SaveChanges();
    }
}

编辑:驱动程序。

{{1}}

1 个答案:

答案 0 :(得分:2)

我实际上并没有在映射中实现它,但我会在数据实体本身中执行它,其中包含以下内容:

public class C
{
    public int Id { get; set; }

    private virtual A _a;
    public virtual A A
    {
        get
        {
            return _a;
        }
        set
        {
            if (value == null)
                throw new ArgumentNullException("A", "You must have A or B.");

            if (_b != null)
                throw new ConstraintException("You can either have A or B; make up your mind!");

            if (value != _a)
                _a = value;
        }
    }

    private virtual B _b;
    public virtual B B
    {
        get
        {
            return _b;
        }
        set
        {
            if (value == null)
                throw new ArgumentNullException("B", "You must have A or B.");

            if (_a != null)
                throw new ConstraintException("You can either have A or B; make up your mind!");

            if (value != _b)
                _b = value;
        }
    }

    public class C(A a)
    {
        if (a == null)
            throw new ArgumentNullException("a");

        this._a = a;
    }

    public class C(B b)
    {
        if (b == null)
            throw new ArgumentNullException("b");

        this._b = b;
    }

    public SwitchBforA(A a)
    {
        if (a == null)
            throw new ArgumentNullException("a", "You'd end up with both A and B null.");

        _a = a;
        _b = null;
    }

    public SwitchAforB(B b)
    {
        if (b == null)
            throw new ArgumentNullException("b", "You'd end up with both A and B null.");

        _a = null;
        _b = b;
    }
}

您可能还应该以通常的方式在数据库中添加约束。