Joh*_*ohn 5 c# architecture url dynamic expando
背景
我们的网站上有一些链接,其格式如下:
http://oursite.com/books/c_sharp_in_depth_12345.
为了解决这个问题,我们使用了一个简单的属性Url:
public class Book
{
public string Url { get; set;}
}
Run Code Online (Sandbox Code Playgroud)
基本上,URL来自我们网站的域名,网站的一部分(即书籍),页面名称和标识资源的唯一ID.完整的URL存储在数据库中.
我们不喜欢我们的数据库存储节名称这一事实.这是Web图层属性的属性,而不是本书的属性.数据库不应该依赖于Web层.
因此,我们从URL中删除该部分并获取以下内容:
public class Book
{
public string UrlWithoutSection { get; set;}
}
Run Code Online (Sandbox Code Playgroud)
好的,适用于此URL.但是后来我们公司的SEO沙皇说我们的网址是错误的,如果我们重新编写这样的网址,谷歌,我们唯一的真爱,只会爱我们:
http://oursite.com/programming-books/c-sharp-in-depth-12345
呃,我以为我们已经删除了对Web层的数据库依赖,但我们没有.事实证明,我们已经删除了对该部分的依赖性,但URL的格式仍然存在于数据库中.我们通过将URL抽象为对象来解决此问题:
public class OurUrl
{
public string title { get; set; }
public string id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
很酷,现在对web层的依赖性消失了.哦,这次我们的首席执行官来找我们.我们刚刚买了一家新公司,现在我们正在销售杂志.呃,好吗?杂志网址将如下所示:
http://oursite.com/magazines/computers/stack-overflow-the-magazine/2012/01/01/12345
好的,没问题,只需创建另一个对象.
public class OurMagazineUrl : OurUrl
{
public DateTime PublishedDate { get; set; }
// Magazine enum will have to be created.
public MagazineType Type { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
它有效,除了我开始意识到我们有一个大型网站的计划.很多网址.许多不同格式的URL.每次创建一个新课程似乎是一个令人头痛的问题.
坚果的问题
如何处理URL以便Web层与业务层和数据层正确分离?我想出了几个关于解决方案的想法:
关于这个问题的更多信息
我希望这有助于澄清一些混乱.
我们正在使用ASP.Net MVC.我们使用路线.我们使用帮手.我们将扁平化的DTO传递给我们的Web层,而不是业务对象.这个问题涉及服务层和DTO的爆炸性增长.
这主要是一个高流量的新闻网站,而不是一个商业网站.它可以有许多不同的网址,网址可以随时更改.它们可能是复杂的,并且由管理层任意决定.
URL示例(不是真实的,仅用于示例目的).
1. http://oursite.com/news/wi/politics/supreme-court/recent-ruling-someid
2. http://oursite.com/news/wi/politics/election-2012/candidate-x-takes-stand-on-issue-y-someid
3. http://oursite/com/news/politics/mayor-says-things-are-a-ok-someid
4. http://oursite.com/news/milwaukee/local-bar-named-to-HOF-someid
5. http://oursite.com/news/wi/politics/supreme-court-someid
6. http://oursite.com/news/whatever-cat-our-CEO-wants/subcat1/subcat2/etc/2011/10/31/some-story-someid
Run Code Online (Sandbox Code Playgroud)
以上所有都是"文章",我们有一个文章类.文章有许多导航属性,例如AuthorObject,RelatedLinksCollection等.业务对象太重而无法传递给客户端,因此我们传递了展平信息的DTO(例如AuthorName).然而,上述链接可能需要不同的信息,即使它们都是"文章".
在静态编程语言中,例如c#,处理这种情况的正常方法是创建单独的DTO类.你可以添加继承来减少一些代码,但你最终仍然会有多个"文章"dto类.
public class IArticleDto {
public string title { get; set; }
public string body { get; set; }
public string Category { get; set; }}
public class StateArticleDto: IArticleDto {
public string title { get; set; }
public string body { get; set; }
public string Category { get; set; }}
public string StateCode { get; set; }
public string Subcategory { get; set; }
}
public class SupremeCourtArticleDto: IArticleDto {
public string title { get; set; }
public string body { get; set; }
public string Category { get; set; }}
public string Subcategory { get; set; }
}
public class ArbitraryCeoArticleDto: IArticleDto {
//who knows
}
Run Code Online (Sandbox Code Playgroud)
等等
以任何可能的方式编写自定义URL的能力,不可协商.如果文章涉及某些内容(州,类别等),它可以成为网址的一部分.
解决方案?
Url根据需要继续添加对象.多少?至少有十几个,但命名它们会很麻烦.每个业务对象执行一个操作可以解决名称问题,但这意味着需要数十个或数百个新对象.呸.
IOC - 通过配置将路由模式传递到数据访问层.然后,数据访问层可以创建完整的URL.网址格式名称仍然是个问题.
使用Dictionary<TKey, TValue>,KeyValuePair<TKey, TValue>等拉英寸
使用Expando或DynamicObject为网址详细信息.所以url将包含一些基本属性(name和id),但必要时可以添加其他属性.
我正在考虑使用4),因为它看起来像动态编程比静态语言更好.然而,它可能只是我最关注它,因为我喜欢玩新玩具(我之前没有使用过expando).
由于物体爆炸,它比1)更好.我不确定2)是否适用于复杂场景.您可以使用DI将简单的路由名称+路由信息传递到数据层,但似乎很难完成而无需额外的收益.它可能不适用于复杂的路由 - 为此,我认为你需要在UI端有一个规则引擎.
与3)相比,我认为4)略胜一筹.如果我错了,有人会纠正我,但动态类型似乎只不过是字典上的合成糖,而是具有更清晰代码的优势.只是一个想法.
您正确地认识到需要将您的域实体(书籍、杂志等)与其 URL 的任何感知脱钩。
您访问一本书(例如)所需的唯一标识符是它Id- 在您的示例中为12345。任何其他分类元素都应该在表示逻辑中处理,因为您可能需要为非 WWW 通道使用不同的 URI 结构吗?如果您推出多语言网站呢?将 /magazines/computers/ 保留在数据库中将是一个障碍。
随着时间的推移,您的 SEO 沙皇的要求可能会发生变化,因为在搜索引擎中排名更高的技术也会发生变化。
因此,这是一个URL 路由问题。
在 ASP.NET Webforms 解决方案中,您可以将以下条目添加到 Global.asax 文件中:
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(System.Web.Routing.RouteTable.Routes);
}
public static void RegisterRoutes(System.Web.Routing.RouteCollection routes)
{
routes.MapPageRoute("Book",
"{productType}/{categoryName}/{productName}/{productId}",
"~/Books.aspx");
routes.MapPageRoute("Magazine",
"{productType}/{categoryName}/{productName}/{year}/{month}/{day}/{productId}",
"~/Magazines.aspx");
}
Run Code Online (Sandbox Code Playgroud)
然后 Books.aspx 和 Magazines.aspx 将收集 URL 的相关部分,其中:
var categoryName = Page.RouteData.Values["categoryName"];
Run Code Online (Sandbox Code Playgroud)
当您收集了足够的信息来唯一标识您想要显示的产品时,您可以查询您的域/数据层以获取您需要的信息。
当新的产品类型可用时(或者您的利益相关者请求新的 URL 结构),您只需添加另一个路由即可。