关于c#:根据使用httpcontext asp.net mvc登录的用户获取时区

Fetching timezone based on user logged in with httpcontext asp.net mvc

在我正在处理的应用程序中,我们为每个用户提供了一个选项,可以选择他们自己的timezone,当为特定用户显示数据时,我们选择timezone选择并显示相应的用户。现在按照这里提到的答案,这真是一个很棒的答案,我继续实现上面提到的选项,即在model级别转换日期,我已经完成如下:

NotificationViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class NotificationViewModel
{

    public string Text{ get; set; }
    public DateTime Moment
    {
        get
        {
            return _Created;
        }
        set
        {
            _Created = Repository.GetUserTimeZoneDateTime(value);
        }
    }
    private DateTime _Created { get; set; }
    public string Icon { get; set; }
}

Repository.cs

GetUserTimeZoneDateTime有2个重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static DateTime GetUserTimeZoneDateTime(DateTime dTime)
{
    using (var context = new EntityContext())
    {
         var tZone = context.tbl_usrs.AsNoTracking().FirstOrDefault(x => x.uname == HttpContext.Current.User.Identity.Name).preferred_timezone;
         var tZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tZone);
         return TimeZoneInfo.ConvertTimeFromUtc(dTime, tZoneInfo);
    }
}

public static DateTime GetUserTimeZoneDateTime(EntityContext context, DateTime dTime)
{
    var tZone = context.tbl_usrs.AsNoTracking().FirstOrDefault(x => x.uname == HttpContext.Current.User.Identity.Name).preferred_timezone;
    var tZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tZone);
    return TimeZoneInfo.ConvertTimeFromUtc(dTime, tZoneInfo);
}

在上面的例子中,将调用第一个重载,但是当从model级别调用时,HttpContext.Current将是null,因此它将失败。

在第二种方法中,我尝试过,timezone将从控制器级别获取。

NotificationViewModel.cs

1
2
3
4
5
6
public class NotificationViewModel
{
    public string Text { get; set; }
    public DateTime Moment { get; set; }
    public string Icon { get; set; }
}

TestController.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using (var context = new EntityContext())
{
     var localTime = Repository.GetUserTimeZoneDateTime(context, DateTime.UtcNow);
     List<NotificationViewModel> model = new List<NotificationViewModel>();
     int days = DateTime.UtcNow.DayOfWeek - DayOfWeek.Sunday;
     DateTime weekStart = localTime.AddDays(-days);
     DateTime weekEnd = weekStart.AddDays(6);
     var p = context.tbl_prchs
                     .Where(x => x.c_date <= weekEnd && x.c_date >= weekStart)
                     .Select(x => new NotificationViewModel()
                     {
                           Icon ="fa fa-gbp",
                           Moment = Repository.GetUserTimeZoneDateTime(context,x.c_date),
                           Text ="Test notes",
                     }).ToList();
     model.AddRange(p);
}

var localTime = Repository.GetUserTimeZoneDateTime(context, DateTime.UtcNow);根据首选用户timezone获取正确的datetime。但是linq expression内的Moment= Repository.GetUserTimeZoneDateTime(context,x.c_date),会抛出错误,如下所示

LINQ to Entities does not recognize the method 'System.DateTime
GetUserTimeZoneDateTime(Direct_Commercial_Van.Models.EntityDataModel.dcvEntities,
System.DateTime)' method, and this method cannot be translated into a
store expression.

这是预料之中的。还有什么其他选择可以在这里尝试实现这一目标?或者我在其他方面如何处理时区问题?


在第一种情况下,您需要将HttpContext(或User.Identity.Name)注入模型(查看构造函数),并从那里通过方法中的附加参数进入方法(所有非常混乱,不推荐)。

在第二种情况下,您需要在.Select()之前首先实现查询

1
2
3
4
5
6
var p = context.tbl_prchs
    .Where(...)
    .ToList() // materialize query in in-memory set
    .Select(x => new NotificationViewModel()
    {
    }).ToList();

但是,您的代码效率非常低,因为您为查询返回的每一行调用数据库(通过GetUserTimeZoneDateTime()方法)。 在调用查询之前,您应该更改代码以获取TimeZoneInfo

1
2
3
4
5
6
7
8
9
10
11
12
// Get the time zone info
var tZone = context.tbl_usrs.AsNoTracking().......'
var tZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tZone);
// Generate the view model
var p = context.tbl_prchs
    .Where(...)
    .ToList() // materialize query in in-memory set
    .Select(x => new NotificationViewModel()
    {
        ....
        Moment = TimeZoneInfo.ConvertTimeFromUtc(x.c_date, tZoneInfo);
    }).ToList();

或者,如果您不想首先实现查询,则可以将TimeZoneInfo注入视图模型并将Moment修改为计算属性。

1
2
3
4
5
6
7
8
9
10
11
public class NotificationViewModel
{
    public string Text { get; set; }
    public DateTime CDate { get; set; }
    public string Icon { get; set; }
    public TimeZoneInfo TimeZoneInfo { get; set; }
    public DateTime Moment
    {
        get { return TimeZoneInfo.ConvertTimeFromUtc(CDate, TimeZoneInfo); }
    }
}

然后查询将是

1
2
3
4
5
6
7
8
9
10
....
var tZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tZone);
var p = context.tbl_prchs
    .Where(...)
    .Select(x => new NotificationViewModel()
    {
        ....
        CDate = x.c_date,
        TimeZoneInfo = tZoneInfo
    }).ToList();