[转载]利用Attribute简化Unity框架IOC注入

mikel阅读(1076)

[转载]利用Attribute简化Unity框架IOC注入 – 破狼 – 博客园.

View Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Green.Utils;
using Microsoft.Practices.Unity.InterceptionExtension;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{

ELUnityUtility.Resolve<IClass2>().Show();
(typeof(IClass2).Resolve() as IClass2).Show();
Console.Read();
}
}

public interface IClass1
{
void Show();
}

[Green.Utils.ELPolicyinjection]
public class Class1 : IClass1
{

#region IClass1 成员
[TestCallHandler]
public void Show()
{
Console.WriteLine(this.GetType());
}

#endregion
}

[Green.Utils.UnityInjection(First, Name = class2, ConfigFile = App1.config)]
public interface IClass2
{
void Show();
}

[Green.Utils.ELPolicyinjection]
public class Class2 : ConsoleApplication1.IClass2
{
[Microsoft.Practices.Unity.Dependency(class1)]
public IClass1 Class1
{
get;
set;
}

[TestCallHandler]
public void Show()
{
Console.WriteLine(this.GetType());
Class1.Show();
}
}

[Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationElementType(typeof(CustomCallHandlerData))]
public class TestCallHandler : ICallHandler
{
#region ICallHandler 成员

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
if (input == nullthrow new ArgumentNullException(input);
if (getNext == nullthrow new ArgumentNullException(getNext);
Console.WriteLine(begin….);
var result = getNext()(input, getNext);
Console.WriteLine(end….);
return result;
}

public int Order
{
get;
set;
}

#endregion
}

[AttributeUsage(AttributeTargets.Method)]
public class TestCallHandlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
{
return new TestCallHandler();
}
}
}

欢迎大家指正,批评,交流是的大家都功能进步。代码下载

[原创]EasyUI的DataGrid合计汇总页脚使用教程

mikel阅读(3483)

最近做订单处理,需要汇总订单明细的数量和金额,所以涉及到使用EasyUI的Datagrid组件的页脚汇总功能,先看了一下官方教程,如下:

Tutorial » Displaying Summary Information in DataGrid’s Footer

In this tutorial, we will show you how to display the summary rows in the footer of datagrid.

To display footer row, you should set the showFooter property to true and then prepare the footer rows that is defined in datagrid data. Below is the sample data:

  1. {“total”:1,“rows”:[{“id”:1,“name”:“Chai”,“price”:18.00}],“footer”:[{“name”:“Total”,“price”:18.00}]}

Create DataGrid

  1. <table id=“tt” title=“DataGrid” class=“easyui-datagrid” style=“width:400px;height:250px”
  2. url=“data/datagrid17_data.json”
  3. fitColumns=“true” rownumbers=“true” showFooter=“true”>
  4. <thead>
  5. <tr>
  6. <th field=“name” width=“80”>Product Name</th>
  7. <th field=“price” width=“40” align=“right”>Unit Price</th>
  8. </tr>
  9. </thead>
  10. </table>

The footer rows are same as body rows, so you can display more than one summary information on footer.

Download the EasyUI example:

上面的教程不用多说,大家可以看一下,重点是再Json数据的格式问题上,需要加入footer的数组记住是数组,并且数组的列要和你datagrid中的列名一致,需要汇总的列有值,其他列可以不赋值
我的服务器端代码如下:
public Dictionary getOrderList(string ordercode)
{
Dictionary result=new Dictionary();
if (String.IsNullOrEmpty(ordercode))
{
result.Add("total",1);
result.Add("rows", new BuyOrderList());
result.Add("footer", new { Goods_Name = "合计", Buy_Number = 0, Order_Amount=0 });
return result;
}
else
{
List datas = new BusinessLogic().Select(new BuyOrderList() { Order_Code = ordercode });
result.Add("total", datas.Count);
result.Add("rows", datas);
List footer=new List();
footer.Add(new BuyOrderList(){Identifier=-2,Goods_Name="合计:",Buy_Number=datas.Sum(m =&gt; m.Buy_Number),Order_Amount=datas.Sum(m =&gt; m.Order_Amount)});
result.Add("footer",footer);
return result;
}
}

[原创]EasyUI常见问题汇总1:Tab组件的重复ID问题

mikel阅读(1261)

刚才遇到了一个Tab组件链接页面多个组件命名冲突导致保存信息保存不上的问题,主要原因是因为每个tab标签都需要一个User_ID的隐含输入组件的值,name可以重复无可非议

但是问题出在每个Tab页面都使用了相同的ID来标示,这就导致,当切换过tab页面后,再次利用$(‘#User_ID’).val(user);来给User_ID的组件赋值的时候,程序自然把值付给了第一个ID为User_ID的

组件,可是真正的想要赋值的组件没有赋上值,导致提交Form的时候提示值为空,看来有必要对EasyUI组件,编写个通用的命名和使用规范来避免这样的错误出现!

[转载]ASP.NET session超时设置

mikel阅读(1269)

[转载]ASP.NET session超时设置 – sky7034 – 博客园.

ASP.NET Session的默认时间设置是20分钟,即超过20分钟后,服务器会自动放弃Session信息.
当我们在ASP.NET程序中打开webconfig的时候,可以看到一段如下的代码:
ASP.NET程序代码:
<sessionState
mode=”InProc”
stateConnectionString=”tcpip=127.0.0.1:42424″
SQLConnectionString=”data source=127.0.0.1;Trusted_Connection=yes”
cookieless=”false”
timeout=”20″
/>
上面一段的代码就是配置应用程序是如何存储Session信息的了.
其中的timeout=”20″ 即为asp.net session的默认时间.需要更改时间我们在此处填上不同的数值就可以了,默认单位为分.

sessionState节点属性:

<sessionState mode=”Off|InProc|StateServer|SQLServer
cookieless=”true|false”
timeout=”number of minutes”
stateConnectionString=”tcpip=server:port”
SQLConnectionString=”SQL connection string”
stateNetworkTimeout=”number of seconds”
/>

必须有的属性是

mode 设置将Session信息存储到哪里
Off 设置为不使用Session功能
InProc 设置为将Session存储在进程内,就是ASP中的存储方式,这是默认值。
StateServer 设置为将Session存储在独立的状态服务中。
SQLServer 设置将Session存储在SQL Server中

可选的属性是:
cookieless 设置客户端的Session信息存储到哪里
ture 使用Cookieless模式
false 使用Cookie模式,这是默认值。
timeout 设置经过多少分钟后服务器自动放弃Session信息。默认为20分钟
stateConnectionString 设置Session信息存储在状态服务中时使用的服务器名称和端口号,例如:”tcpip=127.0.0.1:42424”。当mode的值是StateServer是,这个属性是必需的。
sqlConnectionString 设置与SQL Server连接时的连接字符串。当mode的值是SQLServer时,这个属性是必需的。
stateNetworkTimeout 设置当使用StateServer模式存储Session状态时,经过多少秒空闲后,断开Web服务器与存储状态信息的服务器的TCP/IP连接的。默认值是10秒

[转载]SQL Server 2008事务日志清理

mikel阅读(951)

[转载]SQL Server 2008事务日志清理 – dudu – 博客园.

这里的事务日志清理是指截断事务日志并释放空间。

操作方法:

1、通过备份事务日志进行截断

备份时的默认选项就是Truncate the transaction log(截断事务日志),备份完成后,事务日志就会自动被截断,但这时你查看日志文件的大小还是和原来一样。所以,需要通过第二步释放日志文件占用的空间。

2、通过收缩日志文件释放日志文件占用的空间

  • 将恢复模式由完整(Full)改为简单(Simple)
  • 收缩(Shrink)日志文件
  • 将恢复模式由简单(Simple)改为完整(Full)

3、收缩数据库(Shrink database)

[转载]android 短信接收流程分析——为更好的拦截短信做准备

mikel阅读(1062)

[转载]android 短信接收流程分析——为更好的拦截短信做准备 – 没有代码 – 博客园.

观察360的短信拦截和QQ管家的短信拦截,发现先安装的就能先拦截到的短信,然后中断广播,之后谁都不能获取到短信。从这里可以推出系统大概有一个广播表,同等级的按安装先后顺序排放。目前的方法是在应用层调用framework API进行控制的。

为了能更好的了解Android接收短信的流程,我进行了更深入的分析,从RIL的通信架构来分析当接收到短信的整个流程。从frameword里的RIL.java

clip_image002

文件可以看出发送短信和接收短信是通过Receiver和Sender架构,发送短信主要通过Sender框架,主要如下(图是从网上窃滴~):

image

上层函数调用Command Interface将请求消息发送到sender架构,由该架构将请求发送到RILD的架构。

目前主要分析接收短信走过的那些渠道,因此发送短信只是略微的说明一下。

接收短信流程如下(从发送消息到接收消息的处理流程)(图是从网上剽滴~):

clip_image002[6]

从 上图右边那块(结合代码)可以看出短信接收是从ril.cpp文件通过socket与RIL.java的socket交流,当ril.cpp收到短信后处 理完成后会通过socket发送字节流给上层的RIL.java,而在RIL.java中有Receiver架构(该架构主要是一条线程)在不断监听,

Receiver架构代码:

class RILReceiver implements Runnable {

byte[] buffer;

RILReceiver() {

buffer = new byte[RIL_MAX_COMMAND_BYTES];

}

public void

run() {

int retryCount = 0;

try {
for (;;) {

LocalSocket s = null;

LocalSocketAddress l;

try {

s = new LocalSocket();

l = new LocalSocketAddress(SOCKET_NAME_RIL,

LocalSocketAddress.Namespace.RESERVED);

s.connect(l);

} catch (IOException ex) {

try {

if (s != null) {

s.close();

}

} catch (IOException ex2) {

// ignore failure to close after failure to connect

}

// don’t print an error message after the the first time

// or after the 8th time

if (retryCount == 8) {

Log.e(LOG_TAG,

“Couldn’t find ‘” + SOCKET_NAME_RIL

+ “‘ socket after ” + retryCount

+ ” times, continuing to retry silently”);

} else if (retryCount > 0 && retryCount < 8) {

Log.i(LOG_TAG,

“Couldn’t find ‘” + SOCKET_NAME_RIL

+ “‘ socket; retrying after timeout”);

}

try {

Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);

} catch (InterruptedException er) {

}

retryCount++;

continue;

}

retryCount = 0;

mSocket = s;

Log.i(LOG_TAG, “Connected to ‘” + SOCKET_NAME_RIL
+ “‘ socket”);

int length = 0;

try {

InputStream is = mSocket.getInputStream();

for (;;) {

Parcel p;

length = readRilMessage(is, buffer);

if (length < 0) {

// End-of-stream reached

break;

}

p = Parcel.obtain();

p.unmarshall(buffer, 0, length);

p.setDataPosition(0);

// Log.v(LOG_TAG, “Read packet: ” + length +
// ” bytes”);

processResponse(p);

p.recycle();

}

} catch (java.io.IOException ex) {

Log.i(LOG_TAG, “‘” + SOCKET_NAME_RIL
+ “‘ socket closed”,

ex);

} catch (Throwable tr) {

Log.e(LOG_TAG, “Uncaught exception read length=”
+ length +

“Exception:” + tr.toString());

}

Log.i(LOG_TAG, “Disconnected from ‘” + SOCKET_NAME_RIL

+ “‘ socket”);

setRadioState(RadioState.RADIO_UNAVAILABLE);

try {

mSocket.close();

} catch (IOException ex) {

}

mSocket = null;

RILRequest.resetSerial();

// Clear request list on close

synchronized (mRequestsList) {

for (int i = 0, sz = mRequestsList.size(); i < sz; i++) {

RILRequest rr = mRequestsList.get(i);

rr.onError(RADIO_NOT_AVAILABLE, null);

rr.release();

}

mRequestsList.clear();

}

}
} catch (Throwable tr) {

Log.e(LOG_TAG, “Uncaught exception”, tr);

}

}

}

因 此从代码可以看出获取到短信消息后经过一系列地分析然后提交给processResponse(p); 方法处理,处理过程中会有两种response,一种是主动上报,比如网络状态,短信,来电等都不需要经过请求,用unsolicited词语专门描述, 另一种才是真正意义上的response,也就是命令的响应用solicited描述。那接收短信就是属于unsolicited,跳到 processUnsolicited (Parcel p)方法,查看该方法可得出会继续触发以下方法mSMSRegistrant.notifyRegistrant(new AsyncResult(null, sms, null)); 追溯该方法对象的创建,最后发现该方法设置handler的源头是在:SMSDispatcher类里

clip_image004

该 类做了一件重要的事:给Command Interface设置handler的处理方法,就是当接收到短信后触发mSMSRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));方法,然后回调调用之前传入的handler,接着在handler里面处理短信消息。

mCm.setOnNewSMS(this, EVENT_NEW_SMS, null);

mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);

mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);

mCm.registerForOn(this, EVENT_RADIO_ON, null);

handler处理接收到的短信消息:

@Override
public void handleMessage(Message msg) {

AsyncResult ar;

switch (msg.what) {

case EVENT_NEW_SMS:

// A new SMS has been received by the device

if (Config.LOGD) {

Log.d(TAG, “New SMS Message Received”);

}

SmsMessage sms;

ar = (AsyncResult) msg.obj;

if (ar.exception != null) {

Log.e(TAG, “Exception processing incoming SMS. Exception:”
+ ar.exception);

return;

}

sms = (SmsMessage) ar.result;

try {

int result = dispatchMessage(sms.mWrappedSmsMessage);

if (result != Activity.RESULT_OK) {

// RESULT_OK means that message was broadcast for app(s) to
// handle.

// Any other result, we should ack here.

boolean handled = (result == Intents.RESULT_SMS_HANDLED);

notifyAndAcknowledgeLastIncomingSms(handled, result, null);

}

} catch (RuntimeException ex) {

Log.e(TAG, “Exception dispatching message”, ex);

notifyAndAcknowledgeLastIncomingSms(false,
Intents.RESULT_SMS_GENERIC_ERROR, null);

}

break;

}

}

int result = dispatchMessage(sms.mWrappedSmsMessage); 该段会通过子类(GsmSMSDispatcher)的dispatchMessage方法处理。经一系列的判断处理最后普通短信将交给 dispatchPdus(pdus);这个方法处理。

protected void dispatchPdus(byte[][] pdus) {

Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);

intent.putExtra(“pdus”, pdus);

dispatch(intent, “Android.permission.RECEIVE_SMS”);

}

void dispatch(Intent intent, String permission) {

// Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any

// receivers time to take their own wake locks.

mWakeLock.acquire(WAKE_LOCK_TIMEOUT);

mContext.sendOrderedBroadcast(intent, permission, mResultReceiver,

this, Activity.RESULT_OK, null, null);

}

最 后,我们可以看出这个方法将短信通过顺序广播播放出去(action是SMS_RECEIVED_ACTION),无论广播是否被中断最后都会调用 mResultReceiver,这里会将已读或未读的状态告诉给对方。如果短信广播中间没有受到終止,那么接下来的流程 是:PrivilegedSmsReceiver类接收到Android.provider.Telephony.SMS_RECEIVED的请求然后调 用 intent.setClass(context, SmsReceiverService.class); 启动SmsReceiverService服务类来处理短信并保存短信。

参考文章:

http://newfaction.net/2011/03/15/android-2-2-ril-java-part-of-the-code-profile.html

http://blog.csdn.net/maxleng/article/details/5593759

http://www.360doc.com/content/10/0625/11/496343_35127382.shtml

[转载]从属性赋值到MVVM模式详解

mikel阅读(1056)

[转载]从属性赋值到MVVM模式详解 – zhangweiwen – 博客园.

示例源码

这 两天学习了一下MVVM模式,和大家分享一下,也作为自己的学习笔记.这里不定义MVVM的概念,不用苍白的文字说它的好处,而是从简单的赋值讲起,一步 步建立一个MVVM模式的Simple.通过前后对比留给读者自己去思考.我也不知道理解是否正确,有不对的地方,希望指出.

赋值VS绑定

要理解MVVM模式,最重要的是理解绑定的概念.做B/S或者对C/S理解不够的程序员可能不了解”绑定”,它与赋值类似,但又”高级”一点.

一个简单的类:

public class MyClass
{           
    public MyClass() {
        this._Time = DateTime.Now.ToString();
    }     

    private string _Time;
    public string Time {
        get {
            return this._Time;
        }
        set {
            this._Time = value;
        }
    }
}

赋值

private void UpdateTime_Click(object sender, RoutedEventArgs e) {
    _MyClass.Time = DateTime.Now.ToString();
    this.lable1.Content = _MyClass.Time;
}

private void Grid_Loaded(object sender, RoutedEventArgs e) {
    this.lable1.Content = _MyClass.Time;
}

很简单的对lable1的Content属性的赋值.总结一下这种模式的流程图:

image

这种模式很简单,很容易理解.缺点也是很明显,View跟CodeBehind紧紧耦合在一起了(事件方法里面需要知道lable1),还有到处都是this.lable1.Content = _MyClass.Time; 这样的赋值代码,这样可维护性是很低的.于是就有了绑定.

属性绑定

绑定就是把把东西关联在一起,例如人的手脚是和整个身体绑定在一起的,手指受伤了,人会感到疼痛.属性绑定通常是把一个Model属性绑定给一个控件的属性,于是它们就有了联系,Model的属性变化了,控件的属性也会变化.

wpf的绑定.

首先把View的DataContext设为MyClass.

<Window.DataContext>
    <local:MyClass />
</Window.DataContext>

这样我们就可以把MyClass的属性绑定给lable1的Content.

<Label Grid.Column="1" Grid.Row="1" Content="{Binding Time}" />

WinForm也能绑定:

public Form1() {
    InitializeComponent();
    this.label2.DataBindings.Add("Text", _MyClass, "Time", true);
}

运行程序:

image

点击Update Time按钮,比较遗憾,绑定那一行的时间并没有更新.看来需要做更多的工作.(见源码Example1)

INotifyPropertyChanged接口

原来对于上面的那个poco类,它的属性Time发生变化时,紧紧靠<Label Grid.Column=”1″ Grid.Row=”1″ Content=”{Binding Time}” />或者this.label2.DataBindings.Add(“Text”, _MyClass, “Time”, true); 是不够的,lable不能”智能”地知道MyClass的Time变化了,需要MyClass主动去通知lable:我的Time属性变化 了.INotifyPropertyChanged接口就是这样的功能.

INotifyPropertyChanged的源码:

// 摘要:向客户端发出某一属性值已更改的通知。
public interface INotifyPropertyChanged
{
    // 摘要:在更改属性值时发生。
    event PropertyChangedEventHandler PropertyChanged;
}

PropertyChangedEventHandler里的事件参数源码:

// 摘要:为 System.ComponentModel.INotifyPropertyChanged.PropertyChanged 事件提供数据。
 public class PropertyChangedEventArgs : EventArgs
 {
     // 摘要:初始化 System.ComponentModel.PropertyChangedEventArgs 类的新实例。
     // 参数:propertyName:已更改的属性名
     [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
     public PropertyChangedEventArgs(string propertyName);

     // 摘要:获取已更改的属性名。
     // 返回结果:已更改的属性名。
     public virtual string PropertyName { get; }
 }

接口非常简单,就一个PropertyChanged事件,而事件委托的参数也很简单,一个字符串属性名.Model继承 INotifyPropertyChanged后,在这个事件中是通知者的角色(执行事件),而<Label Grid.Column=”1″ Grid.Row=”1″ Content=”{Binding Time}” />和this.label2.DataBindings.Add(“Text”, _MyClass, “Time”, true); 这里可以理解为事件的订阅.

继承INotifyPropertyChanged后的MyClass:

public class MyClass : INotifyPropertyChanged
{
    public MyClass() {
        this._Time = DateTime.Now.ToString();
    }

    private string _Time;
    public string Time {
        get {
            return this._Time;
        }
        set {
            if (this._Time != value) {
                this._Time = value;
                if (PropertyChanged != null) {
                    PropertyChanged(this, new PropertyChangedEventArgs("Time"));
                }
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

重点是Set值时执行事件,运行程序发现,lable终于知道MyClass的属性变化了,它们绑定了.而且可以发现绑定是双向的,即控件的值更新,model的属性值也会更新,添加一个按钮显示model的属性值:

private void Show_Click(object sender, RoutedEventArgs e) {
    MessageBox.Show(_MyClass.Time);
}

image

这里做到了把Model的属性绑定给View的控件的属性中,下面看看集合的绑定.

集合绑定

跟上面一样,普通的集合控件们是不认的,要用特殊的集合,它就是ObservableCollection<T>,它继承了INotifyCollectionChanged和INotifyPropertyChanged.部分源码:

 [Serializable]
 public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
一个简单的类:
public class Employe
{
    public ObservableCollection<string> Employees { get; set; }

    public Employe() {
        Employees = new ObservableCollection<string>() 
        {
            "肥猫", "大牛", "猪头"
        };
    }
}

把它绑定到一个ComboBox中:

<ComboBox Grid.Column="2" Grid.Row="0"  ItemsSource="{Binding Employees}" Width="50px"/>

另外做一个按钮添加来Employees

private void AddDepartment_Click(object sender, RoutedEventArgs e) {
    _MyClass.Employees.Add(this.textBox1.Text);
}

运行程序,添加一个Employee,发现ComboBox也更新了(见源码Example3).

image

命令绑定

还有一个绑定就是命令绑定.实际解决的是要把View完全解耦,不用再写控件事件,因为AddDepartment_Click这样的写法就会把View和CodeBehind的耦合在一起,跟上面属性赋值类似.

ICommand

// 摘要:定义一个命令
[TypeConverter("System.Windows.Input.CommandConverter, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null")]
[ValueSerializer("System.Windows.Input.CommandValueSerializer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null")]
public interface ICommand
{
    // 摘要: 当出现影响是否应执行该命令的更改时发生。
    event EventHandler CanExecuteChanged;

    // 摘要:定义用于确定此命令是否可以在其当前状态下执行的方法。    
    // 参数:parameter:此命令使用的数据。如果此命令不需要传递数据,则该对象可以设置为null。
    // 返回结果:如果可以执行此命令,则为true;否则为false。
    bool CanExecute(object parameter);
    //
    // 摘要:定义在调用此命令时调用的方法。
    // 参数:parameter:此命令使用的数据。如果此命令不需要传递数据,则该对象可以设置为 null。
    void Execute(object parameter);
}

最主要需要实现的是Execute方法.即事件发生时要执行的方法.下面把Add Department的按钮事件去掉,改为绑定一个命令.实现这个命令首先要得到的是textbox上的值.要在命令里得到View控件的值,可以在 model里新建一个属性值与这个控件绑定,因为绑定是双向的,所以属性值就是控件的值.根据上面的Employe类添加如下代码:

private string _NewEmployee;
public string NewEmployee {
    get {
        return this._NewEmployee;
    }
    set {
        if (this._NewEmployee != value) {
            this._NewEmployee = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("NewEmployee"));
        }
    }
}

每个命令要实现为一个单独的类,继承ICommand,这里用一个委托把添加部门的逻辑转移到Employe中:

public class AddEmployeeCommand : ICommand
{
    Action<object> _Execute;
    public AddEmployeeCommand(Action<object> execute) {
        _Execute = execute;
    }

    public bool CanExecute(object parameter) {
        return true;
    }

    public event EventHandler CanExecuteChanged {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter) {
        _Execute(parameter);
    }
}

Employe类再添加一个ICommand用作绑定:

private ICommand _AddEmployee;
public ICommand AddEmployee {
    get {
        if (_AddEmployee == null) {
            _AddEmployee = new AddEmployeeCommand((p) =>
            {
                Employees.Add(NewEmployee);
            });
        }
        return _AddEmployee;
    }
}
有了AddEmployee 我们就可以绑定到按钮中:
<Button Grid.Column="0" Grid.Row="0" Content="Add Department" Command="{Binding AddEmployee}" />

到这里,我们可以得到跟上面一样的功能,但成功把按钮事件改为了命令绑定.(见源码Example4)

完成上面所有工作,我们解决了一个问题,即View”后面”的模块(Code Behind也好,Model也好)完全没了view的影子,”后面”的模块不用管textbox还是Label来显示一个Name,只管把Name赋值 就好了,也不用关心一个button还是一个picturebutton来点击,只管实现逻辑.但细心观察,代码还是有不少问题.

其中最主要的是为了实现上面的功能,污染了Employe这个类.Employe应该是常见的Model层中的一个类,它应该是一个poco类,职 责是定义领域模型和模型的领域(业务)逻辑.为了实现绑定,添加了各种接口和与领域(业务)无关的属性,这就是对Model的污染.所以,当想实现绑定, 而又不想污染model,就得引入新的一层–ViewModel,这样就走向了MVVM模式.

MVVM模式

VM是MVVM的核心.主要作用有两个.

1.提供属性和命令供View绑定

2.还要承担MVC模式中C(Controller)的职责,作为View和业务层的中间人.

模式实践.

把上面的代码稍为修改即可以改为MVVM模式.

Model,Employee回归Poco:

public class Employee
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }

    public void Add() {
        DataBase.AllEmployees.Add(this);
    }
}

ViewModel提供绑定的属性和命令:

public class EmployeeViewModel : INotifyPropertyChanged
   {
       public event PropertyChangedEventHandler PropertyChanged;

       /// <summary>
       /// 供?ComboBox绑ó定¨
       /// </summary>
       public ObservableCollection<Employee> Employees { get; set; }

       public EmployeeViewModel() {
           Employees = new ObservableCollection<Employee>(DataBase.AllEmployees);
       }

       #region 供?textbox 绑ó定¨        
       private string _NewEmployeeName;
       public string NewEmployeeName {
           get {
               return this._NewEmployeeName;
           }
           set {
               if (this._NewEmployeeName != value) {
                   this._NewEmployeeName = value;
                   if (this.PropertyChanged != null) {
                       PropertyChanged(this, new PropertyChangedEventArgs("NewEmployeeName"));
                   }
               }
           }
       }

       private string _NewEmployeeEmail;
       public string NewEmployeeEmail {
           get {
               return this._NewEmployeeEmail;
           }
           set {
               if (this._NewEmployeeEmail != value) {
                   this._NewEmployeeEmail = value;
                   if (this.PropertyChanged != null) {
                       PropertyChanged(this, new PropertyChangedEventArgs("NewEmployeeEmail"));
                   }
               }
           }
       }

       private string _NewEmployeePhone;
       public string NewEmployeePhone {
           get {
               return this._NewEmployeePhone;
           }
           set {
               if (this._NewEmployeePhone != value) {
                   this._NewEmployeePhone = value;
                   if (this.PropertyChanged != null) {
                       PropertyChanged(this, new PropertyChangedEventArgs("NewEmployeePhone"));
                   }
               }
           }
       }
       #endregion

       public ICommand AddEmployee {
           get {
               return new RelayCommand(new Action(() =>
                           {
                               if (string.IsNullOrEmpty(NewEmployeeName)) {
                                   MessageBox.Show("姓名不能为空!");
                                   return;
                               }
                               var newEmployee = new Employee { Name = _NewEmployeeName, Email = _NewEmployeeEmail, Phone = _NewEmployeePhone };
                               newEmployee.Add();
                               Employees.Add(newEmployee);
                           }));
           }
       }
   }

代码的职责非常明确,提供5个属性(1个命令,4个普通属性)供View绑定.虽然简单,但却产生了一大堆代码,可能这就是MVVM框架出现的原因.不管怎样,一个简单的MVVM模式的Simple就完成了(参考代码Example5).

MVVM:mvvm.png

参考:链接

[转载][IIS][ASP.NET] Web Gardens 特别功效:降低CPU占用

mikel阅读(797)

[转载][IIS][ASP.NET] Web Gardens 特别功效:降低CPU占用 – 博客园团队 – 博客园.

昨天开始,博客园Web服务器出现CPU占用率很高的问题(平均在80%)以上,当前站点的IIS同时连接在15000上下波动,访问速度变慢。

CPU占用高,还带来一个副作用:当CPU占用超过90%时,IIS会自动停用“动态内容压缩”(dynamic content compress),造成带宽占用高。

高CPU、高带宽、低访问速度,“两高一低”造成园友的“抱怨”、服务员的“崩溃”。。。就像一阵风暴突袭一个美丽宁静的花园。。。

在服务员们的努力下,今天终于让园子恢复了平静,而解决方法是借助另外一个花园 —— Web Gardens。

具体操作是:在IIS的应用程序池设置中,将Maximum Worker Processes设置为4(原先为1),也就是由4个独立的进程运行当前站点。

用了Web Gardens之后,立杆见影,CPU占用立即降下来,IIS同时连接也随之下降,访问速度即刻恢复。

[转载]为ASP.NET MVC RC分离Controllers-Views项目后添加“脚手架”功能(二)

mikel阅读(866)

[转载]为ASP.NET MVC RC分离Controllers-Views项目后添加“脚手架”功能(二) – SZW – 博客园.

上一篇《为ASP.NET MVC RC分离Controllers-Views项目后添加“脚手架”功能(一)》 中讲到如何分离ControllersViews项目,并且为Controllers项目添加MVC RC的“脚手架”,可惜“脚手架”的功能保留的过于完整,以至于自动创建和察看View页面都会在当前项目中进行,除非你心甘情愿每次创建完成后手动将文件转移到ViewsWeb)项目,否则这个“脚手架”的意义几乎就失去了。并且,及时转移了.aspx之类的View文件,那么查看还是在当前项目中进行,这可怎么办呢?本文将为此创造一个“一石二鸟”的方法,可以同时解决View页面新增和查看的问题。

View查看

由于没功夫研究这个“脚手架”内部的机制(如果有朋友能够实现hack的话也就不用这么麻烦了,呵呵),所以想到了一个变通的方法:能不能在Controller对应的地方加上一些“快捷方式”,然后当查看的时候直接查找源文件呢?经过了一些尝试,发现直接创快捷方式文件(.lnk)是行不通的。

于是又打起了MyMvc.Controllers.csproj的主意。经过一些测试研究,发现. Csproj文件支持一个<Link>的标签,可以在VS程序中起到“快捷方式”的效果(具体如何设置节点在本文后面提供的解决方案程序和下载代码中可见,这里不再详述)。

经过一系列测试,这些被Link的文件不会被编译到MyMvc.Controllers.Dll中,OKView的问题就此告一段落,剩下的工作由统一的解决方案来完成。

View新增

既然MyMvc.Controllers中的View都是没有实际意义的快捷方式,那么新增的文件显然也不能让他留在MyMvc.Controllers项目中,于是想到了“乾坤大挪移”——把新建的View自动移动到MyMvc.Web项目中,并且在MyMvc.Controllers.csproj中将对应的文件编程一个“快捷方式”。

最终解决方案——MvcScaffoldTool

MvcScaffoldTool是本人原创,并开源的一个用于解决之前提到的脚手架创建、查看文件路径问题的小工具,也同样适用于其它一些类型项目的其他地方。

既然要从外部入手,那么最直接、最自动的方法当然是写一个程序,用于自动“监控”MyMvc.Controllers中的状态,当其发生变化的时候自动转移文件并且处理对应的. Csproj文件。

由于很少做WinForm,所以粗制滥造了一个:

实现代码已经提供下载,这里不再占用大家宝贵的视野。

由于时间有限,暂时只做了一些基本的功能。原理大致如下:

指定Views(Web)Controllers项目文件夹及文件,监视Controllers项目文件对应节点的变动,当发现有新文件加入的时候,立即将该文件转移到Web项目下,并且修改两个项目的项目文件(.csproj),为Controllers项目中的对应文件建立“快捷方式”,同时为Web项目创建真实的文件包含。

除了以上这些基本功能,还有一些可以扩展和优化的地方,比如当Web下面删除一些View页面的时候,使Controllers中的快捷方式也同步删除,原理与上面类似。另外,MvcScaffoldTool自动隐藏后会运行在后台,目前只能通过“任务管理”将其关闭(此功能已升级)并缩小到系统开始栏的系统托盘:

双击该图表可以恢复操作窗口的显示,同时暂停监测。

注意:以上方法在VS2008(Sp1)和VS2010中测试成功,由于尚不知道VS2005及以下版本是否有办法安装官方提供的MVC RC及其“脚手架”,所以未作测试,也欢迎反馈。

MvcScaffoldTool的使用方法

MvcScaffoldTool的使用很简单:

1、将MvcScaffoldTool.exe复制到解决方案(一般即所有项目文件的上级目录):

2、在需要使用Controllers中的脚手架创建View页面之前运行MvcScaffoldTool.exe(和项目一起开启也可以),并选择正确的项目文件夹和项目文件:

程序会优先选择.csproj类型的文件作为项目文件,如果有多个,可以自行选择。

3、单击“确认并启动”按钮,MvcScaffoldTool.exe会创建一个xml文件并在后台运行。如果已经创建过xml文件,以上的设置会从xml文件中读取,不必每次开启都设置。

————————————————————————————————

接下去的操作和普通使用MVC RC开发几乎一模一样。

4、使用Controllers项目中的“脚手架”从Action中创建View页面:

5Controlllers下面会自动创建对应的View页面:

奇怪,怎么MvcScaffoldTool.exe怎么没有起作用呢?别急,请看下一步:

6、现在删除文件还来得及,如果确认添加,点击“全部保存”按钮:

7、不出意外地话,你马上会看到这样的提示:

以及:

不用担心,这说明修改已经成功,单击“重新加载”即可。

重新加载项目后的结果:

可以清楚地看到,MyMvc.Controllers下面刚才创建的.aspx文件已经转移到了MyMvc.Web下面,同时原来的文件成了一个快捷方式。点击这个快捷方式即可查看MvcScaffoldTool.aspx的原文件。

8、怎么还没有完?对!还有“Go to View”没有测试呢!

单击MvcScaffoldTool()中的Go to View

这时候虽然“解决方案管理器”中的当前文件是MyMvc.Controllers下面的一个快捷方式,不过打开的文件却是MyMvc.Web下真实的View文件:

MvcScaffoldTool更新日志

2009-2-27 v1.0.0
完成MvcScaffoldTool基本功能。

2009-2-27 v1.1.0
解决MvcScaffoldTool必须在“任务管理器”中关闭的问题。
使MvcScaffoldTool可以在开始栏中以托盘方式最小化,在后台运行。

2009-2-28 v1.2.0
添加为Views项目自动创建必要目录的功能,解决“重复重新加载”的bug。

以下下提供MvcScaffoldTool源代码及分离V-C的测试源代码:

v2.0.0.0版本exe文件下载:/Files/szw/MvcScaffoldTool-v2.0.0.0.rar

源代码SVN:http://mvc-scaffold-tool.googlecode.com/svn/trunk/

开源项目主页:http://code.google.com/p/mvc-scaffold-tool/

MyMvc 源代码

PS:写这篇文章旨在提供一种可行的思路,如果大家有什么更加好的方法也请不吝赐教!由于从发现问题到开发MvcScaffoldTool再到写这两篇文章总共只有3个多小时的时间,如此仓促肯定会有不少问题,不周之处望大家包涵,同时欢迎大家反馈:)

[转载]为ASP.NET MVC RC分离Controllers-Views项目后添加“脚手架”功能(一)(已添加MVC2.0及MVC3.0更新)

mikel阅读(968)

[转载]为ASP.NET MVC RC分离Controllers-Views项目后添加“脚手架”功能(一)(已添加MVC2.0及MVC3.0更新) – SZW – 博客园.

连续忙了好几个月,好久没有写东西了,最近稍微有点空,空闲的时候回到了对ASP.NET MVC RC(以下简称MVC RC)的研究上来。MVC RC的“脚手架(Scaffold)”功能可以说为MVC RC的开发如虎添翼,不过应用到真实的开发环境中似乎存在一些遗憾的地方:很多时候我们并不希望把ModelsViewsControllers放在同一个项目里面,而是把它们分离到不同的项目,然后由一个项目(比如Views)统一引用其他所有的项目程序集。但是这样做了以后,Controllers项目中脚手架的功能就“消失了”。

WebMVC RC)项目中,我们可以这样在Web项目中使用脚手架创建View页面:

或者创建Controller文件:

甚至可以在Controller文件中自动创建或者查看对应ActionView页面:

不过这一切经历了ViewsControllers的“生死离别”之后,就再也无法在Controllers中使用了(原因会在下文中说明)。下面我们来做一个测试。

首先我们先把MVC三层分离,比如这样:

需要注意的是当这样做的时候,在Controllers项目中需要引用以下这三个核心的程序集:

System.Web.Abstractions.dll

System.Web.Mvc.dll

System.Web.Routing.dll

以及一些在默认的Controller.cs文件中被引用到的命名空间,如:

System.Web

System.Configuration

以下是分离M-V-CM之所以也要分离出去是因为C需要引用M,而C不能引用V)之后,运行的默认界面,说明这样的分离是成功的:

OK,到此为止MVC工作正常,只不过……无法在MyMvc.Controllers中使用MVC的脚手架:

分析一下原因:我们在把Controller分离出来的时候是建了“类库(Class Library)”,一个普通的类库当然无法使用MVCWeb)的脚手架,于是我打开了MyMvc.Web的项目文件MyMvc.Web.csproj,用记事本打开这个文件(本质上是一个XML文件),发现了一个和其他项目与众不同的地方:

<ProjectTypeGuids>{603c0e0b-db56-11dc-be95-000d561079b0};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

这个节点中用分号分割了3段看似Guid的代码,分别是:

{603c0e0b-db56-11dc-be95-000d561079b0}

{349c5851-65df-11da-9384-00065b846f21}

{fae04ec0-301f-11d3-bf4b-00c04f79efbc}

立刻对比了一下MyMvc.Controllers下面的MyMvc.Controllers. csproj,发现没有这个节点。喜出望外!于是马上将这个节点复制到MyMvc.Controllers. csproj对应的地方,重新加载项目以后,发现MyMvc.Controllers的脚手架功能回来了!

不过通过这样简单的添加还是发现了一些小小的瑕疵:细观MyMvc.Controller的项目图标类型,怎么和Web的时一样的(下图)?不爽。

看来这样的直接复制是把整个MVC Web Application下面的类型都一起复制过来了,为了避免日后不必要的麻烦,还是需要让Controllers变回“类库”,于是打起了那三个Guid的注意,经过两次两两搭配后({fae04ec0-301f-11d3-bf4b-00c04f79efbc}一定要保留)发现,只要删除中间的{349c5851-65df-11da-9384-00065b846f21}之后,MyMvc.Controllers项目又回到了“类库”的状态:

于是猜测{fae04ec0-301f-11d3-bf4b-00c04f79efbc}对应了Web Application的项目类型,而{603c0e0b-db56-11dc-be95-000d561079b0}对应了MVC RC的模板。

注意:{603c0e0b-db56-11dc-be95-000d561079b0}对应了MVC RC(v1.0)的项目代码,

如果您使用的是MVC 2.0(包括Preview版本在内),对应的编码为:{F85E285D-A4E0-4152-9332-AB1D724D3325}

如果您使用的是MVC 3.0(包括Beta和RC版本在内),对应的编码为:{E53F8FEA-EAE0-44A6-8774-FFD645390401}


通过以上这些操作,分离了M-V-C结构,并且在Controllers“完整”保留了MVC RC的脚手架的功能。

上面的“完整”之所以要突出一下,是因为这种保留实在是太“完整”了,以至于在Controller中自动添加View页面会添加到Controller项目中,而不是我们所希望的MyMvc.View项目中(查看对应View页面也存在相同的问题)。这倒是个大问题,如果这个问题不解决,那么“脚手架”反而成了“绊脚石”,这个问题如何解决呢?

请看下一篇《ASP.NET MVC RC分离C-V项目后添加“脚手架”功能(二)

(本文的实例也在《ASP.NET MVC RC分离C-V项目后添加“脚手架”功能(二)》中提供下载。)