这个问题直接源于Validating parameters properties with Code Contracts .
在那个问题中Ahmed KRAIEM和 Stephen J. Anderson声明与对象的状态正确性有关的检查如果必须始终保持,则应放在该对象内。
但是我在实现它时遇到了一个问题:在将检查实现为代码契约的不变量之后,我无法再使用对象初始化、AutoMapper 或 EntityFramework。
问题的出现是因为它们首先使用默认的空构造函数创建一个新对象,然后填充各种属性,这会触发代码契约的不变量在运行时生成异常。
包含在 Visual Studio 单元测试中的快速示例(您需要通过 NuGet 添加 AutoMapper 才能使其工作):
namespace Playground.Sandbox
{
using System.Diagnostics.Contracts;
using AutoMapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class ContractTest
{
private const int CatAge = 5;
private const string CatName = "Tama";
private const int DogAge = 10;
private const string DogName = "Poochi";
static ContractTest()
{
Mapper.CreateMap<Dog, Cat>();
}
[TestMethod]
public void EmptyConstructorShouldThrow()
{
var cat = new Cat();
Assert.AreEqual(default(int), cat.Age);
Assert.AreEqual(default(string), cat.Name);
}
[TestMethod]
public void NonEmptyConstructorShouldNotThrow()
{
var cat = new Cat(CatAge, CatName, true);
Assert.AreEqual(CatAge, cat.Age);
Assert.AreEqual(CatName, cat.Name);
}
[TestMethod]
public void ObjectInitializerShouldThrow()
{
var cat = new Cat { Age = CatAge, Name = CatName };
Assert.AreEqual(CatAge, cat.Age);
Assert.AreEqual(CatName, cat.Name);
}
[TestMethod]
public void AutoMapperConversionShouldThrow()
{
var dog = new Dog { Age = DogAge, Name = DogName };
var cat = Mapper.Map<Dog, Cat>(dog);
Assert.AreEqual(DogAge, cat.Age);
Assert.AreEqual(DogName, cat.Name);
}
private class Cat
{
public Cat()
{
}
public Cat(int age, string name, bool doesMeow)
{
this.Age = age;
this.Name = name;
}
public int Age { get; set; }
public string Name { get; set; }
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(this.Age > 0);
Contract.Invariant(!string.IsNullOrWhiteSpace(this.Name));
}
}
private class Dog
{
public int Age { get; set; }
public string Name { get; set; }
}
}
}
如果您想知道为什么没有使用 public Cat(int, string, bool)
构造函数,那是为了愚弄 AutoMapper。对于这个例子,我需要一个构造函数来初始化整个对象,AutoMapper 识别它并自动使用它来初始化目标对象。然而,我们的数据对象没有这样的构造函数(而且,它们不能,因为它们可以有几十个属性)。
唯一通过的测试是 NonEmptyConstructorShouldNotThrow
,其他的都(正确地)失败了。
问题的出现是因为它们首先使用默认的空构造函数创建一个新对象,然后填充各种属性,这会触发代码契约的不变量在运行时生成异常。
在使用对象初始化、AutoMapper 或 EntityFramework 时,我是否错误地使用了代码契约,或者没有办法在数据对象中实现不变量?
最佳答案
在 Cat
的默认构造函数中设置有效值,如下所示:
public Cat()
{
Age = 1;
Name = "Cat";
}
一切都会好起来的。
编辑: 正如 Damien_The_Unbeliever 的评论所述,对象始终 应该处于有效状态,因此在使用默认构造函数后它可能不会处于无效状态。
因此,您必须为Age
和Name
设置有效值!
否则,默认构造函数根本没有意义,即使您计划这样做,甚至您 promise 在之后的某个时候设置您的属性。
唯一的另一种方法是让你的类成为可构建,这意味着你调用一个完成初始化并激活和的方法,然后使用你的契约(Contract)检查方法.
关于c# - 是否可以在数据对象中使用代码契约的不变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20002388/