[MVC]适合ASP.NET MVC的视图片断缓存方式(中):更实用的API

mikel阅读(749)

转载:http://www.cnblogs.com/JeffreyZhao/archive/2009/09/21/aspnet-mvc-fragment-cache-2-more-friendly-api.html

  上一篇文章中我 们提出了了片断缓存的基本方式,也就是构建HtmlHelper的扩展方法Cache,接受一个用于生成字符串的委托对象。在缓存命中时,则直接返回缓存 中的字符串片断,否则则使用委托生成的内容。因此,缓存命中时委托的开销便节省了下来。不过这个方法并不实用,如果您要缓存大片的HTML,还需要准备一 个Partial View,再用它来生成网页片段:

<%= Html.Cache(..., () => Html.Partial("MyPartialViewToCache")) %>

  但是在实际开发过程中,我们最乐于看到的使用方法,应该只是使用某个标记来“围绕”一段现有的代码。也就是说,我们希望的API使用方式可能是这样的:

<% Html.Cache("cache_key", DateTime.Now.AddSeconds(10), () => { %>
    <% foreach (var article in Model.Articles) { %>
<p><%= article.Body %></p>
<% } %>

<% }); %>

  我们可以从这种“表现形式”上推断出这个Cache方法的签名:

public static void Cache(
this HtmlHelper htmlHelper,
string cacheKey,
CacheDependency cacheDependencies,
DateTime absoluteExpiration,
TimeSpan slidingExpiration,
Action action)
{
...
}

  与前一个扩展相比,最后一个委托参数变成了Action,而不是Func<string>。这是因为ASP.NET页面在编译时,会将页面Cache块中的代码,编译为内容的输出方式——这点在之前的文章中已经有过比较详细的描述。不过有一点还是与之前相同的,我们要省下的是action委托的开销。也就是说,如果缓存命中,则不执行action。缓存没有命中,则执行action,获得action生成的字符串,加入缓存并输出。

  看似比较简单,但这里有个问题:如之前的Func<string>参数,我们执行后自然可以获得一个字符串作为结果。但是现在是个action,执行后它又把内容输出到什么地方去,我们又该如何得到这里生成的字符串呢?根据页面输出行为,我们可以推断出页面上的内容是被写入一个HtmlTextWriter中的。那么,这个HtmlTextWriter又是如何生成的呢?

  它是根据Page类型的CreateHtmlTextWriter方法生成的:

protected virtual HtmlTextWriter CreateHtmlTextWriter(System.IO.TextWriter tw) { ... }

  在页面准备生成内容之前,Page会调用其CreateHtmlTextWriter来包装一个TextWriter,这个 TextWriter一般即是由Response.Output暴露出来的HttpWriter对象。CreateHtmlTextWriter方法生成 的HtmlTextWriter,便会交给Page的Render方法用于输出页面内容了。这便是我们的入手点,我们可以趁此机会在 HtmlTextWriter和CreateHtmlTextWriter之间“插入”一个组件。这个组件除了将外部传入的数据传入内部的 TextWriter以外,还有着“纪录”内容的功能:

internal class RecordWriter : TextWriter
{
public RecordWriter(TextWriter innerWriter)
{
this.m_innerWriter = innerWriter;
}
private TextWriter m_innerWriter;
private List<StringBuilder> m_recorders = new List<StringBuilder>();
public override Encoding Encoding
{
get { return this.m_innerWriter.Encoding; }
}
public override void Write(char value) { ... }
public override void Write(string value)
{
if (value != null)
{
this.m_innerWriter.Write(value);
if (this.m_recorders.Count > 0)
{
foreach (var recorder in this.m_recorders)
{
recorder.Append(value);
}
}
}
}
public override void Write(char[] buffer, int index, int count) { ... }
public void AddRecorder(StringBuilder recorder)
{
this.m_recorders.Add(recorder);
}
public void RemoveRecorder(StringBuilder recorder)
{
this.m_recorders.Remove(recorder);
}
}

  一个TextWriter有数十个可以覆盖的成员,但是一般情况下我们只需覆盖其中三个Write方法就 可以了。以上代码用Write(string)作为示例,可以看出,如果RecordWriter中添加了Recorder之后,便会将外界写入的内容再 交给Recorder一次。换句话说,如果我们希望纪录页面上写入Writer的内容,只要在RecordWriter里添加Recorder就可以了。 当然,在此之前我们需要为视图页面“开启”缓存功能:

// 定义在CacheExtensions中
public static TextWriter CreateCacheWriter(this HtmlHelper htmlHelper, TextWriter writer)
{
var recordWriter = new RecordWriter(writer);
htmlHelper.SetRecordWriter(recordWriter);
return recordWriter;
}
// 定义在视图页面(aspx)中
<script runat="server">
protected override HtmlTextWriter CreateHtmlTextWriter(System.IO.TextWriter tw)
{
return base.CreateHtmlTextWriter(Html.CreateCacheWriter(tw));
}
</script>

  当然,在实际开发过程中不会再aspx中重写CreateHtmlTextWriter方法,我们往往会将其放在视图页面的共同基类中。例如在 我的项目中,我就为所有的视图“开启”了这种纪录功能。由于在没有缓存的情况下这层薄薄的封装只是在做一个“转发”功能,因此不会带来性能问题。

  此时,新的Cache方法便非常直观了:

public static void Cache(
this HtmlHelper htmlHelper,
string cacheKey,
CacheDependency cacheDependencies,
DateTime absoluteExpiration,
TimeSpan slidingExpiration,
Action action)
{
var cache = htmlHelper.ViewContext.HttpContext.Cache;
var content = cache.Get(cacheKey) as string;
var writer = htmlHelper.GetRecordWriter();
if (content == null)
{
var recorder = new StringBuilder();
writer.AddRecorder(recorder);
action();
writer.RemoveRecorder(recorder);
content = recorder.ToString();
cache.Insert(cacheKey, content, cacheDependencies, absoluteExpiration, slidingExpiration);
}
else
{
htmlHelper.Output.Write(content);
}
}

  如果缓存没有命中,则我们会向RecordWriter中添加一个Recorder,然后再执行action委托,这样action中的所有内容便会被纪录下来。action执行完毕后,我们再摘除Recorder即可。现在Cache方法已经可用了,例如:

<%= DateTime.Now %>
<br />
<% Html.Cache("now", DateTime.Now.AddSeconds(5), () => { %>
    <%= DateTime.Now %>

<% }); %>

  那么,Html.Cache能否嵌套呢?答案也是肯定的。

<%= DateTime.Now %>
<br />
<% Html.Cache("now", DateTime.Now.AddSeconds(5), () => { %>
    <%= DateTime.Now %>
    <br />
<% Html.Cache("inner_now", DateTime.Now.AddSeconds(10), () => { %>

<% Html.RenderPartial("CurrentTime"); %>

<% }); %>

<% }); %>

  外层缓存块5秒后过期,内存缓存块10秒钟过期,因此在某一时刻(如第一次刷新后7秒后),您会发现页面上会出现这样的结果:

2009/9/21 15:36:10
2009/9/21 15:36:08
2009/9/21 15:36:03

  我们的RecordWriter支持同时拥有多个recorder,您可以根据上面得出的结果来理解内外层循环是以何种顺序向RecordWriter添加Recorder的,这并不困难。

  从代码中我们也可以发现,Cache块内部也可以直接使用Html.RenderPartial。您也可以在Cache块内部使用各种辅助方法,它们的结果会被一并缓存下来。

  不过它们还是有“前提”的,至于这个前提是什么,我们下次在讨论吧。如果您想先睹为快,可以关注MvcPatch项目。

[Memcahed].NET平台上的Memcached客户端介绍

mikel阅读(810)

早上接到一个任务,需要对Linux服务器的Memcached的update操作进行性能测试,我发现我是一个典型的“手里拿着锤子,就把所有问题都当成钉子”的人。我第一个念头就是,上Memcached的官网找.NET的客户端。最后在Codeplex上找到了一个叫Memcached Providers的客户端程序,很小,218K,里面就3个DLL,一个是Memcached Providers本身的DLL,还有一个是Enyim.Caching,Enyim.Caching也是一个.NET平台上的Memcached客户端,最后就是著名的log4net。

Memcached Providers的配置很方便,首先就是在.NET项目中引用上述提到的3个DLL文件,然后就需要修改项目的配置文件,如果是桌面程序,就修改APP.CONFIG,如果是WEB程序,就修改WEB.CONFIG。

首先在configSections节点下增加如下配置

1
2
3
4
5
6
<section name="cacheProvider" type="MemcachedProviders.Cache.CacheProviderSection, MemcachedProviders"
        allowDefinition="MachineToApplication" restartOnExternalChanges="true"/>
<sectionGroup name="enyim.com">
<section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" />
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>

接着在configuration节点下增加Enyim的配置节点

1
2
3
4
5
6
7
8
9
<enyim.com>
<memcached>
<servers>
<!-- put your own server(s) here-->
<add address="10.60.0.105" port="19191" />
</servers>
<socketPool minPoolSize="10" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00" />
</memcached>
</enyim.com>

在configuration节点下增加Memcached Providers的配置节点

1
2
3
4
5
6
<cacheProvider defaultProvider="MemcachedCacheProvider">
<providers>
<add name="MemcachedCacheProvider" type="MemcachedProviders.Cache.MemcachedCacheProvider, MemcachedProviders"
                keySuffix="_MySuffix_" defaultExpireTime="2000"/>
</providers>
</cacheProvider>

最后就是在configuration节点下增加Log4net的配置节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/cacheProvider>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}]- %message%newline" />
</layout>
</appender>
<root>
<priority value="WARN"/>
<appender-ref ref="ConsoleAppender">
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="WARN"/>
<levelMax value="FATAL"/>
</filter>
</appender-ref>
</root>
</log4net>

最后做一个简单的测试

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
[TestMethod]
public void TestMethod1()
{
string key = "mykey";
string value = "Success!!!";
DistCache.Add(key, value);                             //存数据
Thread.Sleep(500);
string ret = (string)DistCache.Get(key);            //读数据
Assert.AreEqual(value, ret);                             //验证
}

整个过程下来比较顺利,基本没有遇到问题,这一切准备完毕以后就可以进入测试了。PHP,JAVA,Python等程序语言的Memcached客 户端是比较多的,而.NET平台的客户端却只有2、3个,如果打算在.NET程序中使用Memcached,Memcached Providers也是一个不错的选择。

[C#]Qizmt:MySpace的开源MapReduce框架

mikel阅读(1143)

MapReduce是由Google提出并实现的编程模型,可利用大量机器所组成的集群处理或生成海量数据集。此外,由Yahoo!公司资助的Hadoop项目则是MapReduce的开源实现,在Facebook等大型应用中得到了广泛使用。而现在,MySpace也将其MapReduce框架Qizmt开源了,可用于在大规模Windows集群上开发或运行分布式计算程序。

MySpace Qizmt可用于各种分布式计算场景,例如:

  • 数据挖掘
  • 数据分析
  • 媒体处理
  • 内容索引

Qizmt的主要功能有:

  • 使用C#快速开发MapReduce任务。
  • 简单的“自己动手(Do-It-Yourself)”安装程序
  • 内置集成开发环境/调试器
    • 自动将堆分配标为红色
    • 为MapReduce任务的快速开发提供自动完成功能
    • 在目标集群中对MapReduce任务进行单步调试
  • 可在集群中的任意机器上:
    • 编辑任务
    • 调试任务
    • 执行任务
    • 管理任务
  • 为MapReduce任务提供“差值交换(Delta-only exchange)”选项
  • 可配置的数据冗余或机器级别的故障转移
  • 可轻易为集群增加机器以提高处理能力
  • 集群程序集缓存(Cluster Assembly Cache),用于将.NET dll释放为MapReduce任务
  • 三种任务类型:
    • MapReduce – 用于对大规模数据进行基于集合的处理
    • 远程 – 用于不适合MapReduce模型的问题
    • 本地:- 用于组织一系列MapReduce和远程任务
  • 执行MapReduce过程中的三种数据交换方式
    • 排序 – 在集群中对键/值对进行排序
    • 分组 – 不将键/值对进行排序,而将它们发送到同一个归并器(reducer)中
    • 散列排序 – 排序随机数据的超快方式

MySpace Qizmt项目现已发布于Google Code,支持装有.NET 3.5 SP1的Windows 2003 Server,Windows 2008 Server及Windows Vista操作系统。

[MVC]流畅地HtmlHelper-Asp.Net MVC

mikel阅读(903)

今天抛开 Fluent NHibernate 不谈,我们来谈谈 Asp.Net MVC ,在MVC的View中,我们经常会使用HtmlHelper来生成各种html代码(可能描述不太清楚看代码吧,呵呵)。

HtmlHelper原先的功能不是很多,还好有很多扩展方法,我们能用它来生成一个Input控件,比如:

<%= Html.TextBox("UserID") %>

我们使用上面的代码能生成一个没有值的单行文本框,我们还可以使用:

<%= Html.TextBox("UserID", Model.User.UserID, new { @class="class" })%> 

给这个文本框加值,加样式,与之对应的html代码:

<input type="text" id="UserID" name="UserID" class="class" value="<%=Model.User.UserID %>" />

确实很方便,有了扩展方法,我们能很方便的创建各种控件,包括form,但如果你要生成很多控件呢?随便说个例子,比如你要添加一个产品,可能它会有几十个属性,意味着你要写几十个Html.TextBox(属性值) (这里可以是其他控件),其实也没有什么,不过真的很不爽,可能我习惯了 Fluent NHibernate 的映射方式,我一直想着使用以下的方式来生成控件:

<%= Html.TextBox<User>(u => u.UserID, Model.User.UserID, new { @class="class"}) %>

怎么样,是不是代码看上去很优美,少去了硬编码,怎么看都好看,哈哈,但是HtmlHelper没有这种扩展方法,那我们只能自己来写一个扩展。

这里不得不感谢微软提供了扩展方法这么好的方式,有了它,一切就变的简单了,先前看到有个朋友问是不是项目该升级到Framework3.5,我觉得是非常有必要的,因为有了它,代码看上去是如此的优美。

因为也是刚尝试接触MVC,看了一下它的源代码,HtmlHelper的扩展方法都在 System.Web.Mvc.Html 命名空间内,大概看了下InputExtensions的代码,大致是根据传入的类型,传入的name生成一个input控件,很简单的方法,但提供了我 们很大的便利。不过这里值得注意的是,如果你的TextBox(name)中有"."的话,你的控件id会把"."替换成"_"的。

说干就干,不是很难,其实就是解析一个表达式树,取出它属性的Name就Ok了。介绍Lambda表达式树的文章很多,这里就不作介绍了。

public static string GetMemeberName<T>(this Expression<T> expression)
{
MemberExpression memberExpression = null;
if (expression.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
else
{
throw new ArgumentException("expression Argument is Error");
}
return memberExpression.Member.Name;
}

这里的MemberName就是获取这个表达式树的,有了它我们就能实现原先的设想了:

public static string TextBox<T>(this HtmlHelper helper, Expression<Func<T, object>> expression)
{
    return helper.TextBox<T>(expression, null);
}
public static string TextBox<T>(this HtmlHelper helper, Expression<Func<T, object>> expression, object value)
{
    return helper.TextBox<T>(expression, value, null);
}
public static string TextBox<T>(this HtmlHelper helper, Expression<Func<T, object>> expression, object value, object htmlAttributes)
{
    return helper.TextBox(expression.GetMemeberName(), value, htmlAttributes);
}

ok,我们可以使用以下方式来构造一个TextBox了:

<table>
<tr>
<td colspan="2">User Login</td>
</tr>
<tr>
<td>User Name:</td>
<td><%= Html.TextBox<StudyMvcApplication.Models.User>(u => u.Name) %></td>
</tr>
<tr>
<td>Password</td>
<td><%= Html.TextBox<StudyMvcApplication.Models.User>(u => u.Password) %></td>
</tr>
</table>

感觉看上去舒服多了,哈哈,看看效果图:

image

总结

现在还没有继续测试下去,实在有点忙,或许有人说有点多余,个人喜欢吧,哈哈。不过HtmlHelper还有很多扩展方法,所以还需要写很多代码,俺会一点一点去添加的。

说实在,MVC好像很多地方要使用那种硬编码,实在不爽,所以要改造一个自己适合的环境还真不容易啊。老赵最近弄了个MVCPath,要不就一起放进去?吼吼

本文章演示代码下载

—————————————————–
      网名:James.Ying(玄天尊)

      MSN:x_inday@msn.com

      经验:5年电子商务网站开发

      目标:电子商务平民化。

—————————————————–

[MVC]适合ASP.NET MVC的视图片断缓存方式(上)

mikel阅读(762)

  说到网站性能优化,没有什么比“缓存”更重要了。即便是某些朋友口中念念不忘的“静态页”,说到底也只是缓存了整张页面内容而已。但是,显然这 样大粒度的缓存策略,在如今“牵一发而动全身”的Web 2.0站点中几乎是无法使用的。试想,在Twitter中的某个名人被数十万人订阅,那么他发一条消息,难道此时网站要去修改数十万用户的静态页面?因 此,我们需要粒度更小的缓存。而比“整页缓存”粒度小一号的缓存,便是所谓“视图片断缓存”了。

  视图片断缓存非常重要,因为它缓存的 也是页面内容,这表示它比更低级别的缓存更有效率,也比静态页等整页内容缓存的适用面要大得多。在ASP.NET WebForm模型中提供了控件级别的缓存,我们可以为控件标记输出缓存策略,这样控件便不会每次都完整执行一遍。当然这个策略还不够灵活,因为它缓存的 最小单元是“控件”,而不是页面中任意的部分。因此我在一年多前提出了一个CachePanel, 由它包装的页面内容都可以被缓存,无论其内部是控件还是普通输出的内容。在实际生产过程中,CachePanel起到了非常重要的作用,许多场景下只要在 页面中包裹一个<ext:CachePanel runat="server" />,性能立即就有了质的飞跃。

  只可 惜,在如今ASP.NET MVC的时代无法直接使用CachePanel这样的服务器端控件。因为CachePanel需要服务器端代码的配合,而ASP.NET MVC中的页面只是“视图模板”,除了呈现之外就不应该有其他职责。因此,我们必须提出一种脱离于后端代码的“标记”方式,将视图中的内容片断进行随意地 缓存。在RailsDjango中都有类似的特性,但ASP.NET MVC甚至在2.0的Road Map中还没有包含这一功能,于是我们只能自己动手丰衣足食。不过有了ASP.NET WebForm作为强大的视图引擎,加这样的功能简直是举手之劳:

public static class CacheExtensions
{
public static string Cache(
this HtmlHelper htmlHelper,
string cacheKey,
CacheDependency cacheDependencies,
DateTime absoluteExpiration,
TimeSpan slidingExpiration,
Func<object> func)
{
var cache = htmlHelper.ViewContext.HttpContext.Cache;
var content = cache.Get(cacheKey) as string;
if (content == null)
{
content = func().ToString();
cache.Insert(cacheKey, content, cacheDependencies, absoluteExpiration, slidingExpiration);
}
return content;
}
}

  我们为HtmlHelper增加了一个Cache扩展方法,接受一些缓存参数(缓存键,绝对过期时间,偏移过期时间),以及一个生成缓存内容的 Func<object>委托。Cache方法的逻辑非常简单:首先根据缓存键来获取内容,如果存在则直接返回,否则即调用委托对象获得新内 容,并将其放入缓存。这样在缓存命中的情况下,委托的开销便可以节省下来了。

  例如,我们可以使用这样的代码进行测试:

Before Rendering:
<%= DateTime.Now %>
<br />
Rendering:
<%= Html.Cache("Now", null, DateTime.Now.AddSeconds(60), Cache.NoSlidingExpiration,
() => { System.Threading.Thread.Sleep(5000); return DateTime.Now; }) %>
<br />
After Rendering:
<%= DateTime.Now %>

  在实际情况中,我们是不会在代码中调用Thread.Sleep方法的,不过这里我们需要模拟一段开销,因此通过暂停当前线程来实现时间消耗。于是我们第一次打开页面:

Before Rendering: 2009/9/17 16:52:37
Rendering: 2009/9/17 16:52:42
After Rendering: 2009/9/17 16:52:42

  从结果中可以看出,Before Rendering和After Rendering相差了5秒钟,这就是Thread.Sleep(5000)的效果。但是如果您在60秒以内再次刷新页面,便可以看到缓存的效果:

Before Rendering: 2009/9/17 16:52:55
Rendering: 2009/9/17 16:52:42
After Rendering: 2009/9/17 16:52:55

  可以看出,Rendering阶段显示的还是刚才的时间,而Before Rendering和After Rendering是即时更新的。此外,由于Cache方法将Thread.Sleep(5000)的开销节省了下来,因此Before Rendering和After Rendering两个阶段打印出的时间完全相同。

  怎么样,简单吧。不过您应该会感到疑惑,这不是我们想要的结果啊,我们想缓存的是页面上的一个片断,但是现在必须将被缓存的内容作为一个完整的字符串输出,那么我们又该如何实现呢?难道我们要这么写吗?

<%= Html.Cache(..., () => "<span style=\"color:red;\">" + Model.Title + "</span>") %>

  当然不可能这样。如果只是这样的话,那么这个Cache的可用性毫无疑问会非常低。因此,我们还需要寻找更好的解决方案——关于这点,我们下次 再聊。而目前的Cache方法,最方便的输出大端页面内容的做法则是将内容放在一个Partial View中,然后使用Html.Partial方法输出内容:

<%= Html.Cache(..., () => Html.Partial("MyPartialViewToCache")) %>

  请注意,我们这里使用的是扩展后的Partial方法,而不是自带的RenderPartial。之前我们谈过WebForm页面的输出方式,而RenderPartial方法是直接向Response.Output输出页面内容,因此我们无法将其捕捉为一个字符串。不过,之前文章中的Partial方法是“山寨”版本,而符合“标准”的Partial方法实现已经包含在MvcPatch项目中。如果您感兴趣的话,可以获取它的源代码并编译。我这段时间在一部分一部分地将以前项目中较为通用的扩展及修改提取至MvcPatch中,希望可以使MvcPatch成为一个可复用的强大组件。

[资源]开发生涯中经常"溜达"的网站

mikel阅读(762)

作为软件开发人员,在做项目的时候难免会碰上一会技术的问题,如因项目中需要添加一些特殊的样式、动画效果或使用些现在的第三方控件等,
这时就开始上网狂“百度”或“google”,现在收集了一些自任为对软件开发时挺有参考价值的网站,与IT同仁们分享下:
1、http://www.dynamicdrive.com/
2、http://www.codeproject.com/
3、http://mochaui.com/demo/
4、http://www.asp.net/AJAX/AjaxControlToolkit/
5、http://www.coolite.com/
6、http://aspnetajax.componentart.com/
7、http://flexigrid.info/
8、http://jqueryui.com/themeroller/
9、http://examples.coolite.com/
10、http://extasp.net/
11、http://www.51aspx.com/
12、http://www.ajaxasp.net.cn/
13、http://www.walterzorn.com/tooltip/tooltip_e.htm

[Flex]Mate框架应该成为Flex4的新标准吗?

mikel阅读(814)

   Flex开发领域有许多框架来创建可扩展的企业级RIA应用,在我看来有两个主要框架:Cairngorm和Mate。二者都为企业级RIA提供了 一个坚实的基础。根据我使用Cairngorm和Mate在企业Java应用的情况来看,Mate对于小型和大型项目来说都是不错的选择。 Cairngorm使用一个单件模式,这使得它难于使用modules,在编译时间、SWF文件尺寸、开发速度上会耗费巨大的资源。相比之下,Mate使 用注入方式使得其易于并快速创建、维护和调试应用。我不是说你使用Cairngorm就错了,但是我的第一选择是Mate。

 

 

  Cairngorm开发走到了尽头而Mate方兴未艾

 

    Cairngorm被Adobe购买(编者注:此处不实)而且最终开源,自从2008年7月在SVN代码库开放源码,Cairngorm总共有0个 新的release,总共15个提交,而且自从2008年11月就没有新的提交,自从Flex2以后就没有一次Cairngorm代码发布。相 反,Mate也是开源的,Mate光去年就有17个新的版本,并伴随有几百个提交到代码库中,没有其他的Flex框架有如此的活跃度。

 

 

  Mate和总线结构

 

    Mate有一些不足之处,而且代码上没有任何处理,这就是总线结构。Mate由asfusion的几个人来维护,他们确实在更新论坛、提供文档、在 各种Flex活动中做演示等方面做的很好。但是,如果他们决定改变他们的职业或者仅仅过度维护Mate,那么可能没有人会继续涉足并保持这个框架不断前 进。我希望看到Mate在它的代码层面获得广泛的支持,有更多的人提交代码而且在框架的发展方向上能做出决定。希望能发布一个Mate1.0的版本(编者 注:目前版本为0.8.8.1),这个版本的代码要非常稳定,但是1.0版本对于想使用它来开发企业项目的人来说要让人觉得放心。

 

 

  支持两个框架或者都不支持

 

    Adobe几年前买了Cairngorm,这对于帮助Flex RIAs来说是个好的主意。现在FlexSDK和Cairngorm是开源的,但现在游戏规则完全改变了。看起来没人在使用Cairngorm,或者寻找 在Cairngorm下对于modules的增强。而且看起来没有社区在对该项目进行参与(编者注:这个说法不正确,有来自UniversalMind的 Extensions for the Adobe Cairngorm MVC,http://code.google.com/p/flexcairngorm/)。有什么因素会使Adobe改变它对一个框架的方 向?Cairngorm在5年后仍然使Adobe的框架吗?Flex4的时候是该去改变了。一个选择就是提供Cairngorm和Mate作为Flex的 专属框架。但是问题来了,谁来维护这些框架?第二个选择是不支持任何一个框架。这种方式社区会驱动RIA发展的方向,通过使用最好用的框架。这种方式已经 存在了数年,例如Struts, Spring, Hibernate, Apache等等。通过支持仅仅一个框架,框架创新的大门就关上了,FlexSDK持续改进并让社区兴奋,其中的关键是让社区参与改进,这是一个巨大的进 步。

 

 

原文在这里:
http://www.flexpasta.com/index.php/2009/07/16/mate-framework-should-it-b…

中文翻译原文:http://www.riameeting.com/node/416

[SEO]小欧:搜索引擎优化的最终“三成”

mikel阅读(916)

搜索引擎优化(或称SEO) 是通常被划分到网络营销的技术范畴,这是因为搜索引擎优化有助于也有利于网络营销的成单率。况且掌握它也很简单,甚至有人认为这玩意根本就不是一项正经技 术,因为看得懂HTML就完全可以上手操作。小欧也觉得SEO也不单单是一种很肤浅的技术,它更是一种全面优化每张网页或整个网站的活动,致使它们与搜索 引擎更加友好,从而在搜索结果排名中更能被访客发现(未必就一定要靠前,一定要到第1页)。

搜索引擎优化的唯一不变就是:即使你做了所有的事情,那也是应该的。你不能用这种“应该”来跟搜索引擎来讨价还价。更不能抱怨它为什么没有给你相应的排名和位置。小欧最近优化的这个有关北京工商代理项目就是这样,略加修改整体网站的结构和链接,结果,在30个小时内,Google就成功收录并检索到。

说到这儿,可能有人就问了,搜索引擎优化跟网络广告是怎么一回事呢?注意!SEO是不是广告,有待深入探讨,但SEO肯定不是为了广告而去优化的。当然,你可以把你要宣传的这些关键词都融入其中,比方说,前些天有个北京短信群发网站让小欧去做优化评估,打开一看,全是堆砌。从头到脚都是那几个词,可能这样的操作,也许就是为了广告而操作吧~~ 但是,这肯定不是在优化。

SEO可以说是一个非常锻炼人耐心与细心的一种长期活动。有时,为了一热门词能更好的被搜索 引擎所接受,奋斗几个月太正常了,而且结果往往也不是我们意料中的那么理想。如果,你做的是一个非常罕见的词,可能会轻松一些,基本上一周左右就能见到成 效。但大多数情况下是这样的,客户让你做,就是做一些难度大的、竞争度高的、排名浮度不稳定的(比如:北京办照),这个时候,就需要我们投入很大的时间和精力来脚踏实地的从头开始了。

既然是从头,那何为头?头就是根~ 何为根?根就是原理?好,接下来就看看搜索引擎的工作原理是什么?

首先我们得清楚搜索引擎与我们的大脑是不同的,它没有我们这么聪明(至少目前没有),它是靠 文本信息为驱动的,也就是说,你的网站行不行,能不能被搜索引擎找到,你得先看一下你的网站上有没有文字?如果没有,对不起,它不认识你。你与它无缘。随 着技术的迅速进步,搜索引擎不会像以往那么“单纯”了,比如:看到了“”,它不会只很单纯的认为此网站就是“牛”站,因为“牛”太多了,可以说人,可以说物,可以说此人很牛(牛逼),可以说此站有牛(肉牛), 还可以说此处产牛(牛厂),更可以说牛原来如此好吃(某某牛肉馆)。。。。。等等等等,太多了。。相反,搜索引擎在抓取网页时,会考虑在特定的时间和特定 的地点出现某些特定的关键词,这还不算,它还要把这些抓取到的东西拿“回家”再进行“加工”和计算,最后,才能被我们在搜索框中检索到。

大概过程为:爬行>>索引>>审核>>计算>>检索

先来看爬行,这项工作主要是靠搜索引擎蜘蛛来 完成,小蜘蛛遵循从一个网页的索引到另一个页面的索引。等网络上的页面超过20亿万张之后,小蜘蛛无需要天天爬行,只看哪些页面是新添加的?哪些页面是最 新修改过的?所以,我们一再强调,为什么要时常更新自己的网站,就是这个道理,有的网站一个月甚至几个月蜘蛛都不去,就是因为他的网站自从建成之后,闻所 未闻从未管理过。

接下来,我们就要知道,如何才能被蜘蛛发现我们这里有它喜欢吃的食物。如前所述,抓取工作不 是人,小蜘蛛它们是看不到图片、Flash、JavaScript、框架、密码保护的网页和目录的。因此,如果你的网站上全是这些内容,小欧建议你最好运 行一下蜘蛛模拟器。免得来了之后,又走了。(至于具体如何使用蜘蛛模拟器,小欧下次介绍)

在抓取网页时,小蜘蛛就会把从网页上的数据记录到索引页,我们知道,索引页存储在一个庞大的 数据库里,小蜘蛛就是在那里完成检索。从本质上讲,其实索引过程就是确定网页的描述和关键词的过程。对我们人来说,是根本无法处理如此大量的数据信息。有 时,它们可能得不到某些网页的正确描述世解释,这时,就需要我们通过优化来帮助它们,这将会使小蜘蛛们更容易更正确地定位自己所爬行的页面,并为下一步的 排名作好准备。

当用户的搜索请求来到时,搜索引擎进程先会与索引到数据库中的网页搜索请求的字符串进行比较。因为它很可能超过1个亿(包括搜索字符串),这时搜索引擎启动“计算关联”来对其索引中的每个搜索字符串。

有多种搜索算法来 计算搜索字符串的相关性。而且每种算法都有自己对关键词密度、链接和元标记的不同权重。这就是为什么不同的搜索引擎能提供“关键词”相同,但“检索结果” 不同的网页来。说到这儿,我们也知道为什么那些著名的搜索引擎:如Google、百度等会定期改变他们的算法,我们如果想得到很好的排名,就必须要适应他 们的最新变化,这是其一;其二就是你的竞争对手,如果你想立于不败之地,那就要深入持久的进行搜索引擎优化。

最后,就是检索结果了。通过上述我们一系列各个环节的努力,搜索引擎用户能够最快捷最简单最有效的找到他想要的信息,我们就成功了。搜索引擎就成功了。用户也成功了。此谓“三成”啊。。。呵呵~~ 好了,今天就到这儿。

小欧原创:关注最新搜索引擎算法&SEO排名

如需转载:请以链接形式注明来自小欧博客 http://blog.sina.com.cn/seo4

[Flex]基于Flex的界面组合SDK

mikel阅读(840)

转载:http://www.cnblogs.com/baihmpgy/archive/2009/09/16/1567387.html
以下是界面类似我们正在开发的一个产品的主界面,前端展示采用Flex开发,后端系统是基于Java的SOA框架。界面左边是导航条,右边是内容区(当然 还有其它栏目,在此忽略)。内容区一般由多个UI Part组成,每一个Part利用异步机制从后端获取数据,此外,它还将接收来自后端的通知消息。整个界面非常符合微软CAB思想,不过Flex没有 CAB组建,但是可以采用Microsoft用户控件方式定义一块一块内容。界面内容区的UI Part可能会被重用。

在 设计中,我想利用界面组合思想来设计,采用该思想的优点有:1)界面分割成不同的组成部分,每一部分实现一个功能,更加符合SRP原则;2)实现每一个 UI Part时,只需专注复杂界面中的一块内容的实现,比较简单;3)容易实现重用;其缺点有:1)每一个界面由多个UI Part组成,需要维护UI Part之间的联系;2)新手不太容易看懂界面的实现。
鉴于微软界面组合诸多优点,我决定将其思想引入到Flex,自己实现一个Composition SDK based on Flex,该SDK实现过程中参考了CAB & SCSF和Prism。
考虑到该软件需要实现的功能,这个SDK将支持如下功能:
1 UI Part生命周期管理。每一个UI Part在显示的时候,需要从后端获取数据,然后监听数据更新消息,当点击界面的“Tab 2”时,Tab 1被隐藏并停止监听消息,Tab 2被显示。在我看来每一个UI Part具有Activated、Deactivated和Closed生命周期状态,当处于Activated状态时,UI Part显示呈现所需数据,当处于Deactivated状态时,UI Part被隐藏并停止更新数据,当处于Closed状态时,UI Part被关闭并停止更新数据,它将被销毁。生命周期管理功能提出的目的是为了实现生命周期变更驱动数据更新,也就是每一个组件数据更新是由其自身的生命 周期状态决定的,不需要由父节点控制,从而实现更大粒度复用。
2 UI Part组合和动态注入。这个功能允许直接在视图容器类中定义每一个UI Part,在这种方式中,一旦容器被显示,则所有的UI Part将被显示;或者是其中某些UI Part是在运行时被动态注入并呈现的,当容器呈现时,根据需要注入特定的UI Part。
3 Master-Details UI Part支持。Master-Details UI Part是一对特殊的UI Part,当Master UI Part的数据发生变更后,Details UI Part也需要更新,和.NET的Master-Details View是一样的。
4 采用Hook机制实现,使得在实现1~3功能的时候,可以尽量与标准控件兼容,不必创建自定义控件或者仅需创建非常简单的自定义控件。Hook机制原理如 下:A)每一个功能由一个Hook实现;B)比如LifecycleHook,对于一个叶子节点的组件,当其被显示/隐藏/关闭时,该Hook要维护其状 态;对于一个容器节点,它除了要维护自己的状态,还要维护子控件的状态,比如VBox容器,当VBox被显示时,其状态为Activated且其所有一级 子节点状态也是Activated;而对于TabNavigator容器,当其被显示时,其状态为Activated且当前选中的Tab的状态也是 Activated,其余Tab的状态都是Deactivated;C)Hook的创建过程是递归的监听界面根节点 onChildAdded/Removed事件;D)SDK提供Hook注册表和Hook管理器,Hook注册表定义了每一类型的组件对应的Hook,而 Hook管理器定义了每一个控件对应的Hook实例。
5 基于该SDK,每一个视图的设计由Workspace和UI Part组成,Workspace使用Flex标准容器控件定义了界面的布局;UI Part是界面每一部分功能的实现,自己封装了生命周期驱动的数据更新。
代码的设计比较简单,其结构如下:

ComponentTreeHook是整个Hook机制的核心类,它将递归监听根节点控件的onChildAdded/Removed,当有子节点添加时,递归挂载整个控件树,挂载过程代码如下:

Code

附件是该SDK的测试文件,包含UI测试和Unit测试。测试文件

[DeBug]System.Threading.ThreadAbortException中第一次偶然

mikel阅读(719)

开发环境为VS2005,OS 为Windows 2003,系统登录后在跳转到另一页面时会报此错误:
在 System.Threading.ThreadAbortException 中第一次偶然出现的“mscorlib.dll”类型的异常
“System.Threading.ThreadAbortException”类型的异常在 mscorlib.dll 中发生,但未在用户代码中进行处理
但不影响程序的正常运行。于是在网上查了查,发现相关资料不多。后来找到微软的官方解释,搞定。
————————————————————————————————————–

症状

如果使用 Response.EndResponse.Redirect Server.Transfer 方法,则出现 ThreadAbortException 异常。 可使用 try-catch 语句捕捉此异常。

原因

Response.End 方法停止页的执行,并将该执行变换到应用程序的事件管线中的 Application_EndRequest 事件。 Response.End 后面的代码行将不执行。
此问题出现在 Response.Redirect Server.Transfer 方法中,这是由于这两种方法都在内部调用 Response.End

解决方案

若要解决此问题,请使用下列方法之一:

  • 对于 Response.End,调用 ApplicationInstance.CompleteRequest 方法而不调用 Response.End,以便跳过 Application_EndRequest 事件的代码执行。
  • 对于 Response.Redirect,使用重载 Response.Redirect(String url, bool endResponse),对 endResponse 参数它传递 false以取消对 Response.End 的内部调用。例如:
      Response.Redirect ("nextpage.aspx", false);

    如果使用这种解决方法,Response.Redirect 后面的代码将得到执行。

  • 对于 Server.Transfer,请改用 Server.Execute 方法。

状态

这种现象是设计使然。