ASP.NET MVC 和 IoC - 链接注入
请温柔点,我是 IoC/MVC 方面的新手,但我正在尝试。我了解 DI 对于测试目的的价值以及 IoC 如何在运行时解决依赖关系,并且已经完成了几个对标准 CRUD 操作有意义的示例...
我正在开始一个新项目,但无法想出一个干净的方法来完成用户权限。我的网站主要通过登录后具有功能(注册、常见问题解答、关于我们等除外)的任何页面来保护。我有一个自定义身份,它有几个控制数据访问的额外属性...所以...
使用 Ninject,我将一个具体类型* 绑定到一个方法 (Bind
这样,当我将 MyIdentity
添加到构造函数时,它就会根据方法调用的结果进行注入
。它适合(从 GetIdentity()
方法)直接查询请求 cookies 对象(通过 FormsAuthentication)吗?在测试控制器时,我可以传入一个身份,但是 GetIdentity()< /code> 方法基本上是不可测试的...
另外,在 GetIdentity() 方法中,我应该手动创建存储库的具体实例吗?
还是有更好的方法?
Please be gentle, I'm a newb to this IoC/MVC thing but I am trying. I understand the value of DI for testing purposes and how IoC resolves dependencies at run-time and have been through several examples that make sense for your standard CRUD operations...
I'm starting a new project and cannot come up with a clean way to accomplish user permissions. My website is mostly secured with any pages with functionality (except signup, FAQ, about us, etc) behind a login. I have a custom identity that has several extra properties which control access to data... So....
Using Ninject, I've bound a concrete type* to a method (Bind<MyIdentity>().ToMethod(c => MyIdentity.GetIdentity());
so that when I add MyIdentity
to a constructor, it is injected based on the results of the method call.
That all works well. Is it appropriate to (from the GetIdentity()
method) directly query the request cookies object (via FormsAuthentication)? In testing the controllers, I can pass in an identity, but the GetIdentity()
method will be essentially untestable...
Also, in the GetIdentity() method, I will query the database. Should I manually create a concrete instance of a repository?
Or is there a better way all together?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为您走在正确的轨道上,因为您从单元测试中抽象出了数据库通信和 ASP.NET 依赖项。不要担心您无法在测试中测试所有内容。您的应用程序中总会有一些不可测试的代码行。
GetIdentity
就是一个很好的例子。在应用程序的某个地方,您需要与特定于框架的 API 进行通信,并且单元测试无法覆盖此代码。但可能仍有改进的空间。虽然未经测试的
GetIdentity
不是问题,但事实上它实际上可以由应用程序调用。它只是挂在那里,等待有人不小心调用它。那么为什么不抽象身份的创建呢?例如,创建一个抽象工厂,它知道如何为当前上下文获取正确的标识。您可以注入这个工厂,而不是注入身份本身。这允许您在应用程序的组合根附近和应用程序其余部分的范围之外定义一个实现。除此之外,代码更清楚地传达了正在发生的事情。没有人会问“我实际上得到了哪个身份?”,因为通过他们调用的工厂方法就可以清楚地知道这一点。这是一个例子:
在你的组合根中,你可以实现这个:
作为一个技巧,我有时让我的测试对象实现工厂和产品,只是为了在单元测试期间方便。例如:通过
这种方式,您可以在需要 IIdentityProvider 的构造函数中注入 FakeMyIdentity。我发现这并不会牺牲测试的可读性(这很重要)。
当然,您希望 AspNetIdentityProvider 中的代码尽可能少,因为您无法(自动)测试它。还要确保您的
MyIdentity
类不依赖于任何框架特定部分。如果是这样,您也需要对其进行抽象。我希望这是有道理的。
I think you are reasonably on the right track, since you abstracted away database communication and ASP.NET dependencies from your unit tests. Don't worry that you can't test everything in your tests. There will always be lines of code in your application that are untestable. The
GetIdentity
is a good example. Somewhere in your application you need to communicate with framework specific API and this code can not be covered by your unit tests.There might still be room for improvement though. While an untested
GetIdentity
isn't a problem, the fact that it is actually callable by the application. It just hangs there, waiting for someone to accidentally call it. So why not abstract the creation of identities. For instance, create an abstract factory that knows how to get the right identity for the current context. You can inject this factory, instead of injecting the identity itself. This allows you to have an implementation defined near the application's composition root and outside reach of the rest of the application. Besides that, the code communicates more clearly what is happening. Nobody has to ask "which identity do I actually get?", because it will be clear by the method on the factory they call.Here's an example:
In your composition root you can have an implementation of this:
As a trick I sometimes have my test objects implement both the factory and product, just for convenience during unit tesing. For instance:
This way you can just inject a FakeMyIdentity in a constructor that expects an IIdentityProvider. I found out that this doesn’t sacrifice readability of the tests (which is important).
Of course you want to have as little code as possible in the AspNetIdentityProvider, because you can't test it (automatically). Also make sure that your
MyIdentity
class doesn't have any dependency on any framework specific parts. If so you need to abstract that as well.I hope this makes sense.
这里有两件事我会做一些不同的事情...
我会使用一个自定义 IPrincipal 对象,其中包含您的身份验证需求所需的所有属性。然后,我将其与自定义 cookie 创建和 AuthenticateRequest 事件结合使用,以避免每个请求都调用数据库。
如果另一个类中需要我的 IPrincipal / Identity,我会将其作为方法参数传递,而不是将其作为对其自身类的依赖项。
当沿着这条路线走时,我使用自定义模型绑定器,因此它们是我的操作的参数,而不是神奇地出现在我的操作方法中。
注意:这只是我一直以来做事的方式,因此请持保留态度。
抱歉,这可能引发的问题多于答案。欢迎就我的方法提出更多问题。
There are two things I'd kinda do differently here...
I'd use a custom IPrincipal object with all the properties required for your authentication needs. Then I'd use that in conjunction with custom cookie creation and the AuthenticateRequest event to avoid database calls on every request.
If my IPrincipal / Identity was required inside another class, I'd pass it as a method parameter rather than have it as a dependency on the class it's self.
When going down this route I use custom model binders so they are then parameters to my actions rather than magically appearing inside my action methods.
NOTE: This is just the way I've been doing things, so take with a grain of salt.
Sorry, this probably throws up more questions than answers. Feel free to ask more questions about my approach.