[DB4O]db4objects 7.4应用笔记

mikel阅读(976)

db4o是面向对象的数据库,复杂应用就不说了,说点简单的。

数据库嘛,简单的操作就是添加,删除,更新和查询。

1、添加

添加非常简单:
先建一个需要存储的对象

    public class Model
{
public int ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return string.Format("ID:{0} , Name:{1}", ID, Name);
}
}
在进行存储操作
            using (IObjectContainer db = Db4oFactory.OpenFile("d:\\d.dat"))
{
db.Store(new Model() { ID = 1, Name = "Test" });
}
或者
            IObjectContainer db = Db4oFactory.OpenFile("d:\\d.dat");
db.Store(new Model() { ID = 1, Name = "Test" });
db.Dispose();
 
2、更新
更新操作就有一个引用的概念。怎么确定是删除的对象,在db4o中是通过ObjectRefence来操作的。对象的指向一定要正确啦。所以虽然也是用Store方法进行更新操作,但是,不能直接更新。比如对于上面添加的一条记录使用
IObjectContainer db = Db4oFactory.OpenFile("d:\\d.dat");
db.Store(new Model() { ID = 1, Name = "Test" });
db.Dispose();
并不会更新,而是一个添加操作。而下面的操作也无法更新:
using (IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName))
{          
    IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
    for (int i = 0; i < list.Count; i++)
    {
        db.Store(new Model() { ID = 1, Name = "Test2" });
    }
}
 
需要更新引用才可以。因此要写成:
using (IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName))
{          
    IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
    for (int i = 0; i < list.Count; i++)
    {
        ((Model)list[i]).Name = "Test2";
        db.Store(list[i]);
    }
}
在循环中list[i]是无法直接赋值的。不能像下面那样用:
using (IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName))
{          
    IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
    for (int i = 0; i < list.Count; i++)
    {
        list[i] = new Model() { Name = "tttttt", ID = 2 };
        db.Store(list[i]);
    }
}
可以给Model对象添加一个方法:
    public class Model
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public void SetValue(Model model)
        {
            this.ID = model.ID;
            this.Name = model.Name;
        }
        public override string ToString()
        {
            return string.Format("ID:{0} , Name:{1}", ID, Name);
        }
    }
然后使用:
using (IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName))
{
    IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
    for (int i = 0; i < list.Count; i++)
    {
        ((Model)list[i]).SetValue(new Model() { Name = "tttttt", ID = 2 });
        db.Store(list[i]);
    }
}
 
当然,也可以像下面一样用:
    IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName);
    IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
    for (int i = 0; i < list.Count; i++)
    {
        ((Model)list[i]).SetValue(new Model() { Name = "tttttt", ID = 2 });
        db.Store(list[i]);
    }
    db.Dispose();
 
3、删除
删除和更新差不多,也需要删除引用,可以有下面两种用法。
using (IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName))
{
IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
for (int i = 0; i < list.Count; i++)
{
db.Delete(list[i]);
}
}
或者

IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName);
IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
for (int i = 0; i < list.Count; i++)
{
db.Delete(list[i]);
}
db.Dispose();
4、查询
更新和删除实际上已经用了查询,查询语句可以写成:
using (IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName))
{
IObjectSet list = db.QueryByExample(new Model() { ID = 2 });
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(((Model)list[i]).ToString());
}
}
或者
 

IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName);
IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i].ToString());
}
db.Dispose();
上面是根据对象查的。下面根据是按类型,用到的方法是Query。
IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName);
IList<Model> list = db.Query<Model>(typeof(Model));
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i].ToString());
}
db.Dispose();

把所有的Model类型的数据都查询出来了就。

5、简单讲解
查询的时候,比如
IObjectSet list = db.QueryByExample(new Model() { ID = 1 });
是查找所有ID为1的Model类型的数据。
如果要查找Name为“123”就写成:
IObjectSet list = db.QueryByExample(new Model() { Name = "123" });
6、封装
下面给两个操作的封装类:
对象封装
或者
静态方法封装
注意,以上两个封装并没有封装更新的方法,更新的时候需要新查询数据然后再更新。

[HTML]Pre标签自动换行

mikel阅读(843)

pre标签会原样保留HTML内容的格式,可是如果宽度过大会把页面撑坏,这时候需要自动换行来帮忙:

Wrapping the pre tag

Making preformated text wrap in CSS3, Mozilla, Opera and IEis the tip that let's you use the pre tag to keep the formatting, without cursing yourself when some of the content is too long and doesn't wrap: 

/* Browser specific (not valid) styles to make preformatted text wrap */
pre {
white-space: pre-wrap;       /* css-3 */
white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
white-space: -pre-wrap;      /* Opera 4-6 */
white-space: -o-pre-wrap;    /* Opera 7 */
word-wrap: break-word;       /* Internet Explorer 5.5+ */
} 
父标记最好加个DIV,并设置CSS属性:word-wrap: break-word;white-space : normal;

[OO]深入理解面向对象软件设计(一) —— 从具体例子谈起

mikel阅读(836)

    java和C#通常被认为是完全面向对象的语言,所有基本代码必须写在某个类中。但是,很多java和C#程序员编写的代码并不是真正面向对象的。有这种事?确实有,面向对象的编程语言只是提供了封装、继承和多态的机制,并不能保证我们用它写出的程序是面向对象的,即使我们把“人”和“狗”的代码糅合在一起,也不会导致编译和运行出错,我们来看一个c#编写的“人与狗的故事”:     

 


    class Program
{
static void Main(string[] args)
{
Story story = new Story();
Console.WriteLine(story.GetStory1());
Console.Read();
}
}
public class Story
{
private string dogName = "Aliths";//狗的名字        
        private string masterName = "Peter";//狗主人的名字        
        private string masterBabyName = "Jim";//狗主人的baby的名字        
        private string friendA = "Borbo";//A朋友的名字        
        private string babyA = "LiLy";//A朋友的baby的名字        
        public string GetStory1()
{
StringBuilder storyStr = new StringBuilder("         人与狗的故事\r\n\r\n");
storyStr.Append(string.Format("    傍晚,{0}带着儿子{1}和爱犬{2}出门散步,"
, masterName, masterBabyName, dogName));
storyStr.Append(string.Format("遇到了熟人{0},{0}带着女儿{1}在玩。\r\n"
, friendA, babyA));
storyStr.Append(string.Format("    {0}将{1}和{2}交给{3}照看,"
, masterName, masterBabyName, dogName, friendA));
storyStr.Append("自己去附近商店买烟。");
storyStr.Append(string.Format("这时{0}咬了{1}。{1}大哭,{2}气得遍地找板儿砖,"
, dogName, babyA, friendA));
storyStr.Append(string.Format("然后一板儿砖下去将{0}拍得满地找牙。\r\n"
, dogName));
//……            

storyStr.Append(string.Format("\r\n\r\n……"));
return storyStr.ToString();
}
}

 

    这段代码包含两个类Program和Story,单纯从语言层面讲,是面向对象的。但Story中把故事、狗和多个人等的代码糅合在一起(如果故事情节涉 及板砖和商店的细节,还会更乱,而且不仅用到人的名字,还有年龄穿着等,故事有时间地点等等,为例子简单,没有提及),称为面向对象的设计,完全说不过 去。

    如果需求不发生变化,这段代码不会有太大问题,没必要把类分的那么清楚。但需求还是变了,要求增加一个故事二:狗主人在另一天出门时遇到了朋友B,给B讲述了发生在前几天的故事一,并且讲述中他添油去醋,并没有按故事一的实际情节讲,我们来看代码:

 


    class Program
{
static void Main(string[] args)
{
StructStory story = new StructStory();
Console.WriteLine(story.GetStory1(1));//GetStory1增加了int参数

Console.WriteLine(story.GetStory2());//GetStory1增加了int参数

Console.Read();
}
}
public class StructStory
{
private string dogName = "Aliths";//狗的名字
        private string masterName = "Peter";//狗主人的名字
        private string masterBabyName = "Jim";//狗主人的baby的名字

private string friendA = "Borbo";//A朋友的名字
        private string babyA = "LiLy";//A朋友的baby的名字

private string friendB = "Jance";//B朋友的名字

public string GetStory1(int storyIndex)
{//为了给GetStory2调用,增加参数storyIndex,并根据不同参数进行不同的讲述

StringBuilder storyStr = new StringBuilder("         人与狗的故事\r\n\r\n");
storyStr.Append(string.Format("    傍晚,{0}带着儿子{1}和爱犬{2}出门散步,"
, masterName, masterBabyName, dogName));
storyStr.Append(string.Format("遇到了熟人{0},{0}带着女儿{1}在玩。\r\n"
, friendA, babyA));
if (storyIndex == 2)
{
//添油……
            }
if (storyIndex != 2)
{//去醋……

storyStr.Append(string.Format("    {0}将{1}和{2}交给{3}照看,"
, masterName, masterBabyName, dogName, friendA));
storyStr.Append("自己去附近商店买烟。");
}
storyStr.Append(string.Format("这时{0}咬了{1}。{1}大哭,{2}气得遍地找板儿砖,"
, dogName, babyA, friendA));
storyStr.Append(string.Format("然后一板儿砖下去将{0}拍得满地找牙。\r\n"
, dogName));
//……
            storyStr.Append(string.Format("\r\n\r\n……\r\n\r\n"));
return storyStr.ToString();
}
public string GetStory2()
{
StringBuilder storyStr = new StringBuilder("      人与狗的故事2\r\n\r\n");
storyStr.Append(string.Format("    第二天,{0}出门时遇到了熟人{1},"
, masterName, friendB));
storyStr.Append("给他讲了狗咬人的故事:\r\n");
storyStr.Append(this.GetStory1(2));
return storyStr.ToString();
}
}

 

    这时代码已经变得无法忍受了,如果需求再变,不堪设想。也许有的朋友要说,需求不至于一直变吧,我想说:需求不变才不正常,因为需求是人类思想的反映,人的想法是不断变化的,整个世界也在变化。高楼大厦之所以没有经常拆了重建,并不是人们对它满意,而是反复拆建需要大量时间和金钱。而 软件相对于建筑来说,只有设计过程(写文档和编码都是设计),没有建造过程,真正的建造过程是在代码写完后由计算机瞬间完成(编译成二进制exe、 dll),所以人们只要不满意就可能让它“重建”。可以设想一下,假如代码完成后要由人工打纸带(像最初的程序,0打孔,1不打)来编译,或许随便找一个 B/S程序够一个人打几年的,这时需求还是会变,但不会要求程序员去修改了——将就着用吧,不然就洗洗睡先。

    认同了需求是变化的,就要找办法解决问题,面向对象相对于面向过程的主要优势之一就在应对变化上,可能也是面向对象在系统软件开发方面流行的主要原因。上述代码有些过于初级,有一定经验的程序员可能会把代码写成这样: 

     Story有发生时间、地点等属性和GetStory方法,Person有被咬(被咬后的反映)和打狗等方法,如果故事情节涉及Brick(板砖)和 Shop(商店)的细节,还会有这两个类,这样基本有了貌似单一职责的几个类。其实,这仅仅是对代码的“归类”,没有做到单一职责,比如Person中的 BefallBite(被狗咬)方法会遇到这样的问题:

    如果Person实例是小孩,反映可能是坐在地上大哭

    如果是大人,可能是找板砖

    如果是狗主人家的小孩,狗可能只是轻咬他玩得,不会哭,会跟狗一起玩

    如果是大人,又是狗主人被咬,不一定舍得板砖拍……

    ……

    显然,Person还应该有一些子类:

 

    这样就基本有了职责单一的类结构。但我们还是发现了问题,Person的子类比较多,如果再加上“根据被狗咬的部位不同反映不同”(Baby被咬屁股不会 坐地上哭,可能是趴着哭;大人被咬右手可能无法拿板砖),那么子类将会更多。这时仅仅靠最基本的单一职责原则及其它几条原则已经不能做出良好的面向对象设 计,设计模式就是在面向对象的基础上进一步提高软件应对变化能力的“良药”。本例是典型的桥接模式应用场景,笔者将在后续博文中用更复杂更完整的例子和大家共同学习设计模式的综合运用。

 

PS 1:

    本文提到的三种编码方式应该可以代表三种编码阶段,在结尾提到了设计模式,其实在知道用设计模式后还有三个阶段,纯属个人看法:

    1、在单一职责等基本原则做的不太好时就接触了设计模式,根据各种模式定义的场景大量运用。这种阶段去做大项目,遇到的问题往往比最初级的混合编码阶段还多。

    2、基本功底打得扎实后,逐步学习运用设计模式,遇到问题能够根据设计模式定义进行思索,合理解决问题

    3、设计模式的定义经常不记得,设计和编码时只根据基本原则进行,遇到问题就重构代码,重构后发现好像和某种模式定义的场景类似,查书后确定是一样的……

   

    呵呵,设计模式最好不要强求,自然形成就好。

 

PS 2:

    刚开始写博客,对编辑器不熟悉,我贴的代码好好的,发布后对齐格式有点乱,反复几次搞不定;还有贴个类图要先截图存gif,再传上来,很麻烦。请高手留言指点,多谢!我用的编辑器是 TinyMCE(推荐)

[MVC]一个小Forum Web程序示例,使用ASP.NET MVC Framework,Repo

mikel阅读(781)

本文简单介绍以下内容:

1. Repository模式简介以及结合IQueryable改进的Repository模式;

2. 简单的TDD实践,使用ReSharper 4.0插件简化操作;

3. 结合使用依赖注入(Dependency Injection[中文翻译])工具StructureMap[文档下载],实现模块之间的松散耦合。

4. 提供一个很简单的论坛程序示例,Step by Step介绍以上内容。

差不多内容的blog示例在Codeplex上也早就有了,介绍MVC TDD和StructureMap的英文文章也挺多,本人才疏学浅,文采很烂,写本文是一是为了自己更深的掌握这些内容,二是为了让喜爱MVC的朋友也能 获得一些帮助,也是第一次写东西放首页上,请各位前辈多提意见和建议。

Repository简介

介绍Repository的文章比较少,其实这个模式相当简单,首先对它做一个简单的介绍:

image

这就是一个比较简单的Repository模式,首先创建一个IRepository接口,这个接口定义一系列契约,然后创建一个实现这个接口的类,负责它的具体操作。然后在Context中对它进行实例化(在本示例中,实例由依赖注入框架完成)。

当然这个模式还可以结合简单工厂模式对它进行扩充,由工厂来完成它的实例创建工作。

 

应用程序整体结构

下面介绍一下本示例的结构,也是比较简单的:

首先,我们使用ASP.NET MVC Framwork Preview 5,所有的操作(发帖、回帖、删帖等)都是由在相应的Controller中的Action来完成的。关于MVC这里就不做详细介绍了。我这里创建了一个 类库项目:TinyForum.Service,在这个项目中,创建一个IForumService定义一组契约,我们在Controller中需要一个 它的实例字段(为了好看,图中为属性,字段在Class diagram中不显示关系)来完成操作(如图)。

image

上图中的ForumService类中,需要对数据操作进行封装。为了使用不同的数据库,还得建立一个项目:TinyForum.Data,在这个项目中,我们会定义一个IForumRepository接口,又由这个接口定义一组对数据库的操作,所有操作返回类型都为IQueryable类型,这样非常的方便,在ForumService中就可以对它进行任意的转换,也不会因为返回大量的IList还可能需要做进一步的筛选影响性能。

image

这是ForumService的实现:

image

这是IForumRepository的定义(随意定义了2个方法):

image

很显然,在ForumService中,需要一个对IForumRepository的依赖,在实现的代码中,我们创建了一个构造方法来获 得这个对象,这时候出现一个麻烦问题,因为我们需要在上下文中去创建我们需要的IForumRepository的实例,这样做会造成可能需要修改 ForumService的代码,这样是违反OCP原则的;

同时ForumController也需要一个对IForumService的依赖,并且要在构造方法中获得它的示例,而MVC默认的Controller构造方法是无参的,编译可以通过,不过运行的时候会得到一个黄色的异常页面,大概是这样:

image

看来需要使用一个依赖注入工具来解救我们。我这里使用的StructureMap。

在Web项目中,添加StructureMap引用,并在Controllers目录下添加一个StructureMapControllerFactory,如图:

image 

然后我们需要为StructureMap做一点配置,让它知道该做什么,首先创建一个StructureMap的注册表:ForumRegistry

image

另外还要添加一个注册表,用于让SQLRepository获得ForumDataContext(Linq):

image 

都不是很复杂,代码已经解释得很详细了,就不做累述了。

最后需要添加一个Bootstrapper,并添加到Global.asax.cs中去,启动的时候就执行。

image

image

基本结构就介绍到这里。

目前的解决方案截图:

image

怕文章又臭又长,所以干脆分开写(至少不长,呵呵),下一个部分将介绍使用Resharper插件、和TDD步骤,并象征性实现一些基本的功能。

[C#]C# 3.0将偷懒进行到底

mikel阅读(974)

C# 3.0将偷懒进行到底

本文为博客园zpino原创

http://www.cnblogs.com/zpino/ 转载请说明出处!

 

最近在MS中了解到了一些东西。首先我要说,我绝对是一个新手。

要说核心原理,俺最多能给你搬来一点,要是刨根问底的话,去找MSDN。

 

隐藏类型局部变量

这个很多人都知道(–!我现在才知道。貌似很受打击,有点孤陋寡闻)

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var a = 1;  //自动判断类型。编译后成为int类型
            var b = 2;   //更加右边的值 推断左边的类型
            var c = 0;
if (b > a)
{
c = 4;
}
else
{
c = 5;
}
Console.WriteLine(c);
}
}
}

 

注意:

1 var 不是一个类型。顶多就像一个占位的东西。编译后替换成int string等等.而且只能作为局部变量!

2 var的值不能为null。不然编译器不能推断。

3 不能为对象和集合的初始化。但是可以初始化数组,包括多维数组。 var q=new{1,2,3};

初始化语句

 

 

扩展方法

这个也是一个很方便的东西

可以扩展一个类的方法。

比如

 

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int g = 1;
Console.WriteLine(g.extensions()); //注意g.extensions() 。g是一个int类型
//可是却有extensions()方法。其实是我把int增强了
        }
}
}

 

extensions就是一个扩展方法。

为什么会有这个效果呢?

其实之前我建立一个静态类,和一个静态方法。而且方法接受的参数是 extensions(this int k)。

上代码^_^

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public static class Class1 //必须为静态类,类名无所谓
    {
public static int extensions(this int k) //静态方法。必须是静态的。
//接受的类型之前加this。其中int可以替换为你想增强的东东。我增强int一个我自己写的新方法
        {
return k + 1;
}
}
}

这样就非常简单的实现不修改代码
直接扩展方法。
其中代码:


g.extensions()

 

被编译成了


Class1。extensions(g)

 

这个指针之类的有关系,俺暂时没有深入了解,所以不乱忽悠了。

注意:

1 方法扩展有优先级。实例方法>所在的namespace>别的namespace

2 记得类、方法要用静态。 

 

匿名对象


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var qq = new {name="你好",pass=123 };  //不创建类 直接构建一个匿名类型
            var vv = new { name="hello",pass=333}; //甚至不用写属性的类型,编译的时候会自动判断
            Console.WriteLine(qq.name);
Console.WriteLine(vv.name);
Console.WriteLine(qq.pass+vv.pass);//使用起来和平时没区别。
        }
}
}

 

匿名类型。不创建类 直接构建一个匿名类型,甚至不用写属性的类型,编译的时候会自动判断,使用起来和平时没区别。

只是有点局限性,不适合跨越传递。

注意:

1 其中声明的qq和vv可以互相赋值。只要编译器判断的属性类型相同。

2 声明的匿名类型全部继承Object

 

–!写完了,有兴趣的,记得装个VS2008试试。不知道在公司用这些老板会不会发飙?:-D

[ADO]SQL Server连接池

mikel阅读(740)

SQL Server 连接池 (ADO.NET)

连接到数据库服务器通常由几个需要很长时间的步骤组成。必须建立物理通道(例如套接字或命名管道),必须与服务器进行初次握手,必须分析连接字符串信息,必须由服务器对连接进行身份验证,必须运行检查以便在当前事务中登记,等等。

实际上,大多数应用程序仅使用一个或几个不同的连接配置。这意味着在执行应用程序期间,许多相同的连接将反复地打开和关闭。为了使打开连接花费的系统开销最小,ADO.NET 使用称为连接池的优化方法。

连接池使新连接必须打开的次数得以减少。池进程保持物理连接的所有权。通过为每个给定的连接配置保留一组活动连接来管理连接。每当用户在连接上调用 Open 时,池进程就会查找池中可用的连接。如果某个池连接可用,会将该连接返回给调用者,而不是打开新连接。应用程序在该连接上调用 Close 时,池进程会将连接返回到活动连接池集中,而不是关闭连接。连接返回到池中之后,即可在下一个 Open 调用中重复使用。

只有配置相同的连接可以建立池连接。ADO.NET 同时保留多个池,每种配置各一个。在使用集成的安全性时,连接按照连接字符串以及 Windows 标识分到多个池中。还根据连接是否已在事务中登记来建立池连接。

池 连接可以显著提高应用程序的性能和可缩放性。默认情况下,在 ADO.NET 中启用连接池。除非显式禁用,否则,在应用程序中打开和关闭连接时,池进程会对连接进行优化。还可以提供几个连接字符串修饰符来控制连接池的行为。有关更 多信息,请参见本主题后面的“使用连接字符串关键字控制连接池”。

在 初次打开连接时,将根据完全匹配算法创建连接池,该算法将池与连接中的连接字符串关联。每个连接池都与一个不同的连接字符串相关联。打开新连接时,如果连 接字符串并非与现有池完全匹配,将创建一个新池。按进程、应用程序域、连接字符串以及 Windows 标识(在使用集成的安全性时)来建立池连接。连接字符串还必须是完全匹配的;按不同顺序为同一连接提供的关键字将分到单独的池中。

在以下 C# 示例中创建了三个新的 SqlConnection 对象,但是管理时只需要两个连接池。注意,根据为 Initial Catalog 分配的值,第一个和第二个连接字符串有所不同。

using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=Northwind"))
{
connection.Open();
// Pool A is created.
}
using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=pubs"))
{
connection.Open();
// Pool B is created because the connection strings differ.
}
using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=Northwind"))
{
connection.Open();
// The connection string matches pool A.
}

如果 MinPoolSize 在连接字符串中未指定或指定为零,池中的连接将在一段时间不活动后关闭。但是,如果指定的 MinPoolSize 大于零,在 AppDomain 被卸载并且进程结束之前,连接池不会被破坏。非活动或空池的维护只需要最少的系统开销。

说明:

当出现故障转移等错误时,会自动清除池。

连接池是为每个唯一的连接字符串创建的。当创建一个池后,将创建多个连接对象并将其添加到该池中,以满足最小池大小的要求。连接根据需要添加到池中,但是不能超过指定的最大池大小(默认值为 100)。连接在关闭或断开时释放回池中。

在请求 SqlConnection 对象时,如果存在可用的连接,将从池中获取该对象。连接要可用,必须未使用,具有匹配的事务上下文或未与任何事务上下文关联,并且具有与服务器的有效链接。

连接池进程通过在连接释放回池中时重新分配连接,来满足这些连接请求。如果已达到最大池大小且不存在可用的连接,则该请求将会排队。然后,池进程尝试重新建立任何连接,直到到达超时时间(默认值为 15 秒)。如果池进程在连接超时之前无法满足请求,将引发异常。

警告:

我们强烈建议您在使用完连接时一定要关闭连接,以便连接可以返回池。要关闭连接,可以使用 Connection 对象的 CloseDispose 方法,也可以通过在 C#using 语句中或 Visual Basic 的 Using 语句中打开所有连接。不是显式关闭的连接可能不会添加或返回到池中。有关更多信息,请参见using 语句(C# 参考)或 Visual Basic 的如何:释放系统资源

说明:

不要在类的 Finalize 方法中对 ConnectionDataReader 或任何其他托管对象调用 CloseDispose。在终结器中,仅释放类直接拥有的非托管资源。如果类不拥有任何非托管资源,则不要在类定义中包含 Finalize 方法。有关更多信息,请参见垃圾回收

如果连接长时间空闲,或池进程检测到与服务器的连接已断开,连接池进程会将该连接从池中移除。注意,只有在尝试与服务器进行通信之后才能检测到断开的连接。如果发现某连接不再连接到服务器,则会将其标记为无效。无效连接只有在关闭或重新建立后,才会从连接池中移除。

如 果存在一个与已消失的服务器的连接,即使连接池进程尚未检测到断开的连接,也可以从池中取出此连接并将连接标记为无效。这种情况是因为检查连接是否仍有效 的系统开销将造成与服务器的另一次往返,从而抵消了池进程的优势。发生此情况时,初次尝试使用该连接将检测连接是否曾断开,并引发异常。

ADO.NET 2.0 引入了两种新的方法来清除池:ClearAllPoolsClearPoolClearAllPools 清除指定提供程序的连接池,ClearPool 清除与特定连接关联的连接池。如果在调用时连接正在使用,将对它们进行相应的标记。连接关闭时,将被丢弃,而不是返回池中。

连接是根据事务上下文来从池中取出并进行分配的。除非在连接字符串中指定了 Enlist=false,否则连接池将确保连接在 Current 上下文中登记。如果连接使用登记的 System.Transactions 事务关闭并返回到池中,连接将保留在池中,以便使用相同 System.Transactions 事务对该连接池的下一次请求将返回相同的连接(如果可用)。如果发出这样的请求,而没有可用的池连接,则会从池的非事务性部分取出一个连接并登记。如果在池的每个区域都没有可用的连接,则会创建一个新的连接并登记。

当连接关闭时,它将被释放回池中,并根据其事务上下文放入相应的子部分。因此,即使分布式事务仍然挂起,仍可以关闭该连接而不会生成错误。这样,您就可以在之后提交或中止分布式事务。

SqlConnection 对象的 ConnectionString 属性支持连接字符串键/值对,可以用于调整连接池逻辑的行为。有关更多信息,请参见 ConnectionString

池碎片是许多 Web 应用程序中的一个常见问题,应用程序可能会创建大量在进程退出后才会释放的池。这样,将打开大量的连接,占用许多内存,从而导致性能降低。

因为集成安全性产生的池碎片

连 接根据连接字符串以及用户标识来建立池连接。因此,如果使用网站上的基本身份验证或 Windows 身份验证以及集成的安全登录,每个用户将获得一个池。尽管这样可以提高单个用户的后续数据库请求的性能,但是该用户无法利用其他用户建立的连接。这样还使 每个用户至少产生一个与数据库服务器的连接。这对特定 Web 应用程序结构会产生副作用,因为开发人员必须衡量安全性和审计要求。

因为许多数据库产生的池碎片

许 多 Internet 服务提供商在一台服务器上托管多个网站。他们可能使用单个数据库确认窗体身份验证登录,然后为该用户或用户组打开与特定数据库的连接。与身份验证数据库的 连接将建立池连接,供每个用户使用。但是,每个数据库的连接存在一个独立的池,这会增加与服务器的连接数。

这也会对应用程序设计产生副作 用。但是,可以通过一个相对简单的方式避免此副作用,而又不会影响连接 SQL Server 时的安全性。不是为每个用户或组连接独立的数据库,而是连接到服务器上的相同数据库,然后执行 Transact-SQL USE 语句来切换为所需的数据库。以下代码段演示如何创建与 master 数据库的初始连接,然后切换到 databaseName 字符串变量中指定的所需数据库。

Visual Basic
' Assumes that command is a valid SqlCommand object and that
' connectionString connects to master.
command.Text = "USE DatabaseName"
Using connection As New SqlConnection(connectionString)
connection.Open()
command.ExecuteNonQuery()
End Using
// Assumes that command is a SqlCommand object and that
// connectionString connects to master.
command.Text = "USE DatabaseName";
using (SqlConnection connection = new SqlConnection(
connectionString))
{
connection.Open();
command.ExecuteNonQuery();
}

通过调用 sp_setapprole 系统存储过程激活了 SQL Server 应用程序角色之后,该连接的安全上下文无法重置。但是,如果启用了池,连接将返回池,在重复使用池连接时会出错。有关更多信息,请参见知识库文章“SQL application role errors with OLE DB resource pooling”(OLE DB 资源池出现 SQL 应用程序角色错误)。

应用程序角色替代项

如果您使用的是 SQL Server 2005,建议您使用可以替代应用程序角色的新安全机制。有关更多信息,请参见在 SQL Server 中创建应用程序角色 (ADO.NET)

[C#]超时时间已到,但是尚未从池中获取连接

mikel阅读(998)

“/ASP.Web”应用程序中的服务器错误。
——————————————————————————–

超时时间已到。超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小。
说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。

异常详细信息: System.InvalidOperationException: 超时时间已到。超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小。

这是个老问题了!你就查两点: 
  一、看所有open的连接是否都close了。 
  二、如果访问量很大,加上Max   Pool   Size=512这一句,当然这是要以损失系统性能为代价的! 
  这样以后一定可以解决你的问题!

  解决方案一

我 想原因可能是并发操作。DataReader是独占连接的,就是说你的程序可能设计上有问题。比如说最大连接设100,假设有100个人同时使用 DataReader正在读取数据库内容,那么当第101人读取的时候,连接池中的连接已经没有了,就会出现上面的错误。DataReader是独占连接 的,每个DataReader都要占用一个连接。当然这个情况是偶尔出现的,所以会很长时间出现一次,因为只有同时有超过连接池最大连接数量的并发操作才 会发生。而且你加大并发数量只能暂时缓解问题,如果你加大到200个并发连接,如果有201 人同时操作怎么办?你说了你使用Connection对象的Close()方法,这是不行的,因为Close()方法仅仅是关闭连接,但这个连接没有释 放,还是被这个对象占用,要释放必须使用Connection的Dispose()方法显式释放连接才可以,否则这个对象占用的连接只能等到垃圾收集的情 况下才能被释放。这种情况肯定会出现“超时时间已到”的错误。

解决方法:
         1 修改几个关键页面或访问比较频繁的数据库访问操作,使用DataAdapter和DataSet来获取数据库数据,不要使用DataReader。
         2 在访问数据库的页面上使用数据缓存,如果页面的数据不是经常更新(几分钟更新一次)的话,使用Cache对象可以不用访问数据库而使用缓存中的内容,那么可以大大减少连接数量。
         3 修改代码,把使用Connection对象的地方都在Close()后面加上Dispose()调用。
         4 建议对数据库操作进行大的修改,建立自己的数据库操作代理类,继承System.IDisposable接口,强迫释放资源,这样就不会出现连接数量不够的问题了。

解决方案二

解决方法(*):WEB.config 里面:在数据库连接加 Max Pool Size = 512;server=local;uid=;pwd=;database=2004;Max Pool Size = 512;">一劳永逸。

解决方案三

估计是连接(Connection)对象没有Close。倒是不必Dispose,而DataReader用完后应该关闭,但不关闭也没问题,只是不关闭的话此连接对象就一直不能用,只要你最终关闭了连接对象就不会出问题。  
   
   连接对象在Open后的操作都放在try块中,后面跟一个finally块:conn.Close();

错误:   3154一般是由于恢复数据库备份时指定的数据库名和原有数据库中数据库相同引起的 
  你指定一个不易重复的名字, 
  或者在恢复时指定选项:覆盖现存数据库 
  
   本人在将SQL2000数据库导入SQL2005出错,错误信息如下:

还原 对于 服务器“EDWARD\\TRACY”失败。 (Microsoft.SQLServer.Smo)

有关帮助信息,请单击: http://go.microsoft.com/fwlink?Prod…er&LinkId=20476

——————————
其他信息:

执行 Transact-SQL 语句或批处理时发生了异常。 (Microsoft.SQLServer.ConnectionInfo)

——————————

备份集中的数据库备份与现有的 \'foodgood\' 数据库不同。
RESTORE DATABASE 正在异常终止。 (Microsoft SQL Server,错误: 3154)

今天我准备把sql2000 的备份数据库 sp.bak 还原在sql2005 上 (因为课堂上用的是sql2000 而我自己电脑里装的是sql2005 )遇到了点麻烦。就是象sql2000 一样操作还原时出现了点问题。说不能还原,最后找到了点方法终于搞定。

1,在sql2005上新建一个数据库比如:商品

2,选择要还原的 sql2000备份文件

3,这里很重要,选中“覆…”看图

sql2000 备份的数据库还原到sql2005后,选择“数据库关系图”提示:此数据库没有有效所有者,因此无法安装数据库关系图支持对象。若要继续,请首先使用“数 据库属性”对话框的“文件”页或  Alter  AUTHORIZATION  语句将数据库所有者设置为有效登录名,然后再添加数据库关系图支持对象。 
    
解决方法如下:
1、设置兼容级别为90(2005为90)
USE  [master]
GO
EXEC  dbo.sp_dbcmptlevel  at dbname='数据库名',  @new_cmptlevel=90
GO  

或是选责你还原的数据库,点右键,选属性->选项->兼容级别,选择SQLServer2005(90) 然后确定,

      这时,你在该数据库下展开“数据库关系图”节点时会有个提示,"此数据库缺少一个或多个使用数据库关系图所需的支持对象,是否创建",选择“是”即可。

2、通过以上的方法操作,如果问题依然存在的话,按下列方法继续

选择你的数据库,然后选择"安全性"->"用户",选择dbo,打开属性页,如登录名为空的话,新建查询,然后

use [你的数据库名]
EXEC   sp_changedbowner   'sa'

执行成功后,你再选择"数据库关系图"节点,时提示 “此数据库缺少一个或多个使用数据库关系图所需的支持对象,是否创建",选择“是”即可。 就可以看到原先建的关系图了。

1、 对Table中的详细内容,以不同的颜色间隔开相邻的两行。

A:选择Table的Detail行,选择属性中的BackgroundColor,值选择表达式,输入:=iif(RowNumber(Nothing) Mod 2, "White", "Beige")。

2、设置每面显示Table表头或表尾

A:选择Table Header或Table Footer,将属性中的RepeatOnNewpage设为True dot

3、在每页都显示放入的图片或标题头等信息。

A:只须在Table Header中加行数,把你要显示的内容放到单元格中,然后再按第二条方式设置后就可以了。

 

程序功能是一次性生成要求数量的主条码,然后打印出来
我的做法是一次性把生成的代码全部放到一个arraylist里面,让后打印的时候再通过循环一个一个的从arraylist里面取出来打印,然后就导致了以下的错误.
错误如下:
有关调用实时(JIT)调试而不是此对话框的详细信息,
请参见此消息的结尾。
************** 异常文本 **************
System.InvalidOperationException: 超时时间已到。超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小。
在 System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
在 System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
在 System.Data.SqlClient.SqlConnection.Open()
在 UseDB.myDB.ExecuteStoreProcedure(String ProcedureName, SqlParameter[] Parameter)
在 Barcode_Print.DataManipulate.UpdateTwoTable(String Dy_codeTail)
在 Barcode_Print.DataManipulate.MakeBarcode(String quantity)
在 Barcode_Print.FrmBarcode.button1_Click(Object sender, EventArgs e)

字串9

在 System.Windows.Forms.Control.OnClick(EventArgs e)
在 System.Windows.Forms.Button.OnClick(EventArgs e)
在 System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
在 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
在 System.Windows.Forms.Control.WndProc(Message& m)
在 System.Windows.Forms.ButtonBase.WndProc(Message& m)
在 System.Windows.Forms.Button.WndProc(Message& m)
在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
连接对象没有及时关闭
我都检查过了,好像没有这样的错误阿
注意每次与数据库交换数据前再定义并打开连接,完后立即关闭.Close()(不要调用Dispose),把连接释放到连接池
不要定义全局型(比如类成员)连接变量
注意用try catch块而且连接关闭一定要放在finally块里面

字串1

和你的条码打印队列没什么关系,和其间或其外使用的数据库连接获取与释放有关系,因为很显然,连接耗尽了。连接应该准备工作完成马上就要提交数据或请求了再申请,用完了立即释放,越早越好。
为什么不能调用dispose?我都是close和dispose一起用的,like this:
conn.close();
conn.dispose();
这个是国外的一个re,但是好像持的是相反的观点
This can lead to heated debate, hopefully I'll answer first and then you can
ignore everyone else
Seriously though, this is what Dispose does (actual code):
switch (this._objectState)
{
case ConnectionState.Open:
{
this.Close();
break;
}
}
this._constr = null;
as you can see, all it does it call Close(). So you might be tempted to say
"i should just call close and save some stack". However, there is a dipose
pattern which you should follow. while you can call one, or the other or
both to achieve the same result, this behaviour could change with future
release. One day (for example 2.0) Dispose might do more…and then you'd

字串8

have to go through all your code and make sure you had called Dispose.
Additionally, languages like C# are dispose-aware thanks to the using
keyword.
As far as i'm concerned, those two points are enough to make sure I always
call Dipose on classes which inherit IDisposable.
As for close, my personal feeling is that it doesn't need to be called if
you are calling Dispose. And, since it's better to be consistent, I'd say
never call it. Why? well, none of the .net language are close-aware, and by
definition of what Dispose does, it would be a mistake from the class
designer (ie, microsoft) to implement cleanup functionality in close which
Dispose also woudlnt' do (possibly simply by running close, as it currently
does).
Karl
另外这一句怎么解释呢?
"注意用try catch块而且连接关闭一定要放在finally块里面"
正如viena(维也纳nn)所说的每次完成都要调用Close(), SqlConnection连接对象会回到连接池中,SqlClient使用的是内部连接机制,类似COM 对象池.这里需要注意的是连接池的参数配置,涉及到连接池的设计. 字串2
Connection Lifetime 设置连接的销毁时间
Max Pool Size 最大连接数
Min Pool Size 最小连接数
如果涉及到事务性处理,连接池的设计也不同.
我刚才查了一下关于connection pool的资料(虽然大部分都是java的)
知道为什么上面有朋友说不要用dispose了
因为close()只是释放连接让这个sqlconnection回到pool中,但是如果是dispose()的话可能pool都被释放掉了么??
Dispose不是释放pool,是释放连接
/*
另外这一句怎么解释呢?
"注意用try catch块而且连接关闭一定要放在finally块里面
*/
考虑数据库操作发生异常的情况:
1 没有异常处理,直接弹出错误对话框,程序终止,连接没有关闭
2 有异常处理,必须在程序运行的每个可能分支最后加连接关闭,否则连接可能没有关闭
如果放在finally块里面,不论是否发生异常,都会执行~

[教程]Javascript拖拽系列文章2之offsetLeft、offsetTop、offsetW

mikel阅读(1299)

在阅读本文之前,请先看一看第一篇文章Javascript拖拽系列文章1之offsetParent属性,因为循序渐进是一个很好的习惯,值得提倡。

好了,看看我们今天的内容吧。

 

首先让我们先看一看element.offsetLeft属性。

支持的浏览器:Internet Explorer 4.0+,Mozilla 1.0+,Netscape 6.0+,Opera 7.0+,Safari 1.0+

定义:返回一个像素数值,它表示当前元素的左边缘到它的offsetParent属性返回的对象左边缘的偏移量。

句法:

leftDis = element.offsetLeft

 

offsetLeft属性在Internet Explorer中的实现存在Bug,无论当前元素的 offsetParent属性取值如何,它总是以Body元素为参照物来计算offsetLeft。幸运的是,这个Bug在 Intern Explorer 8 Beta 1中已经修复。仍然需要注意,IE会从Body元素的Left-Border为标准开始计算 OffsetLeft,而其他的浏览器将从Left-Margin开始计算。

测试代码1:

 

Code
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
<style type="text/css">
body{
border:1px solid red;
margin-left:0px;
}
#parent{
position:relative;
left:25px;
top:0px;
border:1px solid black;
}
</style>
<script type="text/javascript" language="JavaScript">
function offset_init(){
var pElement = document.getElementById("sonObj");
parentObj = pElement.offsetParent;
var ioffsetLeft=pElement.offsetLeft;
alert(parentObj.tagName);
alert(ioffsetLeft);
}
</script>
</head>
<body onload="offset_init()">
<div id="parent">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<span id="sonObj">测试OffsetParent属性</span>
</div>
</body>
</html>

 

 

 

在IE中,运行这段代码后,会依次弹出两个窗口,分别显示“DIV”、“437”,分别表示 offsetParent和offsetLeft。在下图中,红色边框代表Body元素,黑色边框代表Div元素。从而证明即使在IE 中,offsetParent不是Body元素,offsetLeft的计算也以Body元素为标准。

图一:在IE7中的结果

在IE 8 Beta 1中这Bug已经被修复,将分别返回“DIV”,“411”。已经和其他浏览器一样符合了标准。

图二:在IETester中对IE 8 Beta 1的测试结果

注:IETester是一款非常不错的免费网页测试工具,可以代表IE的各个版本来渲染网页。最新版本0.2.3可以在其官方网站http://www.my-Debugbar.com/wiki/IETester/HomePage 下载。个人认为每一个WEB开发者都应该拥有一个。

 

仍然需要注意的是,如果在一个行内标签(作为offsetParent)内嵌入一个标签(作为当前元素),类此如下代码:

 

Code
<span id="parent">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<div id="sonObj">测试OffsetParent属性</div>
</span>

 

 

 

将引起混乱,因为各个浏览器的渲染各不相同(WebKit内核浏览器将会把OffsetParent属性指向 Body元素,而且IE对OffsetLeft取值不同),在行内标签内嵌入行内标签这种情况下,问题尤为明显(各个浏览器对OffsetLeft属性的 取值就都有差异了,无理可循)。

好了,offsetLeft就讲完了,我们继续看offsetTop属性。

支持的浏览器:Internet Explorer 4.0+,Mozilla 1.0+,Netscape 6.0+,Opera 7.0+,Safari 1.0+

定义:

返回一个数值,指明了当前元素的上边缘到其offsetTop属性返回的对象的上边缘的距离。

句法:

topDis = element.offsetTop

 

前面对于offsetLeft的Bug也存在于offsetTop属性中,同样,这个Bug在IE 8 Beta 1中也已经修复。

图三:在IE7及以下版本中,offsetsetTop属性的Bug。

当然也不要在内联标签内嵌入标签,因为WebKit内核浏览器会错误解释offsetParent属性。

offsetWidth属性

支持的浏览器:Internet Explorer 4.0+,Mozilla 1.0+,Netscape 6.0+,Opera 7.0+,Safari 1.0+

定义:

当前元素的宽度。

句法:

elementWidth = element.offsetWidth

需要指出的是,offsetWidth属性所指的宽度是当前元素的width+padding+border+margin的总和。

offsetHeight属性

支持的浏览器:Internet Explorer 4.0+,Mozilla 1.0+,Netscape 6.0+,Opera 7.0+,Safari 1.0+

定义:

当前元素的高度。

句法:

elementHeight = element.offsetHeight

同样,offsetWidth属性所指的高度是当前元素的Height+padding+border+margin的总和。

以上所说的四个属性再加上上一篇文章的offsetParent属性其实都不是Dom规范的一部分,但是目前的浏览器都实现了它们,这几个属性也是实现JavaScript拖拽功能的核心元素。因此一定要深入理解它们。

下一篇将讲讲事件中的相关属性。

[ORM]某人的ORM框架

mikel阅读(787)

  很多开源的ORM框架都需要配置,那实在是件很烦人的事情。我以前曾打算学NHibernate,开始还以为.NET的Hibernate应该 没JAVA的Hibernate用起来麻烦,结果在网上看了些教材,发觉那是一样的麻烦。说实话,只要是要配置的,我都没心情去做。一般的开源ORM框架 都比较重量级的,虽然能满足我的需要,但功能实在是太多太复杂了,我压根儿很难用得上,而且用起来也麻烦,我还不如自己直接写代码来得快呢。我总结了一下 平时做的项目,大部分的时候我只需要增、删、改、查这几个基本的功能就行了,其它的什么关联啊什么的,用得很少很少。想来想去,还是自己写个简单易用的 orM框架吧,满足一般的需求就够了。

  许多ORM框架把许多代码都用在了配置和其它一些无用功的方面,这样做虽然更加的具有通用性,但同时也给使用者带来了许多麻烦的工作和性能上的 损耗,而且体积也变得很大,代码复杂,难以维护。其实这部分的功能根本就没多大的用处,程序员又不是傻子,何必搞那么多的配置来纠错呢。根据约定优于配置 的原则,制定一个规范,只要大家都按照约定的规则来使用就OK了,对于程序员来说,要做到这一点一点也不难。

  我的ORM框架的约定是:

  1,实体类名和数据表名相同。

  2,主键是"表名ID",并且主键是自增的整数。

  3,数据表的字段名和属性名相同。

  4,正确使用。

  只要遵循以上的约定,就可以正确的使用我的ORM框架了。

  创建表的ORM管理:


//创建UserInfo表的管理者,DbConnectionString是数据库连接字符串名,在配置文件中设置
IORMManager<UserInfo> manager = orMBuilder<UserInfo>.GetORMManager("DbConnectionString");

有了IORMManager,就可以执行数据操作了。

 

要进行条件查询也很简单,通过IORMManager创建过滤器就行了

Code
///创建过滤器
        IFilter filter = manager.CreateFilter("UserName", userName, Operator.Equal);
UserInfo userInfo = manager.LoadOne(filter);

复合查询可以通过创建BooleanFilter对多个IFilter进行逻辑合并

Code
IFilter bFilter=manager.CreateBooleanFilter(leftFilter,rightFilter,LogicOperator.And);

经常会用到的一个比较复杂的查询应该就是分页功能了吧,我的ORM框架对分页也有很好的支持

Code

为数据表写实体类是件很麻烦的事情,尤其是字段特别多的时候,这种苦力活不应该由我们来做啊,太浪费了,所以我写了一个自动生成实体类的小软件,偷懒一下。

EntityBuilder.rar 数据表实体类生成工具。

WebSite1.rar 这个是ORM框架的Demo,不小心把罗斯文数据库也打包进去了。

喜欢的朋友看一下吧。