我正在使用Asp.Net Core 2.1和Code First开发一个Web应用程序。我拥有一堆十进制类型的属性,并使用以下属性对其进行了修饰:
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
问题是当表单进入编辑模式时,由于输入字段包含货币符号,客户端验证会引发以下错误:
该字段必须是数字。
如何告诉asp.net核心将带有货币符号的输入字段视为十进制值?
答案 0 :(得分:0)
尝试创建一个custom model binder,将{$ 15,481“转换为小数。
您想要的结果是将输入验证为货币而不是小数,但是在模型绑定发生之前,您可能还需要清理输入,因此您将使用描述“清理”操作的界面。
public interface IScrubberAttribute
{
object Scrub(string modelValue, out bool success);
}
下一步,添加CurrencyScrubberAttribute
,它将完成用户输入的解析,以查看其是否为有效的货币格式。 C#的decimal.TryParse
具有一个重载,该重载需要一个NumberStyle
和CultureInfo
,这是进行货币验证的方法。您会注意到,目前这仅适用于美元($),但只需要设置CultureInfo即可处理其他货币。
[AttributeUsage(AttributeTargets.Property)]
public class CurrencyScrubberAttribute : Attribute, IScrubberAttribute
{
private static NumberStyles _currencyStyle = NumberStyles.Currency;
private CultureInfo _culture = new CultureInfo("en-US");
public object Scrub(string modelValue, out bool success)
{
var modelDecimal = 0M;
success = decimal.TryParse(
modelValue,
_currencyStyle,
_culture,
out modelDecimal
);
return modelDecimal;
}
}
使用新的CurrencyScrubberAttribute
,如下所示:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
[CurrencyScrubber]
public decimal Price { get; set; }
}
添加模型活页夹。对于像Product这样的强类型模型, ComplexTypeModelBinderProvider 接受挑战,然后为每个属性创建一个绑定器。
public class ScrubbingModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (!context.Metadata.IsComplexType&&context.Metadata.PropertyName!=null)
{
// Look for scrubber attributes
var propName = context.Metadata.PropertyName;
var propInfo = context.Metadata.ContainerType.GetProperty(propName);
// Only one scrubber attribute can be applied to each property
var attribute = propInfo.GetCustomAttributes(typeof(IScrubberAttribute), false).FirstOrDefault();
if (attribute != null)
return new ScrubbingModelBinder(context.Metadata.ModelType, attribute as IScrubberAttribute);
}
return null;
}
}
模型绑定器将处理具有IScrubberAttribute
的简单类型,但是如果由于任何原因我们不打算处理绑定,我们会将其传递给 SimpleTypeModelBinder ,以处理。
public class ScrubbingModelBinder : IModelBinder
{
IScrubberAttribute _attribute;
SimpleTypeModelBinder _baseBinder;
public ScrubbingModelBinder(Type type, IScrubberAttribute attribute)
{
if (type == null) throw new ArgumentNullException(nameof(type));
_attribute = attribute as IScrubberAttribute;
_baseBinder = new SimpleTypeModelBinder(type);
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
// Check the value sent in
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != ValueProviderResult.None)
{
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
// Attempt to scrub the input value
var valueAsString = valueProviderResult.FirstValue;
var success = true;
var result = _attribute.Scrub(valueAsString, out success);
if (success)
{
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
}
// If we haven't handled it, then we'll let the base SimpleTypeModelBinder handle it
return _baseBinder.BindModelAsync(bindingContext);
}
}
将其添加到ConfigureServices
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new ScrubbingModelBinderProvider());
});
答案 1 :(得分:-1)
货币文本可以显示在单独的位置,数据可以显示在文本框中。
这里是相同的代码。
在模型中
public class Test
{
[Key]
[MaxLength(30)]
public string Id { get; set; }
public string Name { get; set; }
[DataType(DataType.Currency)]
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
public float? Cost { get; set; }
}
在视图中
@model CoreCodeFist.Models.Dataobj.Test
@{
ViewData["Title"] = "Home Page";
}
<div class="row">
<div class="col-md-3">
<form asp-action="Index">
<h2>Test</h2>
<div class="input-group">
<span class="input-group-addon">@string.Format("{0:C}", Model.Cost!=null?Model.Cost:0).FirstOrDefault()</span>
<input asp-for="Cost" asp-format="{0}" class="form-control" />
</div>
<div class="input-group">
<span asp-validation-for="Cost" class="text-danger"></span>
</div>
<br />
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
在控制器中
//First Page Load Method In Initialize Model Because Cost Is Initialize
public IActionResult Index()
{
return View(new Test());
}
//This Is Edit Method
public IActionResult Edit(float id)
{
Test t = new Test()
{
Cost = id
};
return View("Index",t);
}
//First Page Post Method
[HttpPost]
public IActionResult Index(Test model)
{
return View(model);
}