背景:我需要我系统的某些部分能够将各种状态消息推送到某个数据结构,以便它们可以被调用方使用,而无需将数据结构传递到方法中明确地说,以及呼叫者的需求可以不同的地方。在非Web代码中模拟请求范围
详细信息:我的应用程序有两个(可能更多)头,一个ASP.NET MVC 5网站和一个Windows服务。所以通常情况下,虽然Web应用程序的组合根将是网站本身,但我正在使用这两个“前端”连接到的独立组合根 - 这允许它们共享通用配置,因为它们几乎都是依赖注入将是100%相同的。另外,为了测试,我决定将大部分代码保留在网站之外,因为真正的单元测试控制器存在问题。
所以我的代码需要能够在任何Web请求的上下文之外运行。同样,该服务按计划执行的任何操作都需要能够作为网站的按需作业运行。因此,我的应用程序中的大部分繁重代码不在网站或服务中。
现在,回到我的状态信息的需求:
- 某些状态信息将被记录,但有可能在作为服务运行更将被记录。排队日志项并将其保存在最后可以。
- 当网站按需运行作业时,可能会记录的东西更少,因为用户可以处理的任何问题都会直接显示给用户,而为了进行调试,我们只关心直接发生错误。新消息需要立即推送到网站(可能通过websockets)。
- 此外,作业可以在调试模式或详细模式下运行,以便一次(比如在网络上)产生比另一次(来自无头服务)更多的信息或警告消息。代码生成消息根本不应该担心这些细节,除非那些会影响生产性能的东西放在编译器指令的调试模式中)。
- 此外,某些代码会将错误,警告或信息推送到从请求返回的对象中。这些很容易处理。但是其他错误,警告或信息(例如阻止所有请求的对象被取回的错误)需要在正常返回值之外冒泡。
现在我正在使用一些看起来不太理想的东西:我所有的方法都必须接受一个参数,他们可以修改这些参数来消除这些错误。例如:
public IReadOnlyCollection<UsableItem> GetUsableItems(
ReadOnlyHashSet<string> itemIds,
List<StatusMessage> statusMessages
) {
var resultItems = _itemService.Get(itemIds);
var resultItemsByHasFrobDuplicate = resultItems
.GroupBy(i => i.FrobId)
.ToLookup(grp => grp.Count() > 1, grp => grp.ToList());
statusMessages
.AddRange(
resultItemsByHasFrobDuplicate[true]
.Select(items => [email protected]"{items[0].FrobId
} is used by multiple items {string.Join(",", items.Select(i => i.usableItemId))
}")
);
return resultItemsByHasFrobDuplicate[false]
.Select(grp => grp.First())
.ToList()
.AsReadOnly();
}
所以,你可以在这里看到,虽然一般项目可以在返回值从方法(和这些项目甚至能放在他们自己的状态信息),有些则不能,调用代码无法处理重复项,并且期望收集的UsableItem
对象没有重复的FrobId
值。重复的情况是意想不到的,需要冒泡到用户或日志。
的代码将通过能够去除statusMessages
参数,并做更多的东西一样CurrentScope.PushMessage(message)
,知道这些信息会得到妥善处理基于其严重程度或其他规则大大提高(真正的消息是有几个属性的对象)。
哦,我在上面的代码中留下了一些东西。我真正要做的是:
_itemService.Get(itemIds, statusMessages); // -- take the darn parameter everywhere
哎呀。这并不理想。
我立刻想到了MiniProfiler.Current
类似,它是可在任何地方,但它限制在当前的请求。但我不明白它是如何能够是静态的,但隔离不同请求之间的任何Step
呼叫,使得用户没有得到他的输出另一个用户的步骤。另外,它不只适用于MVC?我需要这个工作,当没有MVC,只是非网络代码。
任何人都可以提出一个方法来改善我的代码,而不必绕列表传递给方法方法之后?东西,将单元测试工作也很重要,因为我需要能够建立捕获单元测试中在我的模拟鼓入的错误的手段(或能够做什么都没有,如果这不是想要的部分要测试的系统)。
P.S.我不介意对我上面的小ToLookup
模式进行委婉的批评以区分重复。我使用这种技术很多,并会对更好的方式感兴趣。
这似乎是合理的,并且至少可以让我将对象传递移动到我的方法的包含类,尽管我可能需要将一些依赖关系从静态作用域移到请求作用域(不是什么大问题)。然而,我的项目有几个部分和部分,这意味着我有相当数量的类来注入服务。我认为这比我所拥有的要好,我可能会这样做(这里是一个头脑清醒的时刻),但我仍然喜欢某种方式来使用并非真正全球化的“全球化”。 – ErikE
哦,是的,我只是本能地觉得方法注入很糟糕,但我只是没有看到更好的方法。现在你说出来了,这很明显......我没有人在这里给我一个实质性的代码审查......感叹。 – ErikE
那么,您在这里的服务可能是应用程序环境中的“全局”。你可以用Singleton/Request作用域注入它。该服务将担心如何保持消息的某些方式,以便它们可供所有应用程序使用。 –