一致地使用“现在”的值通过交易 [英] Consistently using the value of "now" througout the transaction

查看:103
本文介绍了一致地使用“现在”的值通过交易的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找在整个交易中使用当前日期和时间的一致值的指南。



通过事务我轻松地意味着应用程序服务方法,例如至少在我的应用程序中,方法通常执行单个SQL事务。



环境上下文



这个问题的答案是将当前日期放在环境中,例如 DateTimeProvider ,并使用它而不是 DateTime.UtcNow 到处。



但是,这种方法的目的仅在于使设计单元可以测试,而我也想防止由于不必要的多次查询引起的错误导致 DateTime.UtcNow 例如:

  //在实体构造函数中:
this.CreatedAt = DateTime.UtcNow;
this.ModifiedAt = DateTime.UtcNow;

此代码创建一个具有稍微不同的创建和修改日期的实体,而有人希望这些属性相等在实体创建之后。



另外,环境上下文很难在Web应用程序中正确实现,所以我提出了一种替代方法: p>

方法注入+ DeterministicTimeProvider




  • DeterministicTimeProvider class被注册为每个生命周期范围的实例AKA在Web应用程序中的每个HTTP请求的实例依赖。

  • 它是构造函数注入到应用程序服务并传递使用实体的构造函数和方法。

  • 使用 IDateTimeProvider.UtcNow 方法而不是通常的 DateTime。 UtcNow / DateTimeOffset.UtcNow 到处获取当前日期和时间。



这是实现:

  ///< summary> 
///提供当前日期和时间。
///当第一次请求时,提供的值是固定的。
///< / summary>
public class DeterministicTimeProvider:IDateTimeProvider
{
private readonly Lazy< DateTimeOffset> _lazyUtcNow =
new Lazy< DateTimeOffset>(()=> DateTimeOffset.UtcNow);

///< summary>
///获取UTC时区中的当前日期和时间。
///< / summary>
public DateTimeOffset UtcNow => _lazyUtcNow.Value;
}

这是一个好办法吗?有什么缺点?有更好的选择吗?

解决方案

嗯..这感觉就像一个更好的问题, CodeReview.SE StackOverflow ,但肯定 - 我会咬人的。


这是一个好办法吗?


如果使用正确,在您描述的场景中,这种方法是合理的。它实现了以下两个目标:


  1. 使您的代码更具可测试性。这是一个常见的模式,我称之为模拟时钟,并在许多精心设计的应用程序中找到。


  2. 将时间锁定到单个值。这不太常见,但您的代码确实实现了这一目标。





什么是缺点?





  1. 由于您正在为每个请求创建另一个新对象,它将创建一个额外的内存使用温和,垃圾收集器的额外工作。这有点是一个微不足道的地方,因为这通常是针对包含控制器在内的所有请求生命周期的对象。


  2. 有一小部分在您从时钟读取之前添加的时间,这是由于加载对象和进行延迟加载而进行的其他工作所引起的。这可以忽略不计 - 可能在几毫秒的数量级。


  3. 由于该值被锁定,因此您(或另一位使用的开发人员)你的代码)可能会引入一个微妙的错误,忘记了值将不会改变,直到下一个请求。您可能会考虑不同的命名约定。例如,而不是now,称之为requestRecievedTime或类似的东西。


  4. 与上一个项目类似,您的提供商也有风险可能会加载错误的生命周期。您可以在新项目中使用它,并忘记设置实例,将其加载为单例。然后锁定所有请求的值。执行这个没有多少可以做,所以一定要很好的评论。


  5. 您可能会发现需要当前的时间(< summary> 在不能执行构造函数注入的情况下,例如静态方法。您将不得不重构使用实例方法,或者必须将时间或时间提供者作为参数传递到静态方法中。





有更好的选择吗?


是的,请参阅 Mike的回答



您还可以考虑 Noda Time ,它内置了类似的概念,通过 IClock 界面, code> SystemClock 和 FakeClock 实现。然而,这两个实现都被设计为单例。它们帮助测试,但是它们没有实现您的第二个目标,即将每个请求的时间锁定到单个值。你可以随时写一个这样的实现。


I'm looking for guidelines to using a consistent value of the current date and time throughout a transaction.

By transaction I loosely mean an application service method, such methods usually execute a single SQL transaction, at least in my applications.

Ambient Context

One approach described in answers to this question is to put the current date in an ambient context, e.g. DateTimeProvider, and use that instead of DateTime.UtcNow everywhere.

However the purpose of this approach is only to make the design unit-testable, whereas I also want to prevent errors caused by unnecessary multiple querying into DateTime.UtcNow, an example of which is this:

// In an entity constructor:
this.CreatedAt = DateTime.UtcNow;
this.ModifiedAt = DateTime.UtcNow;

This code creates an entity with slightly differing created and modified dates, whereas one expects these properties to be equal right after the entity was created.

Also, an ambient context is difficult to implement correctly in a web application, so I've come up with an alternative approach:

Method Injection + DeterministicTimeProvider

  • The DeterministicTimeProvider class is registered as an "instance per lifetime scope" AKA "instance per HTTP request in a web app" dependency.
  • It is constructor-injected to an application service and passed into constructors and methods of entities.
  • The IDateTimeProvider.UtcNow method is used instead of the usual DateTime.UtcNow / DateTimeOffset.UtcNow everywhere to get the current date and time.

Here is the implementation:

/// <summary>
/// Provides the current date and time.
/// The provided value is fixed when it is requested for the first time.
/// </summary>
public class DeterministicTimeProvider: IDateTimeProvider
{
    private readonly Lazy<DateTimeOffset> _lazyUtcNow =
        new Lazy<DateTimeOffset>(() => DateTimeOffset.UtcNow);

    /// <summary>
    /// Gets the current date and time in the UTC time zone.
    /// </summary>
    public DateTimeOffset UtcNow => _lazyUtcNow.Value;
}

Is this a good approach? What are the disadvantages? Are there better alternatives?

解决方案

Hmmm.. this feels like a better question for CodeReview.SE than for StackOverflow, but sure - I'll bite.

Is this a good approach?

If used correctly, in the scenario you described, this approach is reasonable. It achieves the two stated goals:

  1. Making your code more testable. This is a common pattern I call "Mock the Clock", and is found in many well-designed apps.

  2. Locking the time to a single value. This is less common, but your code does achieve that goal.

What are the disadvantages?

  1. Since you are creating another new object for each request, it will create a mild amount of additional memory usage and additional work for the garbage collector. This is somewhat of a moot point since this is usually how it goes for all objects with per-request lifetime, including the controllers.

  2. There is a tiny fraction of time being added before you take the reading from the clock, caused by the additional work being done in loading the object and from doing lazy loading. It's negligible though - probably on the order of a few milliseconds.

  3. Since the value is locked down, there's always the risk that you (or another developer who uses your code) might introduce a subtle bug by forgetting that the value won't change until the next request. You might consider a different naming convention. For example, instead of "now", call it "requestRecievedTime" or something like that.

  4. Similar to the previous item, there's also the risk that your provider might be loaded with the wrong lifecycle. You might use it in a new project and forget to set the instancing, loading it up as a singleton. Then the values are locked down for all requests. There's not much you can do to enforce this, so be sure to comment it well. The <summary> tag is a good place.

  5. You may find you need the current time in a scenario where constructor injection isn't possible - such as a static method. You'll either have to refactor to use instance methods, or will have to pass either the time or the time-provider as a parameter into the static method.

Are there better alternatives?

Yes, see Mike's answer.

You might also consider Noda Time, which has a similar concept built in, via the IClock interface, and the SystemClock and FakeClock implementations. However, both of those implementations are designed to be singletons. They help with testing, but they don't achieve your second goal of locking the time down to a single value per request. You could always write an implementation that does that though.

这篇关于一致地使用“现在”的值通过交易的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆