Код контракта: требуется недоказанный источник! = Ноль - PullRequest
2 голосов
/ 27 декабря 2011

После прочтения некоторых других ответов на этот вопрос я все еще остаюсь с предупреждением. В этом фрагменте я извлекаю активацию пользователя из своей базы данных. К этому моменту всегда будет хотя бы одна Активация пользователя. Если есть что-то более одного, то все превратилось в грушу ... Я следовал некоторым другим инструкциям о том, как подавить предупреждение о недоказанном источнике, но безрезультатно. Предупреждение: Предупреждение 83 CodeContracts: требуется недоказано: источник! = Ноль в строке 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 ]

4 голосов
/ 27 декабря 2011

Я бы попробовал использовать другой метод, возможно, избегая методов расширения linq Enumerable. Разве класс UserActivations не имеет собственного метода или свойства, чтобы определить, сколько элементов содержит экземпляр?

В любом случае не следует использовать метод расширения Count() для проверки того, является ли последовательность пустой, поскольку она будет перечислять всю последовательность (если последовательность не реализует ICollection). Или, как отметил Павел Гатилов, если объект реализует IQueryable, Count может неожиданно выполнить запрос к базе данных.

Это не имеет большого значения здесь, где вы ожидаете иметь один элемент, но это может быть очень важно в тех случаях, когда последовательность может регулярно иметь тысячи элементов. Вместо этого вы должны использовать метод расширения Any().

Поскольку использование Any(), вероятно, ничего не изменит с точки зрения анализатора контрактов, тем не менее, вам следует использовать свойство Count класса UserActivations (если, например, оно реализует 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, чтобы сказать, что метод никогда не вернет ноль.

2 голосов
/ 27 декабря 2011

Проверьте ваш журнал сборки.Вы используете 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 завершится ошибкой.

...