常见 Web URI 路径的方法放在哪里
我们有一个非常常见的架构:
数据库
存储库层
业务对象
服务层 - 为客户端
Web 层 (MVC)
提供 DTO我们有许多通用的资源路径,特别是图像和播客(例如 http://media.mysite.com/podcasts/)。我想创建一个具有以下属性的静态实用程序类:
MySite.Utils.ImagePathUri
MySite.Utils.PodcastsPathUri
等
我的问题是:您将 uri 路径放在哪里?这个实用程序类进入哪个项目?
最初,它似乎是理所当然的:Web 层。这是一个网站。我应该能够在其他层不知道的情况下更改站点的 URL。
一切都很好,但是,。 。 。然后有一天,我的一项服务需要提供 SyndicateFeed 类型。 SyndicateFeed 需要完整的 URI,而不仅仅是部分文件名。但服务不应该访问完整路径。或者他们应该?
我与自己争论了几件事,但无法得出坚定的立场:
- 将路径移至服务层。这将 Web 层与服务层紧密耦合,但也许没关系,因为它们一开始就非常紧密耦合。
- 将路径移动到业务对象或存储库。我不喜欢这样,但如果我愿意将其放入服务层,我至少必须考虑它。
- 不要在服务层内部使用 SyndicateFeed,而仅在 Web 层中使用它。解决了这个问题,但 SyndicateFeed 似乎应该属于服务层。
- 放弃 SyndicateFeed。任何 SyndicateFeed 都可以通过 PartialView 在 MVC 中更轻松地创建,该 PartialView 可以生成适当的 XML,而不必弄乱 ElementExtensions 等臃肿的抽象。我喜欢这个,但我们在很多地方都使用 SyndicateFeed,因此需要最详细的解释。
- 向服务层中的聚合提要提供一个假 uri,然后在 Web 层中更改它。你能说黑客吗?
- 将完整路径放入数据库中。起初听起来不错,但后来我意识到,一旦你有了动态生成的图像,它就会崩溃。
- 我没有想到其他一些解决方案。
你有什么想法?将 Web 资源(图像、播客等)的实用程序类放在哪里?如果您说“Web 层”,您对 SyndicatedFeed 问题有何看法?
更新
最终,我决定废弃 SyndicateFeed 类,这样就无需在服务层和存储库层中包含文件路径。然而,问题仍然出现在其他地方,使用 DI,特别是像 Ninject 这样的 IoC 是非常有意义的。将它们封装到一个通用接口中也是如此。
SyndicateFeed、视图引擎以及为什么 Declarative 比 Declarative 更好
我放弃了 SyndicateFeed,而是使用 Razor 创建了我需要的 XML。它不仅更容易创建,而且可读性提高 1000%。我的观点是,使用命令式代码(C#、VB 等)来创建 XML 比应有的要困难。 XML 是声明性的,而不是命令性的。
相反,我现在认为视图引擎(例如 Razor)的声明性语法比命令式语言更容易使用。
We have a pretty common architecture:
Database
Repository Layer
Business Objects
Services Layer - serves DTOs to the client
Web Layer (MVC)
We've got a number of common paths to resources, in particular images and podcasts (Ex. http://media.mysite.com/podcasts/). I want to create a static utility class with properties:
MySite.Utils.ImagePathUri
MySite.Utils.PodcastsPathUri
etc
My question is: Where do you put uri paths? Into which project does this utility class go?
Initially, it seemed a no-brainer: the web layer. It's a web site. I should be able to change the urls of a site without the other layers knowing about it.
Everything was fine, but, . . . then one day one of my services needed to provide a SyndicationFeed type. The SyndicationFeed needs a full URI, not just a partial file name. But the services shouldn't have access to full paths. Or should they?
I've debated with myself several things, but can't come up with a firm position:
- Move the paths to the services layer. That tightly couples the web layer to the services layer, but maybe that's ok, since they're pretty tightly coupled to begin with.
- Move the paths to the business objects or repos. I don't like this, but if I'm open to putting it into the services layer, I have to at least consider it.
- Don't use SyndicationFeed inside of services layer, but use it only in the web layer. Solves the issue, but it seems like SyndicationFeed should belong in a services layer.
- Discard SyndicationFeed. Any SyndicationFeed can more easily be created in MVC with a PartialView that generates the appropriate XML without having to mess with the bloated abstractions like ElementExtensions. I like this one, but we're using SyndicationFeed in a number of places, so that one will take the most explaining to do.
- Provide a fake uri to the syndication feed in the services layer and then change it in the web layer. Can you say hack?
- Put the full path in the database. That sounds ok at first, but then I realize that it breaks as soon as you have a dynamically generated image.
- Some other solution that doesn't occur to me.
What are your thoughts? Where do you put utility classes for web resources (images, podcasts, etc)? And if you say "web layer", what's your take on the SyndicationFeed issue?
UPDATE
At the end of the day, I decided to scrap the SyndicationFeed class, which negated the need for including the path to the file in the service and repository layers. However, the problem still comes up elsewhere and using DI and, in particular, an IoC like Ninject makes perfect sense. So does wrapping together these into a common interface.
SyndicationFeed, View engines and why Declarative does Declarative better
I ditched the SyndicationFeed and instead created the XML that I needed using Razor. Not only is it much easier to create, but it's 1000% more readable. I've come around to the opinion that using imperative code (C#, VB, etc.) to create XML is just harder than it ought to be. XML is declarative, not imperative.
Instead, I've now decided that declarative syntax of View Engines (e.g. Razor) is much easier to work with than imperative languages.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我感受到你的痛苦。我也遇到过类似的情况,我通过将 uri 从 Web 层传递到我的存储库层来解决。
我的项目使用 Ninject 进行绑定,并且由于我已经使用 Ninject 将连接字符串传递到存储库,因此传递路径字符串也很简单。
然后,我的存储库会处理路径字符串并在我的业务对象中填充必要的属性。
这合适吗?我不知道,但目前有效。我对这个解决方案并不完全满意,但我还没有机会尝试改进。
很想听听其他人是如何处理这个问题的。
I feel your pain. I too had a similar situation that I resolved by passing the uri to my repository layer, from my web layer.
My project uses Ninject for binding, and since I was already passing the connection string to the repository with Ninject, it was a simple matter to pass my path string as well.
My repository then massages the path string and populates the necessary properties in my business object.
Is this proper? I do not know, but it works for now. I am not completely happy with the solution, but I have not yet had a chance attempt an improvemnt.
Would love to hear how others have dealt with this.
面对类似的情况,我觉得最好的解决方法是定义一个配置接口,在最顶层生成一个对象。中间的每一层都会使用更具体的属性和操作来完善接口:
服务层将添加其自身所需的内容:
最后,我让 Web 层实现连接所有接口的特定对象。丑陋的?也许。我承认那里有对isa的电话和一些选角。
然而,我能够向每一层传递一个强类型接口。在我看来,这比通过一系列调用从配置文件中获取普通的旧字符串要好。另外,因为每个属性都特定于一个层,并且聚合对象正在传递,所以我只需加载配置一次。
Facing a similar situation I felt the best way to approach it was to define a configuration interface that resulted in an object at the top most layer. Each layer in between would refine the interface with more specific properties and operations:
the service would layer would add the things needed by itself:
In the end I had the web tier implement the specific object that wired up all of the interfaces. Ugly? Perhaps. I will admit there was calls to isa in there and some casting.
However, I was able to then pass to each layer a strongly typed interface. In my opinion it was better than having a series of calls to get plain old string from a config file. Also, because each property was specific to a layer and the aggregate object was being passed around I only had to load the configuration once.