‘ .NET开发 ’ category archive

Visual Studio 2010 尝鲜

593 views 四月 13, 10 by Timothy

昨晚在群里看到同事的讨论,MSDN终于放出Visual Studio 2010 RTM下载了(下文简称VS2010)。早上上班看到这个消息很是激动,迫不及待的去MSDN把期待已久的VS2010给拖了下来。VS2010和以往发布的版本不一样,这次划分为三个版本:Professional、Premium、Ultimate。
俺比较贪心,要用就用最高版本,拖了个Ultimate旗舰版,ISO镜像总共2.2GB,其实跟以往的VS2008相比,也不算大。

下面是VS2010中的新增特性(网上copy过来的):
·对多显示器的支持
·支持调用继承树
·新特性支持TDD
·新特性支持Office开发
·全新的Quick Search功能
·Call hierarchy高亮对象的引用
·直接使用一个未定义的对象
·提供更方便的javascript脚本编写
·HTML snippets超越智能提示
·可以进行自定义开始页
·C# 4.0的语言新特性
·提升了SharePoint开发功能
·特性聚焦:分析和调试并行应用程序
·正式包含F#函数式语言

下载完后,迫不及待开始安装:
1
2 和期望比较不一样的是,安完需要的组件,供占5个多GB的硬盘空间,还是比较苗条了。选完需要安装的组件,俺很慎重的按下了Install按钮……
3
漫长的安装过程,中途机器重启了两次,一次是在.NET Framework 4.0安装过后,一次是在整体安装完毕过后。

4安装完毕,俺怀着激动的心情,启动了VS2010,跳出不规则的Splash Window.6

整体使用感觉相当不错,此前还担心WPF实现的IDE界面会拖慢运行速度,看来这样的担心是多余的。跟VS2008相比,感觉VS2010的反应速度会快很多。看来这方面的优化还是做足了功夫的。另外,令人满意的是VS2010的扩展功能,内置的Extention Manager能够看到很多的在线扩展,选择你想要的,可以在线安装和卸载。这种方式让VS2010的扩展更加丰富和强大,值得表扬!

image另外,还有很多的关于.NET Framework 4.0 以及 VS2010的新特性还没来得及体验,比如dynamic,并行编程,新版的ADO.NET Entity Framework,新引入的F#语言,等等……
无意中也发现,ReSharper 5.0 也随着VS2010的发布而发布了,看来步调相当一致啊~这款功能强大的工具,自从VS2005开始,俺就一直用,现在不装反而不习惯了。于是卸掉之前的ReSharper 4.5,升级到5.0版。ReSharper 5.0本身也支持VS2008和VS2010,这样机器上的两个IDE可以和谐共存,并同时使用ReSharpter 5.0,简直太完美了。

总体感觉:VS2010和VS2008相比,确实是一大进步,或者说,确实是个飞跃…… 打算用段时间就把VS2008从机器中清理掉,嘿嘿……

扩展方法收集–实体验证

304 views 一月 17, 10 by Timothy

关于实体验证,是早期在博客园看到一个同学的文章,里面利用扩展方法对实体进行验证的思路比较有创意。大家可以先跳过去先看一下:http://www.cnblogs.com/tristanguo/archive/2009/05/15/1457197.html
这个实体验证的扩展方法,设计比较巧妙,充分利用了链式编程的特点,让代码更加简洁和美观,唯一不足的地方是后来作者在改进的时候感觉不是很到位。于是偶的同事Rex同学,对这实体验证的扩展方法进行了改进,在我看来,应该算是比较完美的解决方法了,因此在我们的实际项目中,也比较大量的应用到了此扩展方法。在这里贴出来,给大家分享一下。
贴代码:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace CommonLib
{
    public static class EntityValidator
    {
        public static ValidateResult Validate(this T target,
                                              Predicate predicate,
                                              string errorMessage)
        {
            var result = new ValidateResult(target);
 
            if (!predicate(target))
            {
                result.Errors.Add(errorMessage);
            }
 
            return result;
        }
 
        public static ValidateResult Validate(this ValidateResult target,
                                              Predicate predicate,
                                              string errorMessage)
        {
 
            if (!predicate(target.Entity))
            {
                target.Errors.Add(errorMessage);
            }
 
            return target;
        }
    }
 
    public class ValidateResult
    {
        internal List Errors { get; set; }
        internal T Entity { get; private set; }
        public bool HasErrors
        {
            get { return Errors.Count > 0; }
        }
 
        internal ValidateResult(T entity)
        {
            Errors = new List();
 
            Entity = entity;
        }
 
        public string[] ErrorMessages { get { return Errors.ToArray(); } }
    }
}

有了这个实体验证的方法,代码可以简洁多了,并且再也不用在业务逻辑判断中,写入复杂的if…else语句块,取而代之的代码,示例如下:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
ValidateResult validateResult =
                        content.Validate(c => !string.IsNullOrEmpty(c.Name), "Name为空")
                               .Validate(c => c.MobilePhone.Length == 11 , "MobilePhone长度不正确")
                               .Validate(c => c.Age >= 10), "年龄不在规定范围")
                               .Validate(c => c.Height <= 200 && c.Height >= 180, "身高不符合标准");
 
                    if (validateResult.HasErrors)
                    {
                        log.Info("实体验证失败:");
                        validateResult.ErrorMessages.ForEach(p => log.Info(p));
                        return null;
                    }

怎么样?这样的链式编程的代码,比起一大串的if…else组合语句,简洁、明了多了,并且还可以提高代码阅读的效率,强烈推荐各位同学使用。

Linq to Sql 之延迟加载与立即加载

256 views 十二月 20, 09 by Timothy

Linq的延迟加载

Linq to Sql中默认采用的模式就是延迟执行,所谓延迟执行,其实就是在获取对象本身时,并不会获取和其关联的其他对象,只有在访问其关联对象的时候,程序才会去加载关联对象的数据到内存中。这样的好处是程序不会在初次访问的时候,就加载大批量的数据,而是以一种延迟加载的方式进行处理,相对而言,对于系统和网络的性能开支会减小很多。对于一个默认的Linq to Sql查询,延迟加载就是其默认的设置,不过,在某些情况下,延迟加载并非完全“智能”,不但没有实现其本意,反而增大了网络流量和性能开支。下面我们以SQL Server中的演示数据库NorthWind来试验一下:

linq_context

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
LinqTestDataContext ctx = new LinqTestDataContext();
ctx.Log = Console.Out;
 
var result = ctx.Orders.Where(p => p.OrderID == 10251);
 
foreach (var t in result)
{
	Console.WriteLine("OrderID:" 
		+ t.OrderID 
		+ "-" 
		+ "OrderDate:" 
		+ t.OrderDate.Value.ToString("yyyy-MM-dd"));
}

通过Linq to sql查询所有OrderID为10251的订单信息,并输出订单编号和订单日期。通过显示Linq的日志输出,我们可以看到后台生成的SQL语句如下:

1
2
3
4
5
6
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [
t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[Sh
ipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPosta
lCode], [t0].[ShipCountry]
FROM [dbo].[Orders] AS [t0]
WHERE [t0].[OrderID] = @p0

输出的SQL看来还比较正常。下面我们再来改一下我们的程序:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 foreach (var t in result)
            {
                Console.WriteLine("OrderID:" + t.OrderID 
		+ "-OrderDate:" 
		+ t.OrderDate.Value.ToString("yyyy-MM-dd") 
		+"-CustomerName:"
		+ t.Customer.ContactName);
                foreach(var m in t.Order_Details)
                Console.WriteLine("ProductID:" 
		+ m.ProductID 
		+ "-Price:" 
		+ m.UnitPrice 
		+ "-Amount:" 
		+ m.Quantity);
            }

出了输出订单相关信息外,还输出其关联对象:客户姓名、订单中的产品编号、单价、数量

再来看看输出的SQL语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [
t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[Sh
ipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPosta
lCode], [t0].[ShipCountry]
FROM [dbo].[Orders] AS [t0]
WHERE [t0].[OrderID] = @p0
 
 
SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactT
itle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Coun
try], [t0].[Phone], [t0].[Fax]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[CustomerID] = @p0
 
 
SELECT [t0].[OrderID], [t0].[ProductID], [t0].[UnitPrice], [t0].[Quantity], [t0]
.[Discount]
FROM [dbo].[Order Details] AS [t0]
WHERE [t0].[OrderID] = @p0

我们可以看到,对于我们修改后的代码,程序向数据库请求了三条SQL语句,当然,这还不是最坏的情况,但是我们在这里的确看到延迟加载似乎“变了味道”,不但没有节省开支,反而增大了网络浏览。怎样才能改善这样的情况呢?

关于立即加载

其实我们知道,有很多扩展方法会导致延迟加载失效,而开始立即执行。当我们在调用诸如:ToList、ToDictionary、ToLookup或者ToArray之类的扩展方法之后,程序会将最终的结果存放到某个临时的变量集合中,并让所有的数据一次性的加载完成。

另外,还有一种方式,通过设置DataContext的DeferredLoadingEnabled属性为false,显示的关闭默认的延迟加载方式。

?View Code CSHARP
1
2
LinqTestDataContext ctx = new LinqTestDataContext();
ctx.DeferredLoadingEnabled = false;

 

这些方式虽然比较方便,但是还是有一定的局限性。例如,简单的使用ToList只能解决一些简单的查询问题,而对于复杂的查询需求,ToList还是不能解决延迟取得子对象所引发的多次查询问题。并且,在大量数据被加载到内存中的时候,对内存的需求也是很大的。不过,幸好Linq to sql给我们提供了另外一套不错的方法。

使用DataLoadOptions实现对加载对象的优化

Linq to Sql提供DataLoadOptions,用以立即加载关联的对象数据,其中包含两种方法:

LoadWith方法,用于立即加载与主对象相关联的数据

AssociateWith方法,用于对关联对象的数据进行筛选,并加载

有了DataLoadOptions,我们就可以用如下的方式优化我们的查询中需要加载的对象:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
            LinqTestDataContext ctx = new LinqTestDataContext();
            ctx.Log = Console.Out;
 
            DataLoadOptions dl = new DataLoadOptions();
            dl.LoadWith<order>(p =&gt; p.Customer);
            dl.LoadWith<order>(p =&gt; p.Order_Details);
            ctx.LoadOptions = dl;
 
            var result = ctx.Orders.Where(p =&gt; p.OrderID == 10251).ToList();
 
            foreach (var t in result)
            {
                Console.WriteLine(&quot;OrderID:&quot; + 
		t.OrderID 
		+ &quot;-OrderDate:&quot; 
		+ t.OrderDate.Value.ToString(&quot;yyyy-MM-dd&quot;) 
		+&quot;-CustomerName:&quot;
		+ t.Customer.ContactName);
                foreach(var m in t.Order_Details)
                Console.WriteLine(&quot;ProductID:&quot; 
		+ m.ProductID 
		+ &quot;-Price:&quot; 
		+ m.UnitPrice 
		+ &quot;-Amount:&quot; 
		+ m.Quantity);
            }

对于我们开发者而言,需要注意的是,对于同一个DataContext实例,DataLoadOptions只能设定一次,并且一旦设定,就无法更改。

接下来,运行程序,看看优化后,程序向数据库服务器请求的的SQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [
t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[Sh
ipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPosta
lCode], [t0].[ShipCountry], [t3].[OrderID] AS [OrderID2], [t3].[ProductID], [t3]
.[UnitPrice], [t3].[Quantity], [t3].[Discount], (
    SELECT COUNT(*)
    FROM [dbo].[Order Details] AS [t4]
    WHERE [t4].[OrderID] = [t0].[OrderID]
    ) AS [value], [t2].[test], [t2].[CustomerID] AS [CustomerID2], [t2].[Company
Name], [t2].[ContactName], [t2].[ContactTitle], [t2].[Address], [t2].[City], [t2
].[Region], [t2].[PostalCode], [t2].[Country], [t2].[Phone], [t2].[Fax]
FROM [dbo].[Orders] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[CustomerID], [t1].[CompanyName], [t1].[ContactName
], [t1].[ContactTitle], [t1].[Address], [t1].[City], [t1].[Region], [t1].[Postal
Code], [t1].[Country], [t1].[Phone], [t1].[Fax]
    FROM [dbo].[Customers] AS [t1]
    ) AS [t2] ON [t2].[CustomerID] = [t0].[CustomerID]
LEFT OUTER JOIN [dbo].[Order Details] AS [t3] ON [t3].[OrderID] = [t0].[OrderID]
 
WHERE [t0].[OrderID] = @p0
ORDER BY [t0].[OrderID], [t2].[CustomerID], [t3].[ProductID]

可以看出,之前的分三次向数据库提交sql的情况,现在被程序优化为一条带LEFT JOIN的关联查询,而获取关联数据。三次SQL请求,被优化为一次,从而减少了数据库和网络流量开支,由此看来DataLoadOptions的好处不言而喻。

一点小结

延迟加载与立即加载,并无孰优孰劣之区别,在某些情况下,需要我们根据自己的需求和实际情况来选择来进行选择。

开源的作业调度框架:Quartz.NET

441 views 十二月 05, 09 by Timothy

Quartz.NET是一套开源的作业调度框架,是最初由Java平台的企业级开源作业调度框架 Quartz 移植到.NET平台的。在sourceforge上,Quartz.NET的主页有这样的介绍: Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems. (Quartz.NET是能应用在小到轻量级的应用程序,大到重量级的企业级系统中的全功能的开源任务调度系统)并且,值得令人信服的是,Quartz.NET已经在生产环境中得以用,并得到了良好的反馈。

回想一下,在小到普通的应用程序,大到企业级项目中,我们常常会遇到需要定时轮询和调度的场景,简单点的需求是每隔固定的秒数或者分钟、小时进行轮询,复杂点的是每周的某个时刻,或者每月,或者每年才执行一次,又或者更复杂的情景,某些天需要执行,某些天不执行。用Windows自带的计划任务,能够实现简单的逻辑,如果要用程序实现这样的逻辑,想必也很复杂了。其实,用Quartz能很好的应付这些情况。

Quartz.NET的核心模块分为几个部分,Scheduler、Job、JobDetail、Trigger

Scheduler:实现调度的主体,负责调度任务的开始、结束,Scheduler中包含已注册的JobDetail和Trigger,并且,当Trigger中的条件得以触发时,Scheduler负责执行和Trigger相关联的Job,实现任务的调度。
Job:需要被调度的任务,Job必须实现IJob接口的Execute方法,而Execute方法的内容,其实也就是我们需要根据不同的需求自己实现的业务逻辑。
JobDetail:包含对Job的具体描述,包括Job的名称、所属的Group名称、以及Job的Type描述,用以Quartz.NET通过反射实现对Job的调用。
Trigger:触发器,描述调度的触发条件和时机。

Quartz.NET之所以没把Job和Trigger设计在一起,而是采用一种松耦合的方式,把Job和Trigger独立为两个不同的对象,原因是因为在Quartz.NET中,Job和Trigger可以建立一种一对多的关系,也就是可以对一个调度任务绑定多个Trigger,另外的好处是当某个Job的Trigger过期的时候,可以直接为Job绑定新的Trigger而不用重新定义和这个Trigger相关联的Job。

最初见到Quartz.NET,还是0.6的版本,一直躺在偶的硬盘里面,直到最近项目中遇到很多需要定时轮询处理的情况,突然想到了Quartz.NET,打开它在sourceforge的大本营,才发现Quartz.NET已经更新到1.0.1版本了。

Quartz.NET为我们提供了简洁的API,通过简单的例子,我们就可以很快上手和使用

在Quartz.NET中,所有的Job都必须实现IJob接口:

?View Code CSHARP
1
2
3
4
5
6
7
namespace Quartz
{
    public interface IJob
    {
        void Execute(JobExecutionContext context);
    }
}

JobExecutionContext 包含运行时的一些上下文信息,还可以用来传递参数

编写我们自己的Job,实现IJob接口:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
using Quartz;
 
namespace ExampleJob
{
    public class TestJob : IJob
    {
        public void Execute(JobExecutionContext context)
        {
            Console.WriteLine("Job executed!");
        }
    }
}

然后就可以通过Scheduler来调度任务了

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建SchedulerFactory
ISchedulerFactory schedFact = new StdSchedulerFactory();
 
// 获取Scheduler实例
IScheduler sched = schedFact.GetScheduler();
sched.Start();
 
// 创建JobDetail
JobDetail jobDetail = new JobDetail("ExampleJob", "MyJobGroup", typeof(ExampleJob));
// 创建Trigger,每分钟触发一次
Trigger trigger = TriggerUtils.MakeMinutelyTrigger();
// 设定trigger的开始时间
trigger.StartTimeUtc = TriggerUtils.GetEvenHourDate(DateTime.UtcNow);
trigger.Name = "myTrigger";
sched.ScheduleJob(jobDetail, trigger);

这样,任务调度框架便会每分钟调度一次我们的Job,在控制台每分钟都能看到一句 ”Job executed!”,是不是很简单呢?

其实Quartz.NET还有很多方便开发人员的特性和高级功能,比如通过配置文件配置Job和Trigger,可以让你的调度程序更加灵活,以应对后续需求的变化,而不需要重新编译程序。另外,还有调度线程池、JobStore、Clustering等很多高级功能,等着我们去发现。

相关资源:

Quartz.NET 主页:http://quartznet.sourceforge.net

在sourceforge的Quartz.NET主页上,有入门教程供初学者查看:http://quartznet.sourceforge.net/tutorial/index.html

另外,博客园的张善友也把教程翻译成了中文的,供E文不好的同学学习和参考,地址:http://www.cnblogs.com/shanyou/archive/2007/08/25/QuartzNETtutorial.html

[置顶]常用扩展方法收集&整理(置顶-不断更新)

1,004 views 十一月 14, 09 by Timothy

扩展方法,是.NET 3.5中引入的新特性,在《扩展方法使用小结中》,我有具体的介绍。合理的使用扩展方法,能节约不少的代码量,甚至能在开发中给我们带来意想不到的效果,让代码更加的简洁、易懂。其实,网上早就有了不少的大牛写的各种出色的扩展方法,以至于我有了整理一个扩展方法库的想法,把一些实用、优秀的扩展方法收集起来,一来为资源共享,二来也是为了应用在以后的项目代码中,提高开发效率。 Read the rest of this entry »

Page 1 of 512345