加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 是否可以在数据对象中使用Code Contracts的不变量?

发布时间:2020-12-15 21:58:47 所属栏目:百科 来源:网络整理
导读:这个问题直接来自 Validating parameters properties with Code Contracts. 在该问题中,Ahmed KRAIEM和Stephen J. Anderson都声明,如果必须始终保持与对象的状态正确性相关的检查,则应将其置于该对象内. 但是我遇到了一个实现它的问题:在将检查实现为Code C
这个问题直接来自 Validating parameters properties with Code Contracts.

在该问题中,Ahmed KRAIEM和Stephen J. Anderson都声明,如果必须始终保持与对象的状态正确性相关的检查,则应将其置于该对象内.

但是我遇到了一个实现它的问题:在将检查实现为Code Contracts的不变量之后,我不再能够使用对象初始化,AutoMapper或EntityFramework.

出现问题的原因是它们首先使用默认的空构造函数创建一个新对象,然后填充各种属性,这会触发Code Contracts的不变量,从而在运行时生成异常.

一个快速示例包含在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.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; }
        }
    }
}

如果你想知道为什么有公共的Cat(int,string,bool)构造函数,如果没有使用它,就是愚弄AutoMapper.对于此示例,我需要一个构造函数来初始化整个对象,AutoMapper会识别它并自动使用它来初始化目标对象.但是,我们的数据对象没有这样的构造函数(而且,它们不能,因为它们可以具有许多属性).

唯一通过的测试是NonEmptyConstructorShouldNotThrow,其他都是(正确)失败.

出现问题的原因是它们首先使用默认的空构造函数创建一个新对象,从而在运行时生成异常.

我是否错误地使用了Code Contracts,或者在使用对象初始化,AutoMapper或EntityFramework时无法在数据对象中实现不变量?

解决方法

在Cat的默认构造函数中设置有效值,如下所示:

public Cat()
{
    Age = 1;
    Name = "Cat";
}

一切都会好的.

编辑:
如Damien_The_Unbeliever的注释中所述,对象始终应处于有效状态,因此在使用默认构造函数后它可能不会处于无效状态.

因此,您必须为Age和Name设置有效值!

否则,默认构造函数完全没有任何意义,即使您计划,甚至更多甚至您承诺在以后设置属性.

唯一的另一种方法是让你的类可构建,意味着你调用一个完成初始化的方法,然后激活并使用你的合同检查方法.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读