我遇到以下问题,我正在尝试在Windows Phone中使用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)
答案 0 :(得分:1)
如何制作一个虚拟类,其中唯一的成员是要测试的东西? (好吧,对每种类型的数据说一个成员):
public class ForYourTestsOnly
{
public string MyString { get; set;}
public int MyInt32 { get; set;}
//And so on...
}
然后:
RuleFor(test => test.MyString)
(我承认我对FluentValidation一无所知,但如果这是让它工作的唯一方法,我想我会朝那个方向搜索......)
答案 1 :(得分:1)
我找到了一种解决方法,它要求你有一个额外的字符串包装类。
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
方法。
答案 2 :(得分:-1)
的问题
RuleFor(username => username)
是Fluent Validation无法提取属性名称。 Fluent验证期望验证属性,因此它尝试从RuleFor()
参数中的表达式树中提取属性名称。由于您的表达式未提及属性,因此失败。
您可以通过明确指定属性来禁止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");
}
}