在您的体系结构中,如何将URL与数据库层和业务对象层分离

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).然而,上述链接可能需要不同的信息,即使它们都是"文章".

  1. 需要类别,子类别,标题和ID
  2. 需要类别,子类别,政治类别,标题和标识
  3. 需要类别,标题和ID
  4. 需要类别,标题和ID
  5. 需要类别,子类别,标题和ID
  6. 需要CeoCategory,CeoSubcategory,PublishedDate,Title和Id

在静态编程语言中,例如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的能力,不可协商.如果文章涉及某些内容(州,类别等),它可以成为网址的一部分.

解决方案?

  1. Url根据需要继续添加对象.多少?至少有十几个,但命名它们会很麻烦.每个业务对象执行一个操作可以解决名称问题,但这意味着需要数十个或数百个新对象.呸.

  2. IOC - 通过配置将路由模式传递到数据访问层.然后,数据访问层可以创建完整的URL.网址格式名称仍然是个问题.

  3. 使用Dictionary<TKey, TValue>,KeyValuePair<TKey, TValue>等拉英寸

  4. 使用ExpandoDynamicObject为网址详细信息.所以url将包含一些基本属性(nameid),但必要时可以添加其他属性.

我正在考虑使用4),因为它看起来像动态编程比静态语言更好.然而,它可能只是我最关注它,因为我喜欢玩新玩具(我之前没有使用过expando).

由于物体爆炸,它比1)更好.我不确定2)是否适用于复杂场景.您可以使用DI将简单的路由名称+路由信息传递到数据层,但似乎很难完成而无需额外的收益.它可能不适用于复杂的路由 - 为此,我认为你需要在UI端有一个规则引擎.

与3)相比,我认为4)略胜一筹.如果我错了,有人会纠正我,但动态类型似乎只不过是字典上的合成糖,而是具有更清晰代码的优势.只是一个想法.

chr*_*ofr 0

您正确地认识到需要将您的域实体(书籍、杂志等)与其 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 结构),您只需添加另一个路由即可。