Picture Cache & DAL





2.00/5 (5投票s)
2007年11月26日
4分钟阅读

25910

269
Picture Cache & DAL(

引言
大家好:
这是一个演示项目……所以你可能会遇到异常……如果你想在实际生活中、实际应用中使用它,你需要做一些修改……
背景
我有一个包含大量图片的应用程序,这些图片存储在 SQL2005 中(出于安全和其他原因,而且我认为它并不比存储在文件系统中慢!)。
很多 BO(业务对象)都有图像(产品、客户、用户等),所以每个 BO 表都有一个指向 Image 数据表的 _img_id 外键。
我编写了自己的可绑定 PictureBox(见下文),我可以绑定 img_id 字段,Picture Box 就可以获取到图像。
从数据库中选择图片可能会对数据库造成很大的负载(用户打开产品详情,然后关闭,再打开,再关闭……)。所以我做了一个缓存。
这是一个非常简单的组件。我使用了单例模式,所以每个应用程序只有一个缓存(静态组件)。因此,当 PictureBox 获得一个 ID(int32)时,它会向自己的 PictureCache(应用程序中唯一的那个)请求图片,如果图片在内存中,就会被返回;如果不在,该组件会尝试从(SQL 或 WS)加载它,如果成功,就会返回 Image……
在示例应用程序中,它是一个缓存组件和 DBpicturebox 的简化版本。(你可以根据自己的需要进行开发……保存功能、释放内存“逻辑”等)
它运行完美……但是……一段时间后,我开发此应用程序的公司开设了第二个办公室,但无法将 SQL 服务器迁移到服务器场,所以它仍然留在第一个办公室,并通过 ADSL 连接,上传速度为 384-512kbps……
应用程序运行正常,但图片……太慢了……
我需要在“基础设施”中添加第二个缓存……最简单的方法是……将这个组件包装成一个 Web 服务
- 远程客户端有一个本地的 Win2k3 服务器,上面运行着 Web 服务
- Web 服务(带有静态 PictureCache 组件)有一个缓存
- 当然,每个应用程序都有自己的应用程序缓存……
‘本地’客户端的获取方式
- 在内存中?是:使用它……否:查询 SQL
‘远程’客户端的获取方式
- 在内存中?是:使用它……否:查询 WS
- WS 在内存中?是:返回它,否:查询 SQL。
这样,冗余就消失了……
Using the Code
所以你需要……
- 一个包含一个 PictureCache 的测试应用程序
private localhost.Service1 ws = new PictureCache.localhost.Service1();
重要提示:在实际应用程序中使用它的静态实例……我在演示解决方案中使用了构造函数来演示它的工作原理!!!
初始化它
pc.MaxSizeInKbyte = 500; //max size of the app cache
pc.mode = PictureCacheDLL.PicCache.PCmode.WS_SQL; // the mode see below...
//set the connection u need
pc.SqlConn = new System.Data.SqlClient.SqlConnection("");
....
- 一个显示图片的 Picture Box
public class DBPictureEdit:System.Windows.Forms.PictureBox
{
private int imgID = 0;
public int ImgId
{
get
{
return imgID;
}
set
{
imgID = value;
if (imgID == 0)
{
this.Image = null;
return;
}
if (pc != null)
{
this.Image = pc.GetImage(value,true);
}
else
{
throw new Exception();
}
}
}
public PicCache pc;
}
初始化它
//set the PictureEdit PicCache
dbPictureEdit1.pc = this.pc;
- WebService
如果你设置了 WS|WS_SQL|SQL_WS 模式,缓存会在需要图片时,向 WebService 查询……所以你需要一个 IIS 和一个 Web 服务,里面包含一些函数。
[WebService(Namespace = "http://tempuri.org/")]
public class Service1 : System.Web.Services.WebService
{
public static PictureCacheDLL.PicCache pc;
....
初始化它
为 WS 项目添加一个 global.asax 文件
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
//initialize the instance
Service1.pc = PictureCacheDLL.PicCache.Instance;
Service1.pc.MaxSizeInKbyte = 1500;
//the web service local cache mode is SQL, ofc...
Service1.pc.mode = PictureCacheDLL.PicCache.PCmode.SQL;
//set the connection
//Service1.pc.SqlConn = new System.Data.SqlClient.SqlConnection("");
}
....
当然,它的模式是 SQL……(有可能远程客户端调用远程 WS,然后远程 WS 又调用另一个 WS,以此类推……但这可能会很有趣 :),不是一个真实的基础设施。)
主函数
[WebMethod]
public byte[] GetImageByte(int imgID, bool original)
{
return pc.GetImageByte(imgID, original);
}
....
还有一些测试和演示函数
- 内存使用情况,清除图片,获取 ID……等等……
- 使用它……
dbPictureEdit1.ImgId = int.Parse(textBox1.Text);
缓存组件
这是一个简单的组件,带有一些功能。
主要功能和属性
重要:它返回 JPEG 格式的图片!!!!而不是原始格式!!!!
///the PictureCache- web service....
private PicCacheWS.Service1 WS = new PictureCacheDLL.PicCacheWS.Service1();
//singleton pattern...
private static PicCache instance = null;
public static PicCache Instance
{
get
{
if (instance == null)
{
instance = new PicCache();
}
return instance;
}
}
//main get Image function
public Image GetImage(int img_id, bool originalSize)
{
if (img_id < 0)
{
return null;
}
if (listID.IndexOf(img_id) < 0)
{
if (!LoadImage(img_id))
{
return null;
}
}
DataRow[] dr = dstImages1.Images.Select(
"img_id = " + img_id.ToString());
if (dr.Length > 0 && dr[0]["img_image"] != System.DBNull.Value)
{
MemoryStream memStream = new MemoryStream(
(byte[])dr[0]["img_image"]);
Image ret = Image.FromStream(memStream);
Image retSmall;
memStream = new MemoryStream();
ret.Save(memStream, System.Drawing.Imaging.ImageFormat.Jpeg);
retSmall = new Bitmap(Image.FromStream(memStream),
new Size(64, 64));
if ((originalSize))
{
return ret;
}
else
{
return retSmall;
}
}
else
{
return null;
}
}
加载方法
- 如果模式是 WS,它只从 Webservice 加载图片
- 如果模式是 SQL,它只从 SQL 加载图片
- 如果模式是 SQL_WS,它先从 SQL 加载,然后从 WS 加载(如果未成功)
- 如果模式是 WS_SQL,它先从 WS 加载,然后从 SQL 加载(如果未成功)
private bool LoadImage(int img_id)
{
if (mode == PCmode.WS || mode == PCmode.WS_SQL)
{
if (LoadFromWS(img_id))
{
FreeMemory();
listID.Add(img_id);
return true;
}
if (mode == PCmode.WS_SQL)
{
if (LoadFromSQL(img_id))
{
FreeMemory();
listID.Add(img_id);
return true;
}
return false;
}
else
{
return false;
}
}
if (mode == PCmode.SQL ||mode == PCmode.SQL_WS)
{
if (LoadFromSQL(img_id))
{
FreeMemory();
listID.Add(img_id);
return true;
}
else
{
if (mode == PCmode.SQL_WS)
{
if (LoadFromWS(img_id))
{
FreeMemory();
listID.Add(img_id);
return true;
}
else
{
return false;
}
}
return false;
}
}
return false;
}
从数据库加载
private bool LoadFromSQL(int img_id)
{
AdapterParamNull(adtxImages);
int count = dstImages1.Images.Rows.Count;
try
{
adtxImages.SelectCommand.Parameters["@action"].Value =
"S";
adtxImages.SelectCommand.Parameters["@img_id"].Value =
img_id;
adtxImages.Fill(dstImages1.Images);
if (dstImages1.Images.Rows.Count == count)
{
return false;
}
else
{
return true;
}
}
catch (Exception ex)
{
return false;
}
finally
{
}
}
从 WS 加载
private bool LoadFromWS(int img_id)
{
try
{
byte[] img = WS.GetImageByte(img_id, true);
if (img != null)
{
dstImages1.Images.AddImagesRow(img_id, img, "",
"", "", "", 0);
return true;
}
return false;
}
catch (Exception)
{
return false;
}
}
大小,释放内存
public void ClearMemory()
{
while (dstImages1.Images.Rows.Count > 0)
{
dstImages1.Images.Rows.RemoveAt(0);
listID.RemoveAt(0);
}
}
private void FreeMemory()
{
//free the memory until the usage < maxSize
while (dstImages1.Images.Rows.Count > 0 && mMaxSizeInKbyte <
SizeInKbyte())
{
//toroljuk a legregebbit;
dstImages1.Images.Rows.RemoveAt(0);
//listID.Remove((int)dstImages1.Images.Rows[0]["img_id"]);
listID.RemoveAt(0);
}
}
public int SizeInKbyte()
{
int size = 0;
foreach (DataRow act in dstImages1.Images.Rows)
{
size += ((byte[])act["img_image"]).Length;
}
size = size / 1024;
return size;
}
最后
所以这是我的 CacheComponent 的一个简单部分,你可以使用它,以你自己的方式进行开发……
在慢速网络上,它可能比直接访问 SQL(没有缓存)更快。
可能的升级/功能
- 保存功能
- 在远程 WS 中增加本地缓存(存储在本地文件系统中,并在启动时加载到内存……),如果数据库中的图片是只读的……
- 更智能的清理逻辑:计算图片的访问次数,按次数排序,然后按此顺序清理……
- 获取小尺寸/缩略图
- 增强 Picture Box……