后台:我需要系统的一部分能够将各种状态消息推送到某个数据结构,以便调用者可以使用它们,而无需将数据结构明确地传递给方法,并且呼叫者的需求可能不同。
详细信息:我的应用程序有两个(可以想象得多)头,一个ASP.NET MVC 5网站和一个Windows服务。通常情况下,虽然Web应用程序的组合根将是Web站点本身,但我使用的是单独的组合根,这些"前端" connect to - 这允许他们共享一个共同的配置,因为几乎所有的依赖注入都是100%相同的。此外,为了进行测试,我决定将大部分代码保留在网站之外,因为真正的单元测试控制器存在问题。
所以我的代码需要能够在任何Web请求的上下文之外运行。同样,服务在计划中执行的任何操作都需要能够作为网站上的按需作业运行。因此,我的应用程序中的大部分繁重代码都不在网站或服务中。
现在,回到状态消息的需要:
现在我使用的东西似乎不太理想:我的所有方法都必须接受一个他们可以修改的参数,以便冒出这些错误。例如:
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 => $@"{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
类似,但它可以在任何地方使用,但它的范围限定为当前请求。但我不明白它是如何静态的,但是在不同请求之间隔离任何ToLookup
次呼叫,这样用户就不会在其输出中获得另一个用户的步骤。另外,它不仅适用于MVC吗?当没有MVC,只有非网络代码时,我需要这个。
任何人都可以建议一种方法来改进我的代码,而不必在方法之后将列表传递给方法吗?一些适用于单元测试的东西也很重要,因为我需要能够在单元测试中设置一种方法来捕捉模拟中的冒泡错误(或者如果不是这样的话,就可以什么也不做要测试的系统的所需部分。
P.S。我并不介意对上面的{{1}}模式进行机智的批评,以便分离重复项。我经常使用这种技术,并且会对更好的方式感兴趣。
答案 0 :(得分:0)
我认为你只是以错误的方式看待这个问题。这些都不涉及或实际上与请求相关。你只需要一些可以注入的服务来推送消息。 如何这样做是无关紧要的,依赖注入的全部意义在于具有依赖性的类不应该知道或关心。
为您的消息服务创建一个界面:
public interface IMessagingService
{
void PushMessage(string message);
}
然后,您应该更改包含GetUsableItems
的类,以将消息传递服务注入构造函数。一般来说,方法注入(当前通过将List<StatusMessages>
传递给方法而进行的操作)是不受欢迎的。
public class MyAwesomeClass
{
protected readonly IMessagingService messenger;
public MyAwesomeClass(IMessagingService messenger)
{
this.messenger = messenger;
}
然后,在你的方法中:
messenger.PushMessage("My awesome message");
此接口的实现可能会根据它是否在Web应用程序或Windows服务中注入而有所不同。您的Web应用程序可能会有一个只使用自己的代码来推送消息的实现,而Windows服务可能需要一个利用HttpClient
向您的Web应用程序发出请求的实现。设置您的DI容器,为正确的应用程序注入正确的实现,并且您已完成。