简体   繁体   中英

Entity framework, code first and datetimes

So, I have a very large project with heavy DI's in place. The solution is overly architected and very complex. The solution was developed using EF Code first approach (no model exists) and with most of the table objects, it contains one or more DateTime properties.

It has come (way late in the process!) to the point where I need to convert the datetimes into UTC format. For existing data in the DB, I can run a SQL Script and do a conversion easily as a one time job.

But for the future and running code, what is the best way to convert (at the time of the insert, update and select) from the UTC format TO and FROM the DB and apply an offset (ie -2 hours) to show to the UI the correct datetime? (ie UTC to PST)

One thing to bare in mind is that there is ALOT of code, ALOT of nested and deep down buried code and I want to be able to find the easiest way, without touching all the objects, interfaces etc... to convert it all to UTC at time of issuing the SQL command within EF. I want to use the SQL Server methods to convert to UTC when storing and convert from UTC when retrieving rather than within the .NET level.

Any thoughts or insights would be much appreciated.

There is no code to show here as this isn't a real coding issue but rather more of an insight discussion on how to get EF to execute (when it constructs the query before calling SQL) SQL Server functions for converting to and from UTC and returning back the result set with the correct datetime.

Thank you.

You can override the EF SaveChanges() method to check if any of the modified entities have a date and convert that to the UTC date. Here is an example code to put in your DB context.

public override int SaveChanges()
{
    var selectedEntityList = ChangeTracker.Entries()
                            .Where(x => x.Entity is EntityInformation &&
                            (x.State == EntityState.Added || x.State == EntityState.Modified));

    foreach (var entity in selectedEntityList)
    {

        ((EntityInformation)entity.Entity).Date = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now.ToUniversalTime());
    }

    return base.SaveChanges();
}

So this will check if any of the modified/added entities has a property called Date and set that to a UTC date. This may not be what you need but this is the way you should approach the solution to have one place to convert the dates to UTC.

A few things:

  • Yes, your database should store UTC, and yes, the you can convert to a specific time zone before displaying in the UI. However, the data access layer is not the correct place to do this. The data model should accurately reflect what's in the database.

    If you are just converting timestamps for input or display, then the best practice is to do this as early as possible on the way in, and as late as possible on the way out. For some this means that the conversion is done in a controller or view model, and for others it's pushed all the way down to the client and done in JavaScript. Where to do it is entirely up to you, but in the data access layer is usually not appropriate for that.

  • Sometimes (but not always) it makes sense to do the conversion in your domain layer , especially if the domain involves decisions made based on a local date or time. It's helpful if your domain can accurately model time in different time zones. Noda Time is is really good for that, as it contains dedicated types such as Instant , LocalDate , and ZonedDateTime .

  • Your desire to do this in one place such that you don't have to touch all the parts of your code is understandable - but I can tell you from experience that taking a one-size-fits-all approach is eventually going to come back to haunt you. The fact is, the common advice of "always use UTC" is shortsighted. Eventually you will have scenarios that need a local time value. Not all usage of DateTime is for the same purpose. Often UTC is appropriate, but sometimes it is not. If you find a way to apply UTC conversions globally in your architecture, you are giving up the ability to control that.

    In particular, timestamping is a good scenario for UTC (or DateTimeOffset ), but scheduling is not. Schedules are usually based on a local time, such as an alarm clock that goes off at 8:00 every morning (in the implied local time), or a meeting that occurs every Wednesday at 10:00 AM Eastern time.

    Also, consider that some date-time scenarios really are just about a fixed date without a time. Birthdates and work anniversaries are a good example of that. Don't try to convert those things to UTC, or you may end up being +/- 1 day.

  • Also, you said you are trying to convert to a fixed offset, but you also said you wanted to convert from UTC to PST. You should understand that PST is only in effect for part of the year. When daylight saving time is in effect, Pacific time switches to PDT. To convert time zones correctly, you can't just use a fixed offset. The conversion needs to decide whether -08:00 or -07:00 is appropriate for the timestamp being converted. See also "Time Zone != Offset" in the timezone tag wiki .

Hope that helps, and good luck with your project!

Since EF 6 you can use an Interceptor and modify the DbCommand before it hits the DB. https://msdn.microsoft.com/en-us/data/dn469464(v=vs.113).aspx#BuildingBlocks

public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) 
{ 
    for (int i = 0; i < command.Parameters.Count; i++) {
            var param = command.Parameters[i];
            if (param.DbType == DbType.DateTime) {
                // Change param.Value here
            }
        }
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM