[缓存] 开源项目 ThumbCached

mikel阅读(816)

转载:http://www.cnblogs.com/domslab/archive/2009/06/29/1513564.html
http://www.cnblogs.com/domslab/archive/2009/06/29/1513514.html
版本:1.1.0
协议:New BSD license
平台:.Net 2.0/Mono 2.0
系统:Windows/Linux/BSD
ThumbCached 是一个简单高效的用于小数据的分布式缓存及储存系统(用于开发Web网站)。适用于缓存及储存数量庞大、个体容量小、读取频繁、需要持久化、非关键性的数 据,例如网站中的缩略图、用户自定义头像、已格式化的页面文本等。ThumbCached 还可以内存缓存模式运行,作用相当于Memcached,在速度上可以达到 Memcached 的70%左右。在 1.1 版本中新增加了嵌入的运行方式,适用于以非服务的形式嵌入到一个Web应用程序。
功能特点:
* 使用一个文件来储存数据,可以避免过多的细小文件所造成磁盘空间浪费,同时方便数据的转移和备份。
* 服务程序和应用程序可以分布于不同的服务器,适用于分布式的网站
* 数据的传输使用标准HTTP协议,方便多种程序语言(如ASP.NET,PHP等)访问。
* 能在一个服务实例中同时创建多个储存单元,以便于将不同类型的数据分开储存。

* 服务端使用高效的异步Socket技术。

下载地址:thumbcached.codeplex.com

 

Thumbcached 的程序结构图如下:

 

ThumbCached由HTTP Service、Cache item manager(上图中的 Buffer Pool)和StoreManager三部分组成,各部分的功能如下:
HTTP Service:接受及回答客户端的请求,传输数据;
Cache item manager:使用.Net的内存管理机制实现的缓冲,将被访问的cache项(以下称为“block”)缓存并加以管理,通过队列的方式将新增加或被更新的block项交给StoreManger;
StoreManager:持久化(储存)所有block的信息和二进制数据内容。

这样的设计能让读、写、更新数据的速度都比较快,因为常用的和新增加的缓存项都是放在pool里面的,而Store queue使用队列的方式将新增加的缓存项在后台持久化到文件。不过也是因为使用队列的原因,可能会导致pool的数据被删除而磁盘上的数据仍然存在的情 况,所以删除数据时是等待磁盘上的数据也删除完毕之后才返回的,因此删除操作比较慢。幸好在实际应用中很少删除操作。

在持久化(即储存)部分,使用了两个文件来配合实现,一个文件存block的信息,另外一个文件存block的数据。
block信息使用SQLite储存并对block的key作索引,这样查找block信息时速度很快,数据表设计如下:

 

第二个文件用于储存block数据,因为block数据是不定长的,所以第二个文件使用了自定义的数据结构,考虑到更新block数据时如果采用直 接丢弃旧的数据再新建一条记录这种方式会导致文件空间浪费,所以block数据的记录采用了覆盖旧记录加“链记录”的方式来实现。即每个记录都可以允许链 N块数据区域,这样能够最大限度地减少文件空间的浪费。下面是第二个文件的结构图:

 

储存数据之后,文件的内容会变成下图这样:

 

ThumbCached 使用HTTP协议跟应用程序通信,下面是协议的定义 :

 

添加缓存项:
—————————————————-
Request:
    POST /update/ITEM_KEY?expire=EXPIRATION_SECOND&abs=EXPIRATION_TYPE HTTP/1.1
    Host: HOSTNAME:PORT
    Content-Length: INTEGER
    Content-Type: THUMBCACHED_DATA_TYPE_NAME
    Last-Modified: GMT_DATE_TIME
    [\r\n]
    [BINARY_DATA]
    
ITEM_KEY: a string to name an item
EXPIRATION_SECOND: an integer number to specify the item expiration time (take effective in non-persistance mode only)
EXPIRATION_TYPE: a number '0' or '1', if it equal to '1' that means absolute expire.
THUMBCACHED_DATA_TYPE_NAME: combine with 'tcd/' and an integer number,
                enum DataTypeNumber
                {
                    Binary =0,
                    Object =1,
                    Boolean =3,
                    Int32 =9,
                    Int64 =11,
                    Single = 13,
                    Double = 14,
                    DateTime = 16,
                    String = 18,
                    EmptyByteArray = 256 + 6,
                    EmptyString = 256 + 18
                }
GMT_DATE_TIME: specify the item time, take effecive in persistance mode.
Response:
    HTTP/1.1 200 OK
    Content-Length: 0
    
获取一个缓存项:
—————————————————-
Request:
    GET /fetch/ITEM_KEY HTTP/1.1
    If-Modified_Since: GMT_DATE_TIME
    
GMT_DATE_TIME: specify one time to test the item last modified time, if the item
    does not modified since GMT_DATE_TIME, this request will return 304-Not Modified http status code.
Response:
    HTTP/1.1 200 OK
    Content-Type: THUMBCACHED_DATA_TYPE_NAME
    Content-Length: INTEGER
    Last-Modified: GMT_DATE_TIME
    [\r\n]
    [BINARY_DATA]
如果指定的key不存在相应的缓存项,则返回404 http代码。

一次获取多个缓存项:
—————————————————-
Request:
    GET /multifetch/?keys=KEY1,KEY2,KEY3… HTTP/1.1
Response:
    HTTP/1.1 200 OK
    Content-Length: INTEGER
    [\r\n]
    KEY1
    THUMBCACHED_DATA_TYPE_NAME
    GMT_DATE_TIME
    DATA_LENGTH
    [BINARY_DATA]
    [\r\n]
    KEY2
    THUMBCACHED_DATA_TYPE_NAME
    GMT_DATE_TIME
    DATA_LENGTH
    [BINARY_DATA]
    [\r\n]
    KEY3
    ……
    
如果指定的key当中有其中的一个或多个没有对应的缓存项,则不返回改key的数据。
删除一个缓存项:
—————————————————-
Request:
    GET /remove/ITEM_KEY HTTP/1.1
    
Response:
    HTTP/1.1 200 OK
    
获取服务器状态:
—————————————————-
Request:
    GET /status    HTTP/1.1
    
Response:
    HTTP/1.1 200 OK
    Content-Type: text/plain
    Content-Length: INTEGER
    [\r\n]
    Pool memory: INTEGER
    Used memory: INTEGER
    Item amount: INTEGER
    Item hits: INTEGER

[创意]移动物联网 之 家电节能 (2)

mikel阅读(1073)

    本系列文章结合时下正热的“物联网”概念,介绍实现“家电节能”的一套解决方案。本部分讲述 “家电节能”的具体实现方法。

 

1. 系统结构

系统包括Sensor NodeAccess NodeServer这三个主要组成部分。各部分的主要功能如下:

a. Sensor Node

负责电量采集,包括电压、电流和功耗等物理量,将模拟量转换为数字量,传送给Access Node;同时,Sensor Node可以接收Access Node发送的控制信息,对设备进行控制。

b. Access Node

负责接收Sensor Node发送的信息,并将这些信息发送给Server;同时,Access Node可以接收Server的控制信息,转发给对应的Access Node

c. Server

    提供UI,负责参数采集命令,将接收到的数据存入本地数据库;接收用户对各Sensor Node的阈值设置和控制指令。同时,Server也提供接口,供其他互联网设备访问。

 

2. 系统实现

2.1 组网方式

基于目前短距离无线通信的现状,ZigbeeRF具有各自的技术特点。Zigbee通信距离中等,抗干扰能力强,性能可靠稳定;采用IEEE802.15.4,处于全球公用的2.4G频段;组网灵活、方便,星型、树型或者Mesh网络;低功耗,低复杂度;多信道、多速率。RF通信距离远,穿透性好,抗干扰能力强;微发射功率,微功耗。 因此,我们使用了基于ZigbeeRF射频的家电节能整体解决方案。系统采用分布式网络,底层电量采集使用RF射频通信,上层使用Zigbee进行组网。由于Zigbee的穿透能力不强,所以在部署的时候,尽量将Zigbee模块放在视距范围内,不要有墙体阻隔。

 

从网络规模上来看,可以分为小型网络和中大型网络这两种。在小型网络中,主机只需要插座的地址就可以与插座通信;Zigbee采用广播通信方式,实现主机与插座信息的透明传输;整个网络最多包含254个插座。 在中大型网络中,采用主从通信模式,以Zigbee节点作为中继,实现主机和插座之间的信息传递;每个Zigbee节点可以包含254个插座,而整个网络可以包含多个Zigbee节点组成的子网络。

 

2.2 Sensor NodeAccess Node

Sensor Node负责电量采集,内部包括AD模块,将模拟量转换为数字量,通过无线的方式传送给Access NodeAccess Node起到一个透明传输的作用,将信息传送给Server端。由于目前大多数家电的控制接口都不公开,因此,比较通用的解决方法是将Sensor Node嵌于插座中,通过插座来检测用电情况。

2.3 Server

Server可以采用成本比较低的嵌入式设备,也可以采用PC机。在我们的项目中,我们使用了PC机,利用PC机的USB口和Access Node通信。需要说明的是,PC机的USB口是通过USB转串口模块和Access Node进行串口通信的。

Server通过串口发送数据采集指令,经Access Node转发给Sensor Node。得到数据以后,存入到本地Access数据库,然后通过曲线图,实时显示采集的数据量。界面如下:

关键代码如下:

打开串口部分

代码

            //Error Check
            StringBuilder strErrorMsg = new StringBuilder();
            
if (textBoxComNo.TextLength == 0)
            {
                strErrorMsg.Append(
"请输入串口号!\n");
            }
            
if (textBoxDataRate.TextLength == 0)
            {
                strErrorMsg.Append(
"请输入串口速率!\n");
            }
            
if (strErrorMsg.Length > 0)
            {
                MessageBox.Show(strErrorMsg.ToString(), 
"Error");
                
return;
            }
            
//State check
            if (!PortOpen)
            {
                m_port 
= "COM" + this.textBoxComNo.Text;
                m_serialPort 
= new System.IO.Ports.SerialPort(m_port, Int32.Parse(this.textBoxDataRate.Text.Trim()));
                m_serialPort.Parity 
= Parity.None;
                m_serialPort.StopBits 
= StopBits.One;
                m_serialPort.ReceivedBytesThreshold 
= 11
                interfaceUpdataHandle 
=   new HandleInterfaceUpdataDelegate(ReceivedDataProcess);  
                m_serialPort.DataReceived 
+= new SerialDataReceivedEventHandler(this.m_serialPort_DataReceived);
                
try
                {
                    m_serialPort.Open();
                    
                }
                
catch (Exception e1)
                {
                    System.Console.WriteLine(e1.Message);
                }
                PortOpen 
= true;
                
this.buttonOpenCom.Text = "关闭串口";
            }
            
else
            {
                
if (StartInt)
                {
                    MessageBox.Show(
"请先停止采集!""Error");
                    
return;                                
                }
                m_serialPort.Close();             
                PortOpen 
= false;
                
this.buttonOpenCom.Text = "打开串口";
            }

 

串口数据处理部分

代码

            string str = ByteArrayToHexString(message);
            
//check to claer the message window
            if (listcounter == 23)
            {
                listcounter 
= 0;
                
this.textBoxComMessage.Text = "";
            }
            
//display on message window
            this.textBoxComMessage.Text += "RX:" + str + "\r\n";
            listcounter
++;
            
if (message[2== 6)
            {
                str 
= BitConverter.ToString(message).Replace("""");
                pSave.Number 
= Hex2Ten(str.Substring(02));
                pSave.Voltage 
= Hex2Ten(str.Substring(64)) + "0";
                pSave.Current 
= Hex2Ten(str.Substring(104));
                pSave.Power 
= Hex2Ten(str.Substring(144));
            }
            
else if (message[2== 4)
            {
                
//add to database         
                str = BitConverter.ToString(message).Replace("""");
                
string stemp = Hex2Ten(str.Substring(68));
                
int itemp = Int32.Parse(stemp);
                
float b = 3200;
                
float ftemp = (float)itemp / b;
                pSave.Consum 
= ftemp.ToString();
                pSave.Infotime 
= System.DateTime.Now;
                
int id = ProductDao.insert(pSave);
                
//统计数据库数据并显示
                Page page = PageQueryDao.getProducts(0);
                
this.labelMsg.Text = String.Format("记录数: {0:d}, \n每页{1:d}条记录, {2:d}/{3:d}页",
                                page.TotalRecord, page.PageSize, page.CurPageIndex 
+ 1, page.TotalPage);
                totalPage 
= page.TotalPage;
                DisplayChart(pSave);
            }

 

 

图像实时显示部分采用ZedGraphZedGraph是用于创建任意数据的二维线型、条型、饼型图表的一个类库,也可以作为Windows窗体用户控件和ASP.NET网页控件。这个类库具有高度的适应性,几乎所有式样的图表都能够被创建。这个类库的用法在于通过提供所有图表属性的省缺值来保持使用性的简单。这个类库包含了基于要绘制的数值范围内的可选择适当度量范围和跨度的代码。关于如何使用ZedGraph,可以参考园子里peterzbBlogC# WinForm开发系列 – ZedGraph

 

为了便于网络上其他设备对Server的数据访问,Server端程序每隔一定时间将数据通过socket发送到其他设备。

 

Server端关键代码:

代码

public     void   OnTimedEvent(object   source,   ElapsedEventArgs   e) 
        {
            mdbget();
            BeginSend();
        }
        
public void send_Click(object sender, EventArgs e)
        {
            BeginSend();
        }
        
public  void BeginSend()
        {
            
string ip = "127.0.0.1";;
            
string port = "2000";
            IPAddress serverIp 
= IPAddress.Parse(ip);
            
int serverPort = Convert.ToInt32(port);
            IPEndPoint iep 
= new IPEndPoint(serverIp, serverPort);
            
byte[] byteMessage;
         
            Socket socket 
= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect(iep);
            byteMessage 
= Encoding.GetEncoding("gb2312").GetBytes(sendtext);
          
            socket.Send(byteMessage);
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
        }
        
private void getmdb_Click(object sender, EventArgs e)
        {
            
this.sensorTableAdapter.Fill(this.sensorDataSet.sensor);
            mdbget();
           
           
        }
        
public void mdbget()
        {
           
            String connectionString 
= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\Debug\\sensor.mdb";
            OleDbConnection connection 
= new OleDbConnection(connectionString);
            connection.Open();
            
string SQL = "select * from sensor";
            OleDbCommand cmd 
= new OleDbCommand(SQL, connection);
            OleDbDataReader rs 
= cmd.ExecuteReader();
            
int i = 0;
            sendtext 
= "";
            
            
while (rs.Read())
            {
                i
++;
                sendtext 
+= (rs[0].ToString() + "," + rs[1].ToString() + "," + rs[2].ToString() + "," + rs[3].ToString() + "," + rs[4].ToString() + "," + rs[5].ToString() + "," + rs[6].ToString() + "");
            }
           
            sendtext 
= i.ToString() + "" + sendtext.ToString();
            rs.Close();
            connection.Close();
            
        }
        
private void client_Load(object sender, EventArgs e)
        {
            
// TODO: 这行代码将数据加载到表“sensorDataSet.sensor”中。您可以根据需要移动或移除它。
            this.sensorTableAdapter.Fill(this.sensorDataSet.sensor);
           
           
            System.Timers.Timer aTimer 
= new System.Timers.Timer(30000);   //实例化Timer类,设置间隔时间为10000毫秒;
            aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);   //到达时间的时候执行事件
            
//   Only   raise   the   event   the   first   time   Interval   elapses.   
            aTimer.AutoReset = true;  //设置是执行一次(false)还是一直执行(true); 
            aTimer.Enabled = true;   //是否执行System.Timers.Timer.Elapsed事件; 
        }

 

其他设备端关键代码:

代码

rivate void server_Load(object sender, EventArgs e)
        {
            openserver();
            System.Timers.Timer aTimer 
= new System.Timers.Timer(30000);   //实例化Timer类,设置间隔时间为xx毫秒;
            aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);   //到达时间的时候执行事件
            
//   Only   raise   the   event   the   first   time   Interval   elapses.   
            aTimer.AutoReset = true;  //设置是执行一次(false)还是一直执行(true); 
            aTimer.Enabled = true;   //是否执行System.Timers.Timer.Elapsed事件;
        }
        
public void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            savedata();
        }
        
private void open_Click(object sender, EventArgs e)
        {
            openserver();
           
        }
        
public void openserver()
        {
            open.Enabled 
= false;
            
try
            {
                mythread 
= new Thread(new ThreadStart(BeginListen));
                mythread.Start();
            }
            
catch (System.Exception er)
            {
                MessageBox.Show(er.Message, 
"完成", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            } 
        }
        
private void BeginListen()
        {
            
string host = "127.0.0.1";
           
            IPAddress ServerIp 
= IPAddress.Parse(host);
            IPEndPoint iep 
= new IPEndPoint(ServerIp,2000);
            socket 
= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
byte[] recvBytes = new byte[65536];
            
            
this.label1.Text = iep.ToString();
            socket.Bind(iep);
            
while (true)
            {
                
try
                {
                    socket.Listen(
5);
                    Socket newSocket 
= socket.Accept();
                    
int bytes;
                    bytes 
= newSocket.Receive(recvBytes, recvBytes.Length, 0);
                    
string sTime = DateTime.Now.ToShortTimeString();
                    
string msg = sTime + ":" + "Message from:";
                    msg 
+= newSocket.RemoteEndPoint.ToString()+":" +Encoding.GetEncoding("gb2312").GetString(recvBytes, 0, bytes);// Encoding.ASCII.GetString(recvBytes, 0, bytes);
                    textBox1.Text = "";
                    textBox1.Text 
= Encoding.GetEncoding("gb2312").GetString(recvBytes, 0, bytes);
                    
this.listBox1.Items.Add(msg);
                    
                }
                
catch (SocketException ex)
                {
                    
this.label1.Text += ex.ToString();
                }
            }
           
        }
        
private void save_Click(object sender, EventArgs e)
        {
            savedata();
           
            
        }
        
public void savedata()
        {
            
string info = textBox1.Text.ToString();
            
if (info != "")
            {
               
                
string[] record = info.Split('');
                
int i;
                
int all = int.Parse(record[0].ToString());
                
//StreamWriter sw = new StreamWriter("data.txt", false);
                String connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:\\sensor.mdb";
                OleDbConnection connection 
= new OleDbConnection(connectionString);
                connection.Open();
                
string SQL;
                sql 
= "delete * from sensor";
                OleDbCommand cmd 
= new OleDbCommand(sql, connection);
                cmd.ExecuteNonQuery();
              
for (i = 1; i <= all; i++)
                {
                    
string[] str = record[i].Split(',');
                    sql 
= "insert into sensor  values (" + str[0+ ",'" + str[1+ "','" + str[2+ "','" + str[3+ "','" + str[4+ "','" + str[5+ "','" + str[6+ "')";
                    cmd 
= new OleDbCommand(sql, connection);
                    cmd.ExecuteNonQuery();
                }
                connection.Close();
               
// MessageBox.Show("执行成功");
                
            }
           
/* else
               MessageBox.Show("还没有数据,无法存储");
*/
        }

 

 

 

Server端软件视频已经上传至youku,地址:http://v.youku.com/v_show/id_XMTQwNDcwNDQ4.html

 

参考链接:

C# WinForm开发系列 – ZedGraph

http://baike.baidu.com/view/117166.htm

 

 

声明和致谢:本项目由北京邮电大学微软技术俱乐部成员施炯、曾阳和叶周全完成,感谢微软亚洲研究院对本项目的资金支持,感谢MSRA UR李贝、张静和王春晖。

[MVC]利用Asp.net MVC处理文件的上传下载

mikel阅读(898)

如果你仅仅只有ASP.NET Web Forms背景转而学习ASP.NET MVC的,我想你的第一个经历或许是那些曾经让你的编程变得愉悦无比的服务端控件都驾鹤西去了.FileUpload就是其中一个,而这个控件的缺席给我 们带来一些小问题。这篇文章主要说如何在ASP.NET MVC中上传文件,然后如何再从服务器中把上传过的文件下载下来.

在Web Forms中,当你把一个FileUpload控件拖到设计器中,你或许没有注意到在生成的HTML中会在form标签中加入一条额外属性 enctype="multipart/form-data". 而FileUpload控件本身会生成为<input type=”file” />,在MVC的view里,有许多种方法可以做到同样效果,第一种的HTML如下:

 

<form action="/" method="post" enctype="multipart/form-data">
<input type="file" name="FileUpload1" /><br />
<input type="submit" name="Submit" id="Submit" value="Upload" />
</form>

注意form标签已经包括了enctype标签,而method属性则设为”post”,这样设置并不多于因为默认的提交时通过HTTP get方式进行的。下面这种方式,使用Html.BeginForm()扩展方法,会生成和上面同样的HTML:

 

 

<%
using (Html.BeginForm("", "home", FormMethod.Post, new {enctype="multipart/form-data"}))
{%>
<input type="file" name="FileUpload1" /><br />
<input type="submit" name="Submit" id="Submit" value="Upload" />
<% }%>

注意<input type=”file”>标签的name属性,我们在后面再讨论,上面代码会如下图:

OK,现在我们可以浏览本地文件然后通过Upload提交按钮将文件提交到服务器端,下一步就是在服务器端处理上传的文件,在使用 fileUpload控件时,你可以很轻松的通过FileUpload的hasFile方法来查看文件是否被上传。但是在ASP.NET MVC中貌似就不是这么方便了,你会和原始的HTTP更接近一些,然而,一个扩展方法可以处理这些:

public static bool HasFile(this HttpPostedFileBase file)
{
return (file != null && file.ContentLength > 0) ? true : false;
}

当你看到对应的Controller类的代码时,你会发现Request对象作为HttpRequestBase类型的一个属性存在。 HttpReuqestBase其实是HTTP请求的一个封装,暴漏了很多属性,包括Files collection(其实是HttpFileCollectionBase的集合),在集合中的每一个元素都是HttpPostedFileBase的 集合,扩展方法是用于确保上传的文件是否存在。实际上,这和FileUpload.HasFile()方法的工作原理一致。

在Controller Action中使用起来其实很容易:

public class HomeController : Controller
{
public ActionResult Index()
{
foreach (string upload in Request.Files)
{
if (!Request.Files[upload].HasFile()) continue;
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
string filename = Path.GetFileName(Request.Files[upload].FileName);
Request.Files[upload].SaveAs(Path.Combine(path, filename));
}
return View();
}
}

 

多文件上传

或许你已经比我更早的想到如何更好的将Request.Files作为一个集合使用。这意味着它不仅仅只能容纳一个文件,而能容纳多个,我们将上面的View改为如下:

<%
using (Html.BeginForm("", "home", FormMethod.Post, new {enctype="multipart/form-data"}))
{%>
<input type="file" name="FileUpload1" /><br />
<input type="file" name="FileUpload2" /><br />
<input type="file" name="FileUpload3" /><br />
<input type="file" name="FileUpload4" /><br />
<input type="file" name="FileUpload5" /><br />
<input type="submit" name="Submit" id="Submit" value="Upload" />
<% }%>

效果如下:

在Controller的代码中已经检查了是否所有的文件上传框中都有文件,所以即使对于多文件上传,我们也不再需要修改Controller的代 码,注意每一个<input type=”file”>都有不同的name属性,如果你需要调用其中一个,比如说,你需要引用第三个输入框只需要使 用:Request.Files["FileUpload3"].

 

存入数据库

在你冲我狂吼”关注点分离”之前,我想声明下面的代码仅仅用于作为说明功能.我将ADO.Net的代码放入Controller action中,但我们都知道,这并不好。数据访问的代码应该放在Model中某个部分的数据访问层中.但是,下面这段代码仅仅可以给大家怎样将上传的文 件存入数据库中一个更直观的印象,首先,我们需要创建一个数据表(FileTest)并创建一个表:FileStore

Create TABLE [dbo].[FileStore](
[ID] [int] IDENTITY(1,1) NOT NULL,
[FileContent] [image] NOT NULL,
[MimeType] [nvarchar](50) NOT NULL,
[FileName] [nvarchar](50) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

FileContent域是image数据类型,用于存储以二进制数据形成的文件,而Index Action改为:

public ActionResult Index()
{
foreach (string upload in Request.Files)
{
if (!Request.Files[upload].HasFile()) continue;
string mimeType = Request.Files[upload].ContentType;
Stream fileStream = Request.Files[upload].InputStream;
string fileName = Path.GetFileName(Request.Files[upload].FileName);
int fileLength = Request.Files[upload].ContentLength;
byte[] fileData = new byte[fileLength];
fileStream.Read(fileData, 0, fileLength);
const string connect = @"Server=.\SQLExpress;Database=FileTest;Trusted_Connection=True;";
using (var conn = new SqlConnection(connect))
{
var qry = "Insert INTO FileStore (FileContent, MimeType, FileName) VALUES (@FileContent, @MimeType, @FileName)";
var cmd = new SqlCommand(qry, conn);
cmd.Parameters.AddWithValue("@FileContent", fileData);
cmd.Parameters.AddWithValue("@MimeType", mimeType);
cmd.Parameters.AddWithValue("@FileName", fileName);
conn.Open();
cmd.ExecuteNonQuery();
}
}
return View();
}

修改后的代码会以循环的方式遍历Web页面中所有的上传文件,并检查<input type=”file”>中是否已经加入文件,然后,从文件中提取出3个信息:文件名,MIME类型(文件的类型),HTTP Request中的二进制流。二进制数据被转换为byte数组,并以image数据类型存入数据库。MIME类型和文件名对于用户从数据库中提取文件来说 非常重要。

将数据库中的文件返回给用户:

你如何将文件传送给用户取决于你最开始如何存储它,如果你将文件存入数据库,你会用流的方式将文件返还给用户,如果你将文件存在硬盘中,你只需要提 供一个超链接即可,或者也可以以流的方式。每当你需要以流的方式将文件送到浏览器中,你都的使用到File()方法的重载(而不是使用我们先前一直使用的 View()方法),对于File()方法有3类返回类型:FilePathResult,FileContentResult和 FileStreamResult,第一种类型用于直接从磁盘返回文件;第二种类型用于将byte数组返回客户端;而第三种方式将已经生成并打开的流对象 的内容返回客户端。

如果你还记得的话,我们将上传的文件存入了数据库,并以byte数组的形式存入FileContent域内.而当需要提取时,它仍然会以一个 byte数组进行提取,这意味着我们使用返回FileContentResult的File()重载,如果我们想让提取的文件名更有意义,我们使用接受3 个参数的重载,三个参数是:byte数组,MIME类型,文件名:

public FileContentResult GetFile(int id)
{
SqlDataReader rdr; byte[] fileContent = null;
string mimeType = "";string fileName = "";
const string connect = @"Server=.\SQLExpress;Database=FileTest;Trusted_Connection=True;";
using (var conn = new SqlConnection(connect))
{
var qry = "Select FileContent, MimeType, FileName FROM FileStore Where ID = @ID";
var cmd = new SqlCommand(qry, conn);
cmd.Parameters.AddWithValue("@ID", id);
conn.Open();
rdr = cmd.ExecuteReader();
if (rdr.HasRows)
{
rdr.Read();
fileContent = (byte[])rdr["FileContent"];
mimeType = rdr["MimeType"].ToString();
fileName = rdr["FileName"].ToString();
}
}
return File(fileContent, mimeType, fileName);
}

在View中最简单的使用来使用这个Action只需提供一个超链接:

<a href="/GetFile/1">Click to get file</a>

如果在数据库中存储的图片是图片类型,和使用超链接不同的是,我们通过指向Controller action的一个带有src属性的<image>标签来获取:

<img src="/GetFile/1" alt="My Image" />

下面再让我们来看看使用FilePathResult(用于从硬盘提取文件)是多简单的事:

public FilePathResult GetFileFromDisk()
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
string fileName = "test.txt";
return File(path + fileName, "text/plain", "test.txt");
}

而这也可以用过超链接提取:

<a href="/GetFileFromDisk">Click to get file</a>

而最后一个选择FileStreamResult也可以从磁盘中提取文件:

public FileStreamResult StreamFileFromDisk()
{
string path = AppDomain.CurrentDomain.BaseDirectory + "uploads/";
string fileName = "test.txt";
return File(new FileStream(path + fileName, FileMode.Open), "text/plain", fileName);
}

FilePathResult和FileStreamResult的区别是什么?我们又该如何取舍呢?主要的区别是FilePathResult使 用HttpResponse.TransmitFile来将文件写入Http输出流。这个方法并不会在服务器内存中进行缓冲,所以这对于发送大文件是一个 不错的选择。他们的区别很像DataReader和DataSet的区别。于此同时, TransmitFile还有一个bug,这可能导致文件传到客户端一半就停了,甚至无法传送。而FileStreamResult在这方面就很棒了。比 如说:返回Asp.net Chart 控件在内存中生成的图表图片,而这并不需要将图片存到磁盘中.

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

原文链接:http://www.mikesdotnetting.com/Article/125/ASP.NET-MVC-Uploading-and-Downloading-Files

[Memcached]Memcached (Distributed Cache) ASP.net P

mikel阅读(928)

转载:http://www.codeproject.com/KB/aspnet/memcached_aspnet.aspx

Introduction

Currently I am working on a web-based project that will be hosted in a web farm. Among the several issues, we faced the issue of having a distributed cache. As we know the current cache in ASP.NET is an in-process cache and can't be used in a web farm. After doing some research found a few solutions on the web but at the end all of them had scalability issues. We found a few third party implementations but they were quite expensive. Then I came across memcahced implemented by Danga Interactive. This is a high performance distributed memory cache initially implemented for http://www.LiveJournal.com. The original implementation runs on *nix system. Luckily there is win32 port available at http://jehiah.cz/projects/memcached-win32/ for those who want to run it in a windows environment. For more detail on the working of Memcached, please read the following article http://www.linuxjournal.com/article/7451.

There is a C# client for memcached available and can be downloaded from the following location https://sourceforge.net/projects/memcacheddotnet/. For coding this provider, I have used the clientlib 1.1.5.

Using the code

Following is the interface for the Cached Provider.

Collapse
public abstract class CacheProvider : ProviderBase
{
// Currently returns -1 as this property is not implemented
public abstract long Count { get;}
// Returns the server names with port in the memcached cluster
public abstract string[] Servers { get;}
// Default expiration time in milli seconds
public abstract long DefaultExpireTime { get;set;}
// Adds object to the cache for the max amount of time
public abstract bool Add(string strKey, object objValue);
// Adds object to the cache for Default Expire time if bDefaultExpire
// is set to true otherwise for max amount of time
public abstract bool Add(string strKey, object objValue,bool bDefaultExpire);
// Add objects for specified about of time. Time is specified in milli seconds        
public abstract bool Add(string strKey, object objValue, long lNumofMilliSeconds);
// Return the object from memcached based on the key otherwise 
// just returns null
public abstract object Get(string strKey);
// Clears everything from the cache on all servers
public abstract bool RemoveAll();
// Removes the an object from cache
public abstract object Remove(string strKey);
// Release resources and performs clean up
public abstract void Shutdown();
// Checks if the key exists in memory
public abstract bool KeyExists(string strKey);
// Return all servers stats
public abstract Hashtable GetStats();
}

Following is how to configure Cache Provider in a web.config or app.config file

Collapse
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="cacheProvider" type="CacheProvider.CacheProviderSection, CacheProvider"
allowDefinition="MachineToApplication" restartOnExternalChanges="true"/>
</configSections>
<cacheProvider defaultProvider="MemcachedCacheProvider">
<providers>
<add name="MemcachedCacheProvider" type="CacheProvider.MemcachedCacheProvider,CacheProvider"
servers="127.0.0.1:11211" socketConnectTimeout="1000" socketTimeout="1000"/>
<!�To add more servers use comma to separate servernames and ports
eg. servers="127.0.0.1:11211,192.168.0.111:11211"
-->
</providers>
</cacheProvider>
</configuration>

Following parameters can be specified for initializing the cache provider

  • socketConnectTimeout � Timeout for Socket connection with memcached servers
  • socketTimeout � Timeout for Socket read-write operations
  • defaultExpireTime � Default expire time in milliseconds

To use Cache Provider in a project add a reference to the CacheProvider.dll and access the methods using DistCache static class. When creating keys for storing data, avoid using spaces in keys e.g. "My Key". This is an issue with C# memcached client. To close the provider gracefully please add a call to DistCache.Shutdown() in Global.asax file's Application_End event.
Update Dec 24, 2007
To report any issues and enhancements please go to the following link http://www.codeplex.com/memcachedproviders. I have started working on the Session State Provider for Memcached. I will be releasing it very soon on codeplex.com.

Update Dec 31, 2007I have released Session State Provider for memcached. Please go to the following link to download
http://www.codeplex.com/memcachedproviders

References

  1. http://www.danga.com/memcached/
  2. http://jehiah.cz/projects/memcached-win32/
  3. https://sourceforge.net/project/showfiles.php?group_id=152153
  4. Sample Project Provider by Memcached C# client 1.1.5
  5. http://www.infoq.com/news/2007/07/memcached
  6. http://msdn2.microsoft.com/en-US/library/aa479038.aspx
  7. http://www.linuxjournal.com/article/7451

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

About the Author

Fahad Azeem

Member
My name is Fahad Azeem. I am interested in distributed software development. Currently I am working for a software consulting company in Chicago which developes software in .NET platform.
My Blog: http://fahadaz.blogspot.com

Occupation: Web Developer
Location: United States United States

[ASP.NET]超级简单:ASP.NET Localization (本地化,多语言)

mikel阅读(784)

      有很多讨论ASP.NET localization(本地化,多语言)的文章,这篇文章是不会的深入讨论ASP.NET Localization (本地化,多语言)。相反,它将给你有关ASP.NET页面中通常使用的内容的localization的一个快速参考,这些内容包括:ASP.NET服 务端控件,html内容,SiteMap,一些其他资源。

      这篇文章包括以下内容:

      1、 如何本地化的ASP.NET服务器控件?

      2、如何本地化HTML内容?

      3、如何本地化站点地图?

      4、如何动态改变文化环境?

     如何本地化的ASP.NET服务器控件?

     ASP.NET服务器控件的localization是所有类型中最简单的一种。一旦你添加一个服务器控件到你的页面上,您可以简单地切换网页到“Design”,然后进入菜单 “Tools”->“Generate Local Resource”。

     它将会为页面上的每个asp.net服务器控件产生一段字符串资源。在这个例子中,创建一个名字为Default.aspx.resx 的文件。它包含了Default.aspx页面所有的资源的name/value对。

   切换回"Source",你将看到在你的html里面添加了如下的代码:

<asp:Button ID="Button1" runat="server" Text="Hello" 
    meta:resourcekey
="Button1Resource1" />

     然后,您可以复制/粘贴,为了创建另一个文化资源文件。例如,您可以为法国用户创建一个Default.aspx.fr.resx。 

 

     下面的截图是当Internet Explorer的语言设置为英语。

 

      通过Internet Explorer->Tools->Internet Options->Languages更改语言为法语. 

 

     下面是为法语版本的页面:

 

   如何本地化HTML内容?

    为了本地化普通的HTML内容,您可以使用<asp:Localize>控件。让我们用一个例子来解释。

    在您的网页,你有一个标题和段落。

<h1>Localization Page Header</h1>
<p>This is a demo page to show you how to do localization in ASP.NET</p>

     为了本地化,您需要给他们添加<asp:Localize>。

代码

<h1><asp:Localize ID="Header" runat="server">Localization Page Header</asp:Localize></h1>
<p><asp:Localize ID="Localize1" runat="server">This is a demo 
   page to show you how to do localization in ASP.NET
</asp:Localize></p>

 

      然后,您可以手动创建一个资源文件并添加meta:resourcekey =“HeaderResource1”到您的网页。您可以利用Visual Studio,它自动生成。切换页面到“Design”,进入菜单“Tools”->“Generate Local Resource”。

      它将为您生成以下代码和资源文件(例如:yourfile.aspx.resx)。

      下面是它更改的代码:

代码

<h1><asp:Localize ID="Header" runat="server" 
   meta:resourcekey
="HeaderResource1" 
   Text
="Localization Page Header"></asp:Localize></h1>
<p><asp:Localize ID="Localize1" runat="server" 
   meta:resourcekey
="Localize1Resource1" Text="This is a demo page to show you 
      how to do localization in ASP.NET"
></asp:Localize></p>

     在其余步骤是与如何本地化ASP.NET服务器控件相同。

   如何本地化SiteMap?

     本文将给你关于SiteMap的本地化更多的细节。

     1、通过加入enableLocalization =“true”,启用站点地图文件网站地图本地化。例如:在Web.sitemap文件。

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"
    enableLocalization
="true">

   2、 在SiteMapNode中使用这种格式: “$resources:ClassName,KeyName,DefaultValue”,更改你要本地化的资源字符串的属性的值。

<siteMapNode url="Default.aspx" 
  title
="$resources:SiteMapLocalizations,HomePageTitle" 
  description
="$resources:SiteMapLocalizations,HomePageDescription">

    3、 添加 global resource 文件夹和文件。右键单击在解决方案资源管理器的web site 。点击“添加ASP.NET文件夹”。从子菜单点击“添App_GlobalResources文件”。这将增加一个 “App_GlobalResources文件”文件夹到根目录。

然后,添加一个资源文件,例如,SiteMapLocalizations.resx。在文件中,您将有资源字符串的 name/value 对。例如:

Name             Value
HomePageTitle    Home

 4、为每一种你需要的文化,添加资源文件。例如,您可能有一个名字为SiteMapLocalizations.fr.resx法语版的文件。下面是英文和法文的菜单屏幕截图:

 

如何编程本地化字符串?

有时您可能需要显示一个字符串,例如,在运行时错误信息。您需要编程将它本地化。下面是如何做到这一点:

1、在App_GlobalResources 文件夹下面添加一个资源文件。例如,我们能添加一个名字为“CommonResource.resx”文件和一个法语版的“CommonResource.fr.resx”。

2、有很多其他很好的方法来获取资源字符串。

你能从 ~\App_GlobalResources\MyMessages.resx 得到的资源通过:

1、产生的封装代码 :

string message = Resources.MyMessages.Hello;

 2、资源表达式

<asp:Label Text="<%$ Resources: MyMessages, Hello %>" />

3、GetGlobalResourceObject方法

string message = GetGlobalResourceObject("MyMessages""Hello");

你能从 ~\App_LocalResources\default.aspx.resx 得到的资源通过: 

1、资源表达式:

<asp:Label Text="<%$ Resources: Hello %>" />

2、meta:resourceKey :

<asp:Label meta:resourceKey="labelResourceKey" />

 3、GetLocalResourceObject方法:

string message = GetLocalResourceObject("Hello"); "

 这有个截图:

  

 如何动态改变文化?

    在示例代码中,我们增加了两个LinkButtons控件。当您点击“English”,该网页将切换到英语文化,点击 “Français”, 将切换到法国文化。为了检测的哪个LinkButtons被点击,我们使用Request.Form [“__EventTarget”]。它能实现,但可能不是最好的方式。

    我们覆盖页面InitializeCulture方法,这样就会对我们作出的选择显示正确的文化。

<asp:LinkButton ID="LanguageEnglish" Text="English" runat="server"></asp:LinkButton>
<asp:LinkButton ID="LanguageFrench" Text="Français" runat="server"></asp:LinkButton>

 

代码

protected override void InitializeCulture()
{
    
string language = Request.Form["__EventTarget"];
    
string languageId = "";
    
if (!string.IsNullOrEmpty(language))
    {
        
if (language.EndsWith("French"))
            languageId 
= "fr-FR";
        
else languageId = "en-US";
        Thread.CurrentThread.CurrentCulture 
= 
        CultureInfo.CreateSpecificCulture(languageId);
        Thread.CurrentThread.CurrentUICulture 
= new CultureInfo(languageId);
    }
    
base.InitializeCulture(); 
}

 

   除了menu之外,其它一切控件都有效。如果在网页中你有一个menu使用Sitemap,您可能需要在Page_Load事件调用 menu.DataBind()或设置的EnableViewState为false。否则,但其他内容更改为新的文化,你的menu上仍会显示以前的文 化字符串。

   当我们动态改变文化,最有可能的是我们需要将它存储在某个地方,当我们从页面到页面跳转时,这样就不会丢失。在示例应用程序,我们使用了Session。

源代码:

/Files/zhuqil/Localization.zip

原文:http://www.codeproject.com/KB/aspnet/AspNetLocalization.aspx

朱祁林

[Linq]非常简单的实现LINQ通用分页绑定方法

mikel阅读(911)

转载:http://www.cnblogs.com/ejiyuan/archive/2009/12/22/1629806.html

在LINQ中,IQueryable <T>接口和IEnumerable <T>接口都分别提供了Skip方法和Take方法,用来做分页非常合适.因此我就想用他们做一个分页控件,因为IQueryable <T> 是继承自 IEnumerable <T> 的。因此使用接口仅需要针对后者就可以了。使用的时候只需提供数据源、绑定的GridView的、每页大小即可。现在问题就出了在数据源上,要求用户提供 一个数据源类型,即IQueryable <T>接口和IEnumerable <T>接口? T是可确定类型(已知类型)的话还可以,若T是匿名类型,如 

var list = from it in de.Customers where it.City == "abc" select new { it.City, it.Country }; 

 

 list的类型只有在运行时才能得到,怎么办呢!其实很简单我,我们可以使用 “参数推导泛型类型”的方法来实现:
看下面的代码(因为重点不在这里所以 代码写的比较粗糙):

代码

public void BindBoundControl<TSource>(IEnumerable<TSource> DataSource, GridView BoundControl, int PageSize)
        {
            
//获取总记录数(这里可以使用参数传入总页数 就不必每次都执行下面方法)
            int totalRecordCount = DataSource.Count();
            
//计算总页数
            int totalPageCount = 0;
            
if (PageSize == 0)
            {
                PageSize 
= totalRecordCount;
            }
            
if (totalRecordCount % PageSize == 0)
            {
                totalPageCount 
= totalRecordCount / PageSize;
            }
            
else
            {
                totalPageCount 
= totalRecordCount / PageSize + 1;
            }
            
//从参数中获取当前页码
            int CurrentPageIndex = 1;
            
//如果从参数中获取页码不正确 设置页码为第一页
            if (!int.TryParse(HttpContext.Current.Request.QueryString["Page"], out CurrentPageIndex) || CurrentPageIndex <= 0 || CurrentPageIndex > totalPageCount)
            {
                CurrentPageIndex 
= 1;
            }
            
//绑定数据源
            BoundControl.DataSource = DataSource.Skip((CurrentPageIndex  1* PageSize).Take(PageSize);
            BoundControl.DataBind();
        }

 

        调用

 

代码

        protected void Page_Load(object sender, EventArgs e)
        {
            NorthwindEntities de 
= new NorthwindEntities();
            BindingUtils bind 
= new BindingUtils();
            
//先排序与一下再绑定
            bind.BindBoundControl<Customers>(de.Customers.OrderBy(v=>v.CustomerID), this.GridView1, 10);  
        }

 

下面我们只是需要重载一下我们的分页方法实现“参数推导泛型类型”就可以了 代码如下:

代码

        public void BindBoundControl<TSource>(IEnumerable<TSource> DataSource, TSource type, GridView BoundControl, int PageSize)
        {
            
this.BindBoundControl(DataSource, BoundControl, PageSize);
        }

 

调用

代码

        protected void Page_Load(object sender, EventArgs e)
        {
            NorthwindEntities de 
= new NorthwindEntities();
            var list 
= from it in de.Customers where it.City == "abc" select new { it.City, it.Country };
            BindingUtils bind 
= new BindingUtils();
            bind.BindBoundControl(list.OrderBy(c
=>c.City), list.FirstOrDefault(), this.GridView1, 10);  
        }

 

这个方法很简单的 只是通过 list.FirstOrDefault() 做参数 来推导 方法中 BindBoundControl<TSource> 的TSource 就可以了,当然因为每次分页时都会执行 list.FirstOrDefault() 会损失一点点的效率。

 

[C#]动软代码生成器源码下载地址

mikel阅读(1617)

动软代码生成器源码下载页面
http://www.maticsoft.com/download.aspx

动软.Net代码生成器(.Net 2.0版)源码

软件版本: 2.41
最后更新: 2009-12-22
软件大小: 13M
软件性质: 简体中文/免费软件
运行环境: .Net 2.0
下载次数: 194
软件描述:

        动软.Net代码生成器2.41版最新全部源码开放!

        转眼间,动软经历了几年的风风雨雨,得到了众多网友们的支持和厚爱。同时也结识了很多的朋友,虽然几年的update很辛苦,但有这么多朋友的支持和鼓励,也甚感欣慰。
为 感谢众多网友对该软件的支持,现在正式开放源码。希望它能带给大家和更多的朋友更大的帮助,也不枉这么多年开发动软的心血。虽然对动软有着深厚的感情,但 为了更好的发挥动软.NET代码生成器更大的社会价值,也为更多的程序员提供更大的便利性和灵活性。也当作我对同行朋友们新年的一点贺礼,希望大家在未来 的工作生活中,快乐工作,享受生活!

    本地免费下载

动软.Net代码生成插件源码

软件版本: 2.4
最后更新: 2009-7-19
软件大小: 69K
软件性质: 简体中文/免费软件
运行环境: Codematic 2.19
下载次数: 28405
软件描述:

动软.Net代码生成器2.1以上版本支持代码插件机制,支持可扩展的代码生成插件,用户可以定制自己的代码生成的插件,根据接口开发自己的代码生成方式,按用户需求进行代码生成。详见插件接口开发帮助和源码。
这里提供了几种典型的代码生成方式的组件源码供参考。
包括组件:
1.BuilderBLLComm  BLL层代码模板组件源码。
2.BuilderDALELParam  DAL层代码构造器(基于企业库方式)
3.BuilderDALParam  DAL数据访问层代码构造器(Parameter方式)
4.BuilderDALSQL  DAL数据访问层代码构造器(基于SQL方式)
5.BuilderModel  Model代码生成组件源码。
6.BuilderWeb  Web层代码生成组件源码。
7.Lib  需要引用的库。

同时希望大家把自己开发的代码生成插件在网上共享,别的网友也可使用已经开发好的插件覆盖安装目录下的插件库文件通过插件管理增加新插件。尽量避免重复开发。借此也可以让大家有个交流的机会。
全部源码下载

    本地免费下载

[C#].NET 中的对象序列化

mikel阅读(1300)

转载:http://www.cnblogs.com/wayfarer/articles/4759.html

Piet Obermeyer

Microsoft Corporation

 

2001 8

 

摘要:为什么要使用序列化?最重要的两个原因是:将对象的状态保存在存储媒体中以便可以在以后重新创建出完全相同的副本;按值将对象从一个应用程序域发送至另一个应用程序域。例如,序列化可用于在 ASP.NET 中保存会话状态,以及将对象复制到 Windows 窗体的剪贴板中。它还可用于按值将对象从一个应用程序域远程传递至另一个应用程序域。本文简要介绍了 Microsoft .NET 中使用的序列化。

 

目录

简介

持久存储

按值封送

基本序列化

选择性序列化

自定义序列化

序列化过程的步骤

版本控制

序列化规则

 

简介

序列化是指将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。

 

在面向对象的环境中实现序列化机制时,必须在易用性和灵活性之间进行一些权衡。只要您对此过程有足够的控制能力,就可以使该过程在很大程度上自动进行。例如,简单的二进制序列化不能满足需要,或者,由于特定原因需要确定类中那些字段需要序列化。以下各部分将探讨 .NET 框架提供的可靠的序列化机制,并着重介绍使您可以根据需要自定义序列化过程的一些重要功能。

 

持久存储

我 们经常需要将对象的字段值保存到磁盘中,并在以后检索此数据。尽管不使用序列化也能完成这项工作,但这种方法通常很繁琐而且容易出错,并且在需要跟踪对象 的层次结构时,会变得越来越复杂。可以想象一下编写包含大量对象的大型业务应用程序的情形,程序员不得不为每一个对象编写代码,以便将字段和属性保存至磁 盘以及从磁盘还原这些字段和属性。序列化提供了轻松实现这个目标的快捷方法。

 

公共语言运行时 (CLR) 管理对象在内存中的分布,.NET 框架则通过使用反射提供自动的序列化机制。对象序列化后,类的名称、程序集以及类实例的所有数据成员均被写入存储媒体中。对象通常用成员变量来存储对其他实例的引用。类序列化后,序列化引擎将跟踪所有已序列化的引用对象,以确保同一对象不被序列化多次。.NET 框架所提供的序列化体系结构可以自动正确处理对象图表和循环引用。对对象图表的唯一要求是,由正在进行序列化的对象所引用的所有对象都必须标记为 Serializable(请参阅基本序列化)。否则,当序列化程序试图序列化未标记的对象时将会出现异常。

 

当反序列化已序列化的类时,将重新创建该类,并自动还原所有数据成员的值。

 

按值封送

对象仅在创建对象的应用程序域中有效。除非对象是从 MarshalByRefObject 派生得到或标记为 Serializable,否则,任何将对象作为参数传递或将其作为结果返回的尝试都将失败。如果对象标记为 Serializable,则该对象将被自动序列化,并从一个应用程序域传输至另一个应用程序域,然后进行反序列化,从而在第二个应用程序域中产生出该对象的一个精确副本。此过程通常称为按值封送。

 

如果对象是从 MarshalByRefObject 派生得到,则从一个应用程序域传递至另一个应用程序域的是对象引用,而不是对象本身。也可以将从 MarshalByRefObject 派生得到的对象标记为 Serializable。远程使用此对象时,负责进行序列化并已预先配置为 SurrogateSelector 的格式化程序将控制序列化过程,并用一个代理替换所有从 MarshalByRefObject 派生得到的对象。如果没有预先配置为 SurrogateSelector,序列化体系结构将遵从下面的标准序列化规则(请参阅序列化过程的步骤)。

 

基本序列化

要使一个类可序列化,最简单的方法是使用 Serializable 属性对它进行标记,如下所示:

 

[Serializable]

public class MyObject {

  public int n1 = 0;

  public int n2 = 0;

  public String str = null;

}

以下代码片段说明了如何将此类的一个实例序列化为一个文件:

 

MyObject obj = new MyObject();

obj.n1 = 1;

obj.n2 = 24;

obj.str = "一些字符串";

IFormatter formatter = new BinaryFormatter();

Stream stream = new FileStream("MyFile.bin", FileMode.Create,

FileAccess.Write, FileShare.None);

formatter.Serialize(stream, obj);

stream.Close();

本例使用二进制格式化程序进行序列化。您只需创建一个要使用的流和格式化程序的实例,然后调用格式化程序的 Serialize 方法。流和要序列化的对象实例作为参数提供给此调用。类中的所有成员变量(甚至标记为 private 的变量)都将被序列化,但这一点在本例中未明确体现出来。在这一点上,二进制序列化不同于只序列化公共字段的 XML 序列化程序。

 

将对象还原到它以前的状态也非常容易。首先,创建格式化程序和流以进行读取,然后让格式化程序对对象进行反序列化。以下代码片段说明了如何进行此操作。

 

IFormatter formatter = new BinaryFormatter();

Stream stream = new FileStream("MyFile.bin", FileMode.Open,

FileAccess.Read, FileShare.Read);

MyObject obj = (MyObject) formatter.Deserialize(fromStream);

stream.Close();

 

// 下面是证明

Console.WriteLine("n1: {0}", obj.n1);

Console.WriteLine("n2: {0}", obj.n2);

Console.WriteLine("str: {0}", obj.str);

上面所使用的 BinaryFormatter 效率很高,能生成非常紧凑的字节流。所有使用此格式化程序序列化的对象也可使用它进行反序列化,对于序列化将在 .NET 平 台上进行反序列化的对象,此格式化程序无疑是一个理想工具。需要注意的是,对对象进行反序列化时并不调用构造函数。对反序列化添加这项约束,是出于性能方 面的考虑。但是,这违反了对象编写者通常采用的一些运行时约定,因此,开发人员在将对象标记为可序列化时,应确保考虑了这一特殊约定。

 

如果要求具有可移植性,请使用 SoapFormatter。所要做的更改只是将以上代码中的格式化程序换成 SoapFormatter,而 Serialize Deserialize 调用不变。对于上面使用的示例,该格式化程序将生成以下结果。

 

<SOAP-ENV:Envelope

  xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

  xmlns:xsd="http://www.w3.org/2001/XMLSchema"

  xmlns:SOAP- ENC=http://schemas.xmlsoap.org/soap/encoding/

  xmlns:SOAP- ENV=http://schemas.xmlsoap.org/soap/envelope/

  SOAP-ENV:encodingStyle=

  "http://schemas.microsoft.com/soap/encoding/clr/1.0

  http://schemas.xmlsoap.org/soap/encoding/"

  xmlns:a1="http://schemas.microsoft.com/clr/assem/ToFile">

 

  <SOAP-ENV:Body>

    <a1:MyObject id="ref-1">

      <n1>1</n1>

      <n2>24</n2>

      <str id="ref-3">一些字符串</str>

    </a1:MyObject>

  </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

需要注意的是,无法继承 Serializable 属性。如果从 MyObject 派生出一个新的类,则这个新的类也必须使用该属性进行标记,否则将无法序列化。例如,如果试图序列化以下类实例,将会显示一个 SerializationException,说明 MyStuff 类型未标记为可序列化。

 

public class MyStuff : MyObject

{

  public int n3;

}

使用序列化属性非常方便,但是它存在上述的一些限制。有关何时标记类以进行序列化(因为类编译后就无法再序列化),请参考有关说明(请参阅下面的序列化规则)。

 

选择性序列化

类通常包含不应被序列化的字段。例如,假设某个类用一个成员变量来存储线程 ID。当此类被反序列化时,序列化此类时所存储的 ID 对应的线程可能不再运行,所以对这个值进行序列化没有意义。可以通过使用 NonSerialized 属性标记成员变量来防止它们被序列化,如下所示:

 

[Serializable]

public class MyObject

{

  public int n1;

  [NonSerialized] public int n2;

  public String str;

}

自定义序列化

可以通过在对象上实现 ISerializable 接口来自定义序列化过程。这一功能在反序列化后成员变量的值失效时尤其有用,但是需要为变量提供值以重建对象的完整状态。要实现 ISerializable,需要实现 GetObjectData 方法以及一个特殊的构造函数,在反序列化对象时要用到此构造函数。以下代码示例说明了如何在前一部分中提到的 MyObject 类上实现 ISerializable

 

[Serializable]

public class MyObject : ISerializable

{

  public int n1;

  public int n2;

  public String str;

 

  public MyObject()

  {

  }

 

  protected MyObject(SerializationInfo info, StreamingContext context)

  {

    n1 = info.GetInt32("i");

    n2 = info.GetInt32("j");

    str = info.GetString("k");

  }

 

  public virtual void GetObjectData(SerializationInfo info,

StreamingContext context)

  {

    info.AddValue("i", n1);

    info.AddValue("j", n2);

    info.AddValue("k", str);

  }

}

在序列化过程中调用 GetObjectData 时,需要填充方法调用中提供的 SerializationInfo 对象。只需按名称/值对的形式添加将要序列化的变量。其名称可以是任何文本。只要已序列化的数据足以在反序列化过程中还原对象,便可以自由选择添加至 SerializationInfo 的成员变量。如果基对象实现了 ISerializable,则派生类应调用其基对象的 GetObjectData 方法。

 

需要强调的是,将 ISerializable 添加至某个类时,需要同时实现 GetObjectData 以及特殊的构造函数。如果缺少 GetObjectData,编译器将发出警告。但是,由于无法强制实现构造函数,所以,缺少构造函数时不会发出警告。如果在没有构造函数的情况下尝试反序列化某个类,将会出现异常。在消除潜在安全性和版本控制问题等方面,当前设计优于 SetObjectData 方法。例如,如果将 SetObjectData 方法定义为某个接口的一部分,则此方法必须是公共方法,这使得用户不得不编写代码来防止多次调用 SetObjectData 方法。可以想象,如果某个对象正在执行某些操作,而某个恶意应用程序却调用此对象的 SetObjectData 方法,将会引起一些潜在的麻烦。

 

在反序列化过程中,使用出于此目的而提供的构造函数将 SerializationInfo 传递给类。对象反序列化时,对构造函数的任何可见性约束都将被忽略,因此,可以将类标记为 publicprotectedinternal private。一个不错的办法是,在类未封装的情况下,将构造函数标记为 protect。如果类已封装,则应标记为 private。要还原对象的状态,只需使用序列化时采用的名称,从 SerializationInfo 中检索变量的值。如果基类实现了 ISerializable,则应调用基类的构造函数,以使基础对象可以还原其变量。

 

如果从实现了 ISerializable 的类派生出一个新的类,则只要新的类中含有任何需要序列化的变量,就必须同时实现构造函数以及 GetObjectData 方法。以下代码片段显示了如何使用上文所示的 MyObject 类来完成此操作。

 

[Serializable]

public class ObjectTwo : MyObject

{

  public int num;

 

  public ObjectTwo() : base()

  {

  }

 

  protected ObjectTwo(SerializationInfo si, StreamingContext context) :

base(si,context)

  {

    num = si.GetInt32("num");

  }

 

  public override void GetObjectData(SerializationInfo si,

StreamingContext context)

  {

    base.GetObjectData(si,context);

    si.AddValue("num", num);

  }

}

切记要在反序列化构造函数中调用基类,否则,将永远不会调用基类上的构造函数,并且在反序列化后也无法构建完整的对象。

 

对象被彻底重新构建,但是在反系列化过程中调用方法可能会带来不良的副作用,因为被调用的方法可能引用了在调用时尚未反序列化的对象引用。如果正在进行反序列化的类实现了 IDeserializationCallback,则反序列化整个对象图表后,将自动调用 OnSerialization 方法。此时,引用的所有子对象均已完全还原。有些类不使用上述事件侦听器,很难对它们进行反序列化,散列表便是一个典型的例子。在反序列化过程中检索关键字/值对非常容易,但是,由于无法保证从散列表派生出的类已反序列化,所以把这些对象添加回散列表时会出现一些问题。因此,建议目前不要在散列表上调用方法。

 

序列化过程的步骤

在格式化程序上调用 Serialize 方法时,对象序列化按照以下规则进行:

 

检查格式化程序是否有代理选取器。如果有,检查代理选取器是否处理指定类型的对象。如果选取器处理此对象类型,将在代理选取器上调用 ISerializable.GetObjectData

如果没有代理选取器或有却不处理此类型,将检查是否使用 Serializable 属性对对象进行标记。如果未标记,将会引发 SerializationException

如果对象已被正确标记,将检查对象是否实现了 ISerializable。如果已实现,将在对象上调用 GetObjectData

如果对象未实现 Serializable,将使用默认的序列化策略,对所有未标记为 NonSerialized 的字段都进行序列化。

版本控制

.NET 框架支持版本控制和并排执行,并且,如果类的接口保持一致,所有类均可跨版本工作。由于序列化涉及的是成员变量而非接口,所以,在向要跨版本序列化的类中添加成员变量,或从中删除变量时,应谨慎行事。特别是对于未实现 ISerializable 的类更应如此。若当前版本的状态发生了任何变化(例如添加成员变量、更改变量类型或更改变量名称),都意味着如果同一类型的现有对象是使用早期版本进行序列化的,则无法成功对它们进行反序列化。

 

如果对象的状态需要在不同版本间发生改变,类的作者可以有两种选择:

 

实现 ISerializable。这使您可以精确地控制序列化和反序列化过程,在反序列化过程中正确地添加和解释未来状态。

使用 NonSerialized 属性标记不重要的成员变量。仅当预计类在不同版本间的变化较小时,才可使用这个选项。例如,把一个新变量添加至类的较高版本后,可以将该变量标记为 NonSerialized,以确保该类与早期版本保持兼容。

序列化规则

由 于类编译后便无法序列化,所以在设计新类时应考虑序列化。需要考虑的问题有:是否必须跨应用程序域来发送此类?是否要远程使用此类?用户将如何使用此类? 也许他们会从我的类中派生出一个需要序列化的新类。只要有这种可能性,就应将类标记为可序列化。除下列情况以外,最好将所有类都标记为可序列化:

 

所有的类都永远也不会跨越应用程序域。如果某个类不要求序列化但需要跨越应用程序域,请从 MarshalByRefObject 派生此类。

类存储仅适用于其当前实例的特殊指针。例如,如果某个类包含非受控的内存或文件句柄,请确保将这些字段标记为 NonSerialized 或根本不序列化此类。

某些数据成员包含敏感信息。在这种情况下,建议实现 ISerializable 并仅序列化所要求的字段。

[MVC]分页存储过程,ASP.NET MVC中的Linq分页

mikel阅读(960)

转载:http://www.cnblogs.com/sonven/archive/2009/07/27/1531836.html
分页存储过程 


Alter procedure [dbo].[sys_getPagerData]
    
@pageSize int数据条数,注意从1开始
    @pageIndex int,页数索引
    @fields nvarchar(100),选取字段
    @Conditions nvarchar(50),选择条件
    @Chart nvarchar(50), 要选取的表
    @recordCount int output, 记录总数
    @pageCount int output 页面条数
AS
declare @SQL nvarchar(300)SQL语句
declare @SkipCount int要跳过查询的记录数

/* 获取记录总数 */
declare @getRecordSql nvarchar(100
set @getRecordSql='select @recordcount=count(1) from '+@chart
exec sp_executesql @getRecordSql,N'@recordCount int output',@recordCount output
/* 计算总页数 */
select @pagecount=floor(@recordCount/@pageSize);
if(@recordCount%@pageSize!=0)set @pagecount=@pageCount+1
/* 计算要跳过查询的数目 */
if(@pagecount=1 or @pagecount=0)set @skipCount=0
else set @skipCount=@pageSize*(@pageCount1);
set @sql='select top '+convert(nvarchar,@pageSize)+' '+
    
@fields+' from '+@Chart
+' where(id not in(select top '+
    
convert(nvarchar,(@skipCount))+' id from '+@Chart+
' order by id desc))'
/*附加查询条件*/
if(@Conditions is not null and @Conditions!='')
    
set @sql=@sql+' and '+@Conditions
添加 print @sql可以监视执行的Sql语句–

execute(@sql)

 

广告:http://www.cnyolee.com/(讲述有理的网站)
      http://www.rsion.com/

ASP.NET MVC中分页

 public ActionResult CateList(string page)
        
{
            
//分页大小
            int pageSize = 15;
            
//总页数
            int recordCount = (int)Helper.DataControl.FactoryCommand("select count(0) from articles where",CommandType.Text).ExecuteScalar();
            
int pageCount = recordCount / pageSize + (recordCount % pageSize == 0 ? 0 : 1);

            
//当前页数
            int currentPage = page == null ? 0 :Convert.ToInt32(page);
            
if (currentPage <1) currentPage = 1;
            
else if (currentPage > pageCount) currentPage = pageCount;
            
//分页信息
            TempData["pagedInfo"= "<a href=\"" + Request.Path + "?page=" + (currentPage == 1 ? 1 : currentPage – 1) +
                "\">上一页</a> | <a href=\"" + Request.Path + "?page=" + (currentPage == pageCount ? pageCount : currentPage + 1+
                
"\">下一页</a> <input type=\"text\" size=\"3\" id=\"tbpage\"> <a href=\"#\" onclick=\"location.href='" + Request.Path +
                
"?page='+tbpage.value;\">跳页</a> "+
                
                
"" + (currentPage) + "页/共" + pageCount + "页&nbsp;每页" + pageSize +
                
"条记录/共" + recordCount + "条!";

            
//计算要跳过的页
            int skipCount =recordCount<=pageSize?0:pageSize * (currentPage 1);

            IEnumerable
<Models.Article> articles = Common.DataContext.Articles.Skip(skipCount).Take(pageSize).OrderByDescending(a=>a.AddDate);
      
//注意:要先使用Skip(int)后再使用Take(int)
            return View(articles);
        }

 

[Linq]LINQ可视化查询编辑器: VLinq

mikel阅读(890)

转载:http://www.cnblogs.com/lyj/archive/2008/04/02/1135120.html

大家学习LINQ是不是寻思写LINQ to SQL语句没有一个可视化的编程环境。Mitsu和他们的团队用了近一年的工作在今天发布了VLinq(Visual Linq Query Builder,LINQ可视化查询编辑器)。

Visual Linq Query Builder(LINQ可视化查询编辑器)作为Visual Studio 2008的一个插件,可以帮助我们在程序中创建LINQ to SQL查询表达式,支持C#和VB两种语言。

Linq可视化查询编辑器

VLinq使用Visual Studio 2008 (betas then RTM) 和Expression Blend开发的。在下载页中提供了整个工程项目和一个安装文件。下载文件:

  • msi安装文件
  • 快速参考指南
  • 用户说明文件
  • 2分钟的开发视频。
  • 源代码

下面是摘自Mitsu博客中的描述的快速开发指南:

1.创建一个新程序

创建一个新程序

2.添加LINQ to SQL class

添加LINQ to SQL class

3.添加VLinq queries

添加VLinq queries

4.设置VLINQ连接

设置VLINQ连接

5.创建新的查询语句

创建新的查询语句

6.在可视化编辑器中编辑查询语句

在可视化编辑器中编辑查询语句

可以收缩和展开查询节点

可以收缩和展开查询节点

7.预览查询结果

预览查询结果

8.查看自动生成的代码

查看自动生成的代码

更多使用可以参考开发视频和快速开发指南。

注意:Visual Linq Query Builder目前好像只支持英文版,我目前电脑安装的是中文版不可以使用,没有给大家介绍详细开发实例。抱歉!大家可以去试试,分享一下!