将日期时间转换为 LINQ-to-entities 查询中的格式化字符串

Posted

技术标签:

【中文标题】将日期时间转换为 LINQ-to-entities 查询中的格式化字符串【英文标题】:Convert datetime to a formatted string inside a LINQ-to-entities query 【发布时间】:2011-12-21 10:27:20 【问题描述】:

如何将DateTime 转换为格式化字符串?

这是以下查询中需要帮助的行:

StartDate = string.Format("0:dd.MM.yy", p.StartDate)

整个查询:

var offer = (from p in dc.CustomerOffer
             join q in dc.OffersInBranch
             on p.ID equals q.OfferID
             where q.BranchID == singleLoc.LocationID
             let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
             orderby value descending
             select new Offer()
             
                 Title = p.OfferTitle,
                 Description = p.Description,
                 BestOffer = value,
                 ID = p.ID,
                 LocationID = q.BranchID,
                 LocationName = q.CustomerBranch.BranchName,
                 OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
                 NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
                 StartDate = string.Format("0:dd.MM.yy", p.StartDate)
             ).First();

我收到以下错误消息:

LINQ to Entities 无法识别方法“System.String ToString(System.String)”方法,并且该方法无法转换为存储表达式。

【问题讨论】:

【参考方案1】:

另一种选择是使用SqlFunctions.DateName,您的代码将是这样的:

var offer = (from p in dc.CustomerOffer
                 join q in dc.OffersInBranch
                     on p.ID equals q.OfferID
                 where q.BranchID == singleLoc.LocationID
                 let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
                 orderby value descending
                 select new
                 
                     Title = p.OfferTitle,
                     Description = p.Description,
                     BestOffer = value,
                     ID = p.ID,
                     LocationID = q.BranchID,
                     LocationName = q.CustomerBranch.BranchName,
                     OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
                     NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
                     StartDate = SqlFunctions.DateName("day", p.StartDate) + "/" + SqlFunctions.DateName("month", p.StartDate) + "/" +  SqlFunctions.DateName("year", p.StartDate)
                 )

如果您不想添加额外的选择新块,我发现它很有用。

【讨论】:

我必须添加这个使用:using System.Data.Objects.SqlClient;才能使用SqlFunctions 添加对 System.Data.Entity.dll 的引用,如果您之前尝试使用 sqlfunctions 您也可以使用 SqlFunctions.DatePart 获取特定格式【参考方案2】:

编辑:现在我理解了这个问题,我再试一次:)

var offer = (from p in dc.CustomerOffer
                     join q in dc.OffersInBranch
                         on p.ID equals q.OfferID
                     where q.BranchID == singleLoc.LocationID
                     let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
                     orderby value descending
                     select new
                     
                         Title = p.OfferTitle,
                         Description = p.Description,
                         BestOffer=value,
                         ID=p.ID,
                         LocationID=q.BranchID,
                         LocationName=q.CustomerBranch.BranchName,
                         OriginalPrice=SqlFunctions.StringConvert((decimal)p.OriginalPrice),
                         NewPrice=SqlFunctions.StringConvert((decimal)p.NewPrice),
                         StartDate=p.StartDate

                     )
                     .ToList()
                     .Select(x => new Offer()
                     
                         Title = x.OfferTitle,
                         Description = x.Description,
                         BestOffer=value,
                         ID=x.ID,
                         LocationID=x.BranchID,
                         LocationName=x.CustomerBranch.BranchName,
                         OriginalPrice=x.OriginalPrice,
                         NewPrice=x.NewPrice,
                         StartDate=x.StartDate.ToString("dd.MM.yy")
                     ).First();

我知道它有点长,但这就是 Linq To SQL 的问题。

当您使用 linq 时,数据库调用不会执行,直到您使用诸如 ToList() 或 First() 之类的方法来生成实际对象。一旦该 SQL 调用由第一个 .First() 调用执行,您现在就可以使用 .NET 类型,并且可以使用 DateTime 内容。

【讨论】:

错误:LINQ to Entities 无法识别方法 'System.String ToString(System.String)' 方法,并且该方法无法转换为存储表达式。 p.StartDate 是 DateTime 还是字符串? 是的。 LINQ 无法将日期时间转换为字符串。有很多关于这个的论坛帖子。看到这个***.com/questions/6887776/…。但我不知道如何让它工作。 让我们continue this discussion in chat 你应该替换第一个First(),呃,例如Take(1) 为后续的 Select 获取 IEnumerable。或者把它排除在外。【参考方案3】:

我最终使用了sql函数FORMAT;这是此实现的简化版本:

https://weblogs.asp.net/ricardoperes/registering-sql-server-built-in-functions-to-entity-framework-code-first

首先你需要在EF中定义函数:

public class FormatFunctionConvention : IStoreModelConvention<EdmModel>

    public void Apply(EdmModel item, DbModel model)
    
        var payload = new EdmFunctionPayload
        
            StoreFunctionName = "FORMAT",
            Parameters = new[] 
                FunctionParameter.Create("value", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.DateTime), ParameterMode.In),
                FunctionParameter.Create("format", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.In)
            ,
            ReturnParameters = new[] 
                FunctionParameter.Create("result", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.ReturnValue)
            ,
            Schema = "dbo",
            IsBuiltIn = true
        ;

        item.AddItem(EdmFunction.Create("FORMAT", "CodeFirstDatabaseSchema", item.DataSpace, payload, null));
    

然后将其定义为 C# 方法:

public static class SqlFunctions

    [DbFunction("CodeFirstDatabaseSchema", "FORMAT")]
    public static String Format(this DateTime value, string format)
    
        return value.ToString(format);
    

    [DbFunction("CodeFirstDatabaseSchema", "FORMAT")]
    public static String Format(this DateTime? value, string format)
    
        return value?.ToString(format);
    

在您的DbContext注册它:

public class SqlDb : DbContext

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        base.OnModelCreating(modelBuilder);
        modelBuilder.Conventions.Add(new FormatFunctionConvention());
    

最后,你可以这样称呼它:

var x = db.MyItems.Select(i => new  FormattedDate = SqlFunctions.Format(i.MyDate, "MM/dd/yyyy") ).ToArray();

【讨论】:

【参考方案4】:

这就是我们所做的,我们在类中添加了一个新函数,并在查询中正常查询日期:

[ComplexType]
public class Offer
    
    public DateTime StartDate 
    
        get;
        set;
    

   public String Title
   
       get;
       set;
   

   /*Other fields*/      
   .
   .
   .


    public string FormattedDate(string format)
    
        return Date.ToString(format);
    




var offer = (from p in dc.CustomerOffer
         join q in dc.OffersInBranch
         on p.ID equals q.OfferID
         where q.BranchID == singleLoc.LocationID
         let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
         orderby value descending
         select new Offer()
         
             Title = p.OfferTitle,
             Description = p.Description,
             BestOffer = value,
             ID = p.ID,
             LocationID = q.BranchID,
             LocationName = q.CustomerBranch.BranchName,
             OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
             NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
             StartDate = p.StartDate
         ).First();

然后您可以调用 FormattedDate 字段传递所需的格式。

edit1.Text = offer.FormattedDate("dd.MM.yy");

或者可以将其定义为仅使用 getter 的字段:

    public string FormattedDate
                
                   get  return Date.ToString("dd.MM.yy") ;
                

 edit1.Text = offer.FormattedDate;

如果您的类是实体,则需要声明该类的新部分并添加字段。

希望这对某人有所帮助。

【讨论】:

【参考方案5】:

vb 中(对 c# 也有效):

Imports System.Data.Entity
... 
query.Select(Function(x) New MyObject With 
    ...
    .DateString = DbFunctions.Right("00" & x.DateField.Day, 2) & "/" & DbFunctions.Right("00" & x.DateField.Month, 2) & "/" & x.DateField.Year
    ...
).ToList()

注意:ToList(), ToEnumerable() 不是这种方式,因为它执行查询,用户想要 linq to sql..

【讨论】:

【参考方案6】:

如果是日期时间,您需要使用.ToShortDateString()。但是你还需要将它声明为 AsEnumerable()。

var offer = (from p in dc.CustomerOffer.AsEnumerable()
                 join q in dc.OffersInBranch
                     on p.ID equals q.OfferID
                 where q.BranchID == singleLoc.LocationID
                 let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
                 orderby value descending
                 select new
                 
                     Title = p.OfferTitle,
                     Description = p.Description,
                     BestOffer=value,
                     ID=p.ID,
                     LocationID=q.BranchID,
                     LocationName=q.CustomerBranch.BranchName,
                     OriginalPrice=SqlFunctions.StringConvert((decimal)p.OriginalPrice),
                     NewPrice=SqlFunctions.StringConvert((decimal)p.NewPrice),
                     StartDate=p.StartDate

                 )
                 .ToList()
                 .Select(x => new Offer()
                 
                     Title = x.OfferTitle,
                     Description = x.Description,
                     BestOffer=value,
                     ID=x.ID,
                     LocationID=x.BranchID,
                     LocationName=x.CustomerBranch.BranchName,
                     OriginalPrice=x.OriginalPrice,
                     NewPrice=x.NewPrice,
                     StartDate=x.StartDate.ToShortDateString()
                 ).First();

【讨论】:

没有。如果我们只使用 iqueryable,ToShortDateString 将不起作用。

以上是关于将日期时间转换为 LINQ-to-entities 查询中的格式化字符串的主要内容,如果未能解决你的问题,请参考以下文章

Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN

将当地时间转换为UTC时间

SQL将日期时间转换为日期名称

sql server2000如何将数字转换为日期时间

将时间戳转化为日期格式

将日期转换为 SQL 日期时间