我有一个SQL Server数据库,其中十进制值总是与“。”一起存储。作为小数点分隔符。
我有一个使用用户文化的ASP.NET MVC 5应用程序。我正在使用“es-ES”文化测试应用程序,其中小数点分隔符为“,”当我尝试从具有十进制值的表中获取某些值时,它返回错误的值。
e.g。
class Model
{
public string Description {get; set;}
public decimal Length {get; set;}
}
数据库中的值
<table>
<th>Description</th>
<th>Length</th>
<tr>
<td>Dog</td>
<td>4.25</td>
</tr>
<tr>
<td>Cat</td>
<td>2.11</td>
</tr>
</table>
当我运行此查询时
var result =
from x in DbContext.Model
select x;
我得到425而不是4.25和211而不是2.11
如何告诉LINQ使用不变文化?
这只是展示我需要的内容的片段。在真实场景中,我做了类似的事情
var result =
from x in DbContext.Model
select new
{
Description = x.Description,
Length = x.Length * 2
};
答案 0 :(得分:1)
你不要告诉LINQ使用不变的文化。 LINQ只是IEnumerable<T>
和IQueryable<T>
接口的扩展函数的集合。
您正在使用DbContext
。这似乎是一个实体框架DbContext
,但即便不是,你的DbContext
应该隐藏在存储器中存储的格式数字(通常是一个SQL数据库,但不是#39;必须是,它可以是CSV文件,EXCEL,JSon,等等。
如果仔细查看对象DbContext.Model
,您会发现此对象的类实现了IQueryable<Model>
。
这意味着,此对象可以容纳两件事:Expression
和&#39;提供商&#39;。 (好吧,也是一个elementType,在你的例子中这将是Model
,在这个讨论中没有意义)
当您使用DbContext.Model
作为查询的起点时,实际上是在更改Expression
内的IQueryable<Model>
。只要您不要求模型序列中的第一个元素,就不会使用Provider
。
Provider
隐藏了您的模型序列的存储方式。 Provider
知道它是关系数据库,还是CSV文件,或者其他什么。 Provider
的任务是将Expression
转换为存储理解并从存储中获取数据的格式。
对于Entity Framework DbContexts,这通常意味着存储是一个SQL(类似)数据库。 Provider
知道数据库模型,它知道如何将Expression
转换为SQL以及如何与数据库进行通信。
阅读本文之后,您应该明白,DbContext.Model对象的用户不应该知道存储以什么格式保存小数。 Provider
的任务是将小数转换为存储格式。如果此存储格式将小数存储为西班牙语字符串,则Provider
应将小数转换为西班牙语字符串。
现在回到你的问题,因为这些知识无法解决你的问题。
您应该调查西班牙语符号渗透到DbContext.Models
似乎数据库的存储被告知将Length存储为字符串而不是小数。
如果您的数据库是SQL,则小数不会保存为字符串,而是保存为具有一定精度的值。
如果数据库将Length保存为字符串,则数据库的设计者告诉实体框架Model.Length
是字符串,而不是小数。在这种情况下,Model
类的定义不能正确表示数据库中Model表的内容。 Model.Length
不应该是小数,而是字符串,因此表示数据库Model
表的结构
简洁的解决方案是将数据库中的Length列从字符串更改为value-with-precision(无论您使用何种数据库)。如果您无法更改数据库Model
表的结构,则应更改Model类,使其代表您的模型:
class Model
{
public string Description {get; set;}
public string Length {get; set;}
private static IFormatProvider spanishCulture = CultureInfo.CreateSpecificCulture("es-ES");
[NotMapped]
public decimal LengthInMeters
{
get {return Decimal.Parse(this.Length, spanishCulture);}
set {this.Length = value.ToString(spanishCulture); }
}
}
这也解决了未知单位长度(m?cm?英寸?)
的问题一个更清晰的解决方案,让您的Model
类完全代表数据库表,将创建一个SpanishModel
类并创建扩展函数以从Model
转换为{{ 1}}并返回
SpanishModel
用法:
// model represents exactly database table:
class Model
{
public string Description {get; set;}
public string Length {get; set;}
}
class SpanishModel
{
public string Description {get; set;}
public decimal Length {get; set;}
}
// extension functions of model:
static class ModelExtensions
{
private static IFormatProvider spanishCulture = CultureInfo.CreateSpecificCulture("es-ES");
public static SpanishModel AsSpanishModel(this Model model)
{
return new SpanishModel()
{
Description = model.Description,
Length = Decimal.Parse(model.Length, spanishCulture);
}
}
public static Model AsModel(this SpanishModel model)
{
return new Model()
{
description = model.Description,
Length = model.Length.ToString(spanishCulture),
};
}
public static IEnumerable<SpanishModel> AsSpanishModels(this IEnumerable<Model> models)
{
return models.Select(model => model.AsSpanishModel();
}
public static IEnumerable<SpanishModel> AsModels(this IEnumerable<SpanishModel> models)
{
return models.Select(model => model.AsModel();
}
}
请参阅Extension Functions Demystified
如果您确实想隐藏SpanishModels不属于您的数据库的事实,请考虑扩展您的DbContext类:
IEnumerable<SpanishModel> requestedModels = myDbContext.Models
.Where(model => model.Description == ...)
.AsSpanishModels();
用法:
static class MyDbContextExtensions
{
public static IQueryable<SpanishModel> SpanishModel(this MyDbContext dbContext)
{
return dbContext.Models.AsSpanishModels();
}
}
您几乎看不到using (var myDbContext = new MyDbContext(...))
{
IQueryable<SpanishModel> tallSpanishModels = myDbContext.SpanishModels
.Where(model => model.Length > 1.85M);
foreach (SpanishModel tallModel in tallSpanishModels)
{
Console.WriteLine($"{tallModel.Description} has a length of {tallModel.Length}";
}
}
的其他DbSets
与DbContext
之间存在任何差异。想想看,我认为这是最好的解决方案。