LINQ to Entities与文化冲突

时间:2017-10-26 19:12:48

标签: c# asp.net-mvc linq

我有一个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
   };

1 个答案:

答案 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}"; } } 的其他DbSetsDbContext之间存在任何差异。想想看,我认为这是最好的解决方案。