2

次の問題があります。WindowsPhoneでFluentValidationを使用して、文字列が有効なユーザー名であるかどうかを検証しようとしています。

string私が抱えている問題は、バリデーターの値がテストする必要のあるタイプを持っていることです。すべての例で、このようにテストされているのは常にオブジェクトのプロパティであることがわかります。RuleFor(customer => customer.Surname).NotEmpty();

このコードを実行すると、次のエラーが発生します

コード:

public class UsernameValidator : AbstractValidator<string>
{
    public UsernameValidator()
    {
        RuleFor(username => username).NotNull().NotEmpty().WithMessage("Enter a username");
        RuleFor(username => username).Matches("^[a-zA-Z0-9_]*$").WithMessage("Only letters and numbers");
        RuleFor(username => username).Length(3, 30).WithMessage("Minimum length is 3");
    }
}

エラー:

FluentValidation for WP7 can only be used with expressions that reference public properties, ie x => x.SomeProperty
   at FluentValidation.Internal.Extensions.CompilePropertyGetterExpression(LambdaExpression expression, Type delegateType)
   at FluentValidation.Internal.Extensions.Compile[TDelegate](Expression`1 expression)
   at FluentValidation.Internal.PropertyRule`1.Create[TProperty](Expression`1 expression, Func`1 cascadeModeThunk)
   at FluentValidation.AbstractValidator`1.RuleFor[TProperty](Expression`1 expression)
   at WorldChat.ViewModels.Validators.UsernameValidator..ctor()
   at WorldChat.ViewModels.RegisterViewModel.get_ErrorUsername()
   at System.Reflection.RuntimeMethodInfo.InternalInvoke(RuntimeMethodInfo rtmi, Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess, StackCrawlMark& stackMark)
   at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, StackCrawlMark& stackMark)
   at System.Reflection.RuntimePropertyInfo.InternalGetValue(PropertyInfo thisProperty, Object obj, Object[] index, StackCrawlMark& stackMark)
   at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
   at System.Windows.CLRPropertyListener.get_Value()
   at System.Windows.PropertyAccessPathStep.ConnectToPropertyInSource(Boolean isSourceCollectionViewCurrentItem)
   at System.Windows.PropertyAccessPathStep.ConnectToProperty()
   at System.Windows.PropertyAccessPathStep.ReConnect(Object newSource)
   at System.Windows.PropertyPathListener.ReConnect(Object source)
   at System.Windows.Data.BindingExpression.SourceAcquired()
   at System.Windows.Data.BindingExpression.System.Windows.IDataContextChangedListener.OnDataContextChanged(Object sender, DataContextChangedEventArgs e)
   at System.Windows.Data.BindingExpression.DataContextChanged(Object sender, DataContextChangedEventArgs e)
   at System.Windows.FrameworkElement.OnDataContextChanged(DataContextChangedEventArgs e)
   at System.Windows.FrameworkElement.OnAncestorDataContextChanged(DataContextChangedEventArgs e)
   at System.Windows.FrameworkElement.NotifyDataContextChanged(DataContextChangedEventArgs e)
   at System.Windows.FrameworkElement.OnAncestorDataContextChanged(DataContextChangedEventArgs e)
   at System.Windows.FrameworkElement.NotifyDataContextChanged(DataContextChangedEventArgs e)
   at System.Windows.FrameworkElement.OnTreeParentUpdated(DependencyObject newParent, Boolean bIsNewParentAlive)
   at System.Windows.DependencyObject.UpdateTreeParent(IManagedPeer oldParent, IManagedPeer newParent, Boolean bIsNewParentAlive, Boolean keepReferenceToParent)
   at MS.Internal.FrameworkCallbacks.ManagedPeerTreeUpdate(IntPtr oldParentElement, IntPtr parentElement, IntPtr childElement, Byte bIsParentAlive, Byte bKeepReferenceToParent, Byte bCanCreateParent)
   at MS.Internal.XcpImports.MeasureNative(IntPtr element, Single inWidth, Single inHeight)
   at MS.Internal.XcpImports.UIElement_Measure(UIElement element, Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.ScrollViewer.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight)
   at MS.Internal.XcpImports.MeasureOverrideNative(IntPtr element, Single inWidth, Single inHeight, Single& outWidth, Single& outHeight)
   at MS.Internal.XcpImports.FrameworkElement_MeasureOverride(FrameworkElement element, Size availableSize)
   at System.Windows.FrameworkElement.MeasureOverride(Size availableSize)
   at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight)
   at MS.Internal.XcpImports.MeasureOverrideNative(IntPtr element, Single inWidth, Single inHeight, Single& outWidth, Single& outHeight)
   at MS.Internal.XcpImports.FrameworkElement_MeasureOverride(FrameworkElement element, Size availableSize)
   at System.Windows.FrameworkElement.MeasureOverride(Size availableSize)
   at Microsoft.Phone.Controls.PhoneApplicationFrame.MeasureOverride(Size availableSize)
   at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight)
4

3 に答える 3

2

回避策を見つけました。追加の文字列ラッパークラスが必要です。

ValidationString.cs

public class ValidationString
{
    public string value { get; set; }

    public ValidationString(string value)
    {
        this.value = value;
    }
}

ユーザー名バリデータークラスに静的メソッドIsUsernameを追加します

public class UsernameValidator : AbstractValidator<ValidationString>
{
    public UsernameValidator()
    {
        RuleFor(username => username.value).NotNull().NotEmpty().WithMessage("Enter a username");
        RuleFor(username => username.value).Matches("^[a-zA-Z0-9_]*$").WithMessage("Only letters and numbers");
        RuleFor(username => username.value).Length(3, 30).WithMessage("Minimum length is 3");
    }

    internal static bool IsUsername(string value)
    {
        return new UsernameValidator().Validate(new ValidationString(value)).IsValid;
    }
}

これで、このUsernameValidatorを文字列で使用できるだけでなく、別のバリデーター、たとえば登録バリデーターでも使用できるようになります。

public class RegisterValidator : AbstractValidator<User>
{
    public RegisterValidator()
    {
        RuleFor(user => user.Username).Must(UsernameValidator.IsUsername);
        // some more rules
    }
}

UsernameValidatorを他の検証クラスで使用したくない場合は、IsUsernameメソッドを省略できます。

于 2012-05-19T13:58:33.527 に答える
1

テストするものだけがメンバーであるダミークラスを作成するのはどうですか?(まあ、データの種類ごとに1つのメンバーと言います):

public class ForYourTestsOnly
{
    public string MyString { get; set;}
    public int MyInt32 { get; set;}
    //And so on...
}

その後 :

RuleFor(test => test.MyString)

(私はFluentValidationについて何も知らないことを告白しますが、それがそれを機能させる唯一の方法である場合、私はその方向に検索すると思います...)

于 2012-05-19T13:00:32.230 に答える
-1

の問題

RuleFor(username => username)

FluentValidationはプロパティ名を抽出できないということです。RuleFor()Fluent Validationはプロパティを検証することを想定しているため、引数の式ツリーからプロパティ名を抽出しようとします。式にプロパティが記載されていないため、これは失敗します。

プロパティを明示的に指定することにより、FVがそれを試行することを禁止できます。これは、そのプロパティが存在しない場合でも機能します。これは、私が理解している限り、FVは検証エラーを報告するためだけにプロパティを必要とするためです。

したがって、ラッパークラスを追加しなくても、次のことがうまくいきました。ValidationString

public class StringNotEmptyValidator : AbstractValidator<string>
{
    public StringNotEmptyValidator()
    {
        RuleFor(str => str).NotEmpty().OverridePropertyName("StringValue");
    }
}

これは、文字列が空でないことを検証する例です。ユーザー名の検証の場合、バリデーターは次のようになります(これはテストしていませんが、概念は同じです)。

public class UsernameValidator : AbstractValidator<string>
{
    public UsernameValidator()
    {
        RuleFor(username => username).NotNull().NotEmpty().WithMessage("Enter a username").OverridePropertyName("UsernameValue");
        RuleFor(username => username).Matches("^[a-zA-Z0-9_]*$").WithMessage("Only letters and numbers").OverridePropertyName("UsernameValue");
        RuleFor(username => username).Length(3, 30).WithMessage("Minimum length is 3").OverridePropertyName("UsernameValue");
    }
}
于 2012-06-13T07:48:16.370 に答える