代码合同:需要未经证实的来源!= null

时间:2011-12-27 16:37:51

标签: c# code-contracts

在阅读了对此类问题的其他一些回复之后,我仍然留下了警告。在这个片段中,我从我的数据库中提取UserActivation。到目前为止,总会有至少一个UserActivation。如果有更多的东西已经全部变成了梨形状...我遵循了一些其他说明如何压制关于未经证实的来源的警告但无效。警告是:警告83 CodeContracts:要求未经证实:source!= null在第161行,请参阅下面的具体行的方法。

这是方法

    private static UserActivation GetUserActivation(Guid userId)
    {
        UserActivations userActivations = UserActivation.GetUserActivationsByUser(userId: userId);

        Contract.Assume(userActivations != null);

        if (userActivations.Count() > 1) // Line Number 161
            throw new Exception("More then one user action found in database");

        return userActivations[0];
    }

我使用的是CC版本1.4.40602.0,这里要求的是UserActivations声明。

public class UserActivations : BusinessListBase<UserActivation>
{
    #region Constructors
    internal UserActivations()
    {
    }

    internal UserActivations(IList<UserActivation> list)
        : base(list)
    {
    }

    internal UserActivations(IEnumerable<UserActivation> list)
        : base(list)
    {
    }

这是GetUserActivationByUser方法

    public static UserActivations GetUserActivationsByUser(User user = null, Guid userId = new Guid())
    {
        Contract.Requires(user != null || userId != null, "Either user or userId must have a value");
        Contract.Ensures(Contract.Result<UserActivations>() != null);

        Guid id = new Guid();
        if (user != null)
            id = user.Id;
        else
            id = userId;

        return new UserActivations(StorageManager.SelectAll(
            Criteria.And(
            Criteria.EqualTo("UserId", id),
            Criteria.EqualTo("Deleted", false))));
    }

原始代码是:

Public static UserActivations GetUserActivationsByUser(User user = null, Guid userId = new Guid())
    {
        Guid id = new Guid();
        if (user != null)
            id = user.Id;
        else
            if (userId != Guid.Empty)
                id = userId;
            else
                throw new Exception("Either user or userId must have a value");

        UserActivations uas = new UserActivations(StorageManager.SelectAll(
            Criteria.And(
            Criteria.EqualTo("UserId", id),
            Criteria.EqualTo("Deleted", false))));

        Contract.Ensures(Contract.Result<UserActivations>() != null);

        return uas;
    }

2 个答案:

答案 0 :(得分:4)

我会尝试使用不同的方法,也许避免使用linq Enumerable扩展方法。 UserActivations类是否有自己的方法或属性来确定实例保存的元素数量?

无论如何,您不应使用Count()扩展方法来测试序列是否为空,因为它将枚举整个序列(如果序列未实现ICollection)。或者,正如Pavel Gatilov指出的那样,如果对象实现IQueryable,则Count可能会意外地执行数据库查询。

这里没什么大不了的,你期望有一个元素,但在一个序列可能经常有数千个元素的情况下,它可能是一个大问题。相反,您应该使用Any()扩展方法。

因为使用Any()可能不会改变合同分析器的观点,所以你应该使用UserActivations类的Count属性(假设它实现了ICollection)。

也许您可以通过这种方式帮助合同分析器:

private static UserActivation GetUserActivation(Guid userId) 
{ 
    UserActivations userActivations = UserActivation.GetUserActivationsByUser(userId: userId); 

    IEnumerable<UserActivation> e = (IEnumerable<UserActivation>)userActivations;

    Contract.Assume(e != null); 

    if (e.Count() > 1) // Line Number 161 
        throw new Exception("More then one user action found in database"); 

    return userActivations[0]; 
} 

如果您控制UserActivations类,更好的解决方案是将Contract.Ensures添加到GetUserActivationsByUser,以表明该方法永远不会返回null。

答案 1 :(得分:2)

检查您的构建日志。您错误地使用了Contract.Ensures。您应该对错误的使用情况发出警告,例如:

  

警告CC1025:CodeContracts:在合同块之后,发现使用合同块中定义的局部变量'dataEvents'

你的方法应该是:

public static UserActivations GetUserActivationsByUser(User user = null, Guid userId = new Guid())
{
    Contract.Ensures(Contract.Result<UserActivations>() != null);

    Guid id = new Guid();
    if (user != null)
        id = user.Id;
    else
        if (userId != Guid.Empty)
            id = userId;
        else
            throw new Exception("Either user or userId must have a value");

    UserActivations uas = new UserActivations(StorageManager.SelectAll(
        Criteria.And(
        Criteria.EqualTo("UserId", id),
        Criteria.EqualTo("Deleted", false))));

    return uas;
}

我无法证明,但我想这可能会让验证者发疯。

事实上,如果我打开运行时检查,你的代码甚至都不会编译:ccrewrite会失败。