惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
D
Docker
Blog — PlanetScale
Blog — PlanetScale
罗磊的独立博客
美团技术团队
V
V2EX
Last Week in AI
Last Week in AI
D
DataBreaches.Net
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Microsoft Security Blog
Microsoft Security Blog
Microsoft Azure Blog
Microsoft Azure Blog
人人都是产品经理
人人都是产品经理
M
MIT News - Artificial intelligence
P
Proofpoint News Feed
B
Blog RSS Feed
博客园_首页
B
Blog
博客园 - 叶小钗
I
InfoQ
WordPress大学
WordPress大学
L
LangChain Blog
Apple Machine Learning Research
Apple Machine Learning Research
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
The Register - Security
The Register - Security
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
博客园 - 司徒正美
Latest news
Latest news
W
WeLiveSecurity
T
The Exploit Database - CXSecurity.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
Cyberwarzone
Cyberwarzone
Scott Helme
Scott Helme
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
C
CXSECURITY Database RSS Feed - CXSecurity.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
N
News and Events Feed by Topic
S
Secure Thoughts
The Hacker News
The Hacker News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google DeepMind News
Google DeepMind News

博客园 - sinlight23

Web开发框架趋势 ASP.NET拾遗 - Health Monitoring .NET Xml序列化时会忽略值为"默认值"的Property或Field ASP.NET MVC 实现模式 - ModelBuilder Enumerable.Range和自定义的IntRange/DateRange HOWTO: Web Deploy时服务器报登录失败的解决方法 HOWTO: 为GitHub for Windows指定代理服务器 ASP.NET MVC - 在MVC 3项目中使用ASP.NET Bundling and Minification机制 脚印:关于错误编码的管理的一些思考 脚印:软件开发随想录 脚印:关于扩展方法的使用 脚印:一次重构讨论 脚印:记录一次重构,将规则生产和规则消费(执行委托)分离 HOWTO: IE8下处理iframe自适应高度 Microsoft ASP.NET 2.0 AJAX 相关信息备忘 MVC 模式在javascript中的应用 VS2010 "SQL Server 2005 Database Project" 使用笔记(二) VS2010 "SQL Server 2005 Database Project" 使用笔记 腳印: 初學者的心態
ASP.NET MVC - PageData的应用
sinlight23 · 2013-07-02 · via 博客园 - sinlight23

一、要实现一个功能,在不同的页面放置一段如下的内容,用于采集用户行为信息:

<input type='hidden' id='page_id' value='xxxx' />
<script type="text/javascript">
    //balabala...
</script>

[1] 需求中还藏着一点,有些页面加,有些页面不加。

二、方案

方案一:当然可以这样做:找到需要采集的页面,一个个打开将采集代码拷贝进去,然后把xxxxx修改为分配给各页面的值。但如此显然违背DRY原则。

方案二:不希望采集代码处处被粘贴的话,那么从View的角度,备选的机制有partial viewlayout pages。如果使用前者,仍然需要被采集页面,重复的引用包含采集代码的partial view。如果使用后者,假使网站已经使用了多个layout pages(这很常见),依然会需要重复的粘帖采集代码。

1. 那么我们将两者结合起来,并且做一点折衷,用paritial view包含采集代码,然后在各layout pages中引用partial view:

@Html.Partial("_TraceByPageIdScript")

让采集代码就可以安然存在于_TraceByPageIdScript这个partial view中。

2. 作为page_id值的xxxx怎么办?

开始的想法是,在被采集的页面中,对ViewBag.PageId赋值(set),保证在该变量被使用前有值(不为null)。然后以为在_TraceByPageIdScript中就可以使用ViewBag.Page了(get)。

实验可耻的失败鸟。

发现pages的ViewBag和layout pages的ViewBag是不同的对象,从属于不同的WebViewPage实例。
类似的,包括MVC3之前的ViewData,都不是用在"pages需要和layout pages共享数据"这种场景的。
[it] Gets or sets a dictionary that contains data to pass between the controller and the view.
http://msdn.microsoft.com/en-us/library/system.web.mvc.viewpage.viewdata.aspx

google到了PageData
[it] Provides array-like access to page data that is shared between pages, layout pages, and partial pages.
http://msdn.microsoft.com/en-us/library/system.web.webpages.webpagebase.pagedata(v=VS.99).aspx

所以采集代码中的xxxx,可以替换为PageData["xxxx"]了

3. 这个时候发现犯了一个错误,断点看了下,PageData["xxxx"]在partial pages中的值是null。(这个和俺对MSDN注释的理解不大一致啊)
换个方式吧:

@Html.Partial("_TraceByPageIdScript", (int?)PageData["PageId"], new ViewDataDictionary())

注意:如果这里省掉了第三个参数,那么当null被传递到partial view时,会导致实际传入的对象类型是@model指明的类型,进而导致异常。这是ASP.NET MVC 3的一个古怪的地方,尚不清楚后续版本是否有调整。
如下两篇资料给自给了一种解法:

  a. http://stackoverflow.com/questions/650393/renderpartial-with-null-model-gets-passed-the-wrong-type
    @Html.Partial("_TraceByPageIdScript", new ViewDataDictionary(PageData["PageId"]))

  b. http://stackoverflow.com/questions/9292852/how-do-i-invoke-a-partial-view-with-null-for-its-model
    @Html.Partial("_TraceByPageIdScript", (int?)PageData["PageId"], new ViewDataDictionary())

4. 还有前面的需求[1]被丢下了,在_TraceByPageIdScript.cshtml中,对采集代码包一下:

@model int?
@if(Model.HasValue)
{
    <input type='hidden' id='page_id' value='@Model.Value' />
    <script type="text/javascript">
        //balabala...
    </script>
}

三、结语

小经周折,至此完整的实现了该需求。

除了ASP.NET MVC相关的知识外,“值即开关”模式的使用,实现了随需加载采集代码的效果,这样今后如果其它页面需要加入采集,只需要在相应页面,通过赋值即可;不再需要采集的页面,赋值为null或者注视掉赋值语句即可。

更理想的方案,其实是被采集的页面,完全不用为了被采集而进行任何修改。

做法之一,以配置的形式维护一个字典,value是page_id,key则要求可以唯一标识某个特定页面。如果有了这个字典,程序可以在运行时根据请求,来找到key,进而就能找到page_id。以此作为基础,就可以利用一个能共享给layout pages的变量,来告诉layout pages是否打开"输出采集代码"的开关。

这里有两个比较复杂的点:

  1. 以何载体来作为这里说的共享变量?这也牵扯到根据请求找到page_id的时机。时机方面可以考虑Action Filter机制,这样自然就能利用上Controller和Pages间的桥梁:ViewBag/ViewDatalayout pages拿到它,心里一定很乐呵。
http://www.manasinc.com/setting-a-viewbag-property-in-the-onresultexecuting-action-filter-in-asp-net-mvc/

  2. 关键的难点在于,字典的key的选取。如果使用url,则当route发生变化时,相应页面的采集就会失效。如果使用route,还要考虑参数取值的变化。

考虑到这个实现方案的复杂性,开发和维护都需要付出更多的精力,就没有进一步的探索下去了。


P.S. PageData["XXX"] 等效于 Page.XXX