[原创]SQL数据库连接字符串怎么写

mikel阅读(802)

UDL 连接数据库
1. 新建一文本文件,改后缀为udl,如xx.udl;
2. 双击打开xx.udl,配置数据库;
3. 配置成功后,用记事本打开xx.udl,其中就有数据库连接字符串了.

[转载].net框架读书笔记---引用参数(ref/out)

mikel阅读(1098)

[转载].net框架读书笔记—引用参数(ref/out) – 恒星的恒心 – 博客园.

接上一篇.net框架读书笔记—方法(类型造器)

默认情况下,CLR假设所有的方法参数都是按值传递参数的。当参数为引用类型的对象时,参数的传递是通过传递对象的引用(或)指针来完成的。这 意味着方法可以改变引用对象,并且调用代码可以看到这种改变的结果。

对于值类型实例的参数来说,传递给方法的将是值类型实例的一个拷贝。这意味着方法会得到一份属于它自己的值类型实例的成员。而调用方法的代码中 的实例不会受到影响。

除了按值传递参数外,CLR还允许我们按引用的方式来传递参数.在C#中可以使用out和ref关键字来做到这一点。out和ref的区别是:

  • 一个方法的参数被标识为out,那么调用代码在调用该方法之前可以不初始化该参数,并且调用方法不能直接读取参数的值,它必须在返回之前为该参 数赋值。
  • 一个方法的参数被表示为ref,那么调用代码在调用该方法前必须首先初始化该参数。被调用方法可以任意读取该参数、或者为该参数赋值。

out关键字示例:

代码

class Program { static void Main(string[] args) { int x;//x不必初始化 SetVal(out x); Console.WriteLine(x);//显示为10 } static void SetVal(out int v) { // int x = v;//该行代码会报错 v = 10;//SetVal方法必须初始化v; } }

上面代码中,x首先被声明在线程堆栈上。接着,x的地址被传递给SetVal。SetVal的参数v是一个指向int值类型的指针。在 SetVal内部,v指向的int被赋值为10.当SetVal返回后,Main中的x的值为10,在值类型参数上使用out会提高代码的效率,因为它避 免l哦值类型的字段在方法调用时的拷贝操作。

ref关键字示例:

代码

class Program { static void Main(string[] args) { int x=5;//x必须初始化 AddVal(ref x); Console.WriteLine(x);//显示为15 } static void AddVal(ref int v) { int x = v;//addVal方法可以直接使用经过初始化的v而不报错 v += 10; } }

上面代码中,x首先被声明在线程堆栈上,紧接着被初始化为5,随后x的地址被传递给AddVal,AddVal的参数是一个指向int值类型的 指针。在AddVal内部,v指向的必须是一个结果初始化的值,这样AddVal才可以在任何表达式中使用该初始值。当AddVal返回后,Main中x 的值将为15;

CLR允许根据out和ref参数来重载方法,下面代码合法:

public class Point { static void Add(Point p) { } static void Add(ref Point p) { } }

还有:

public class Point { static void Add(Point p) { } static void Add(out Point p) { p = new Point();//必须给out参数赋值 } }

但是,仅仅通过out和ref来区分重载方法是不合法的,下面代码编译它不过:

public class Point { static void Add(ref Point p) { } static void Add(out Point p) { p = new Point();//必须给out参数赋值 } }

因为经过jit编译后打代码是相同的。

在值类型参数上使用out和ref关键字与用传值的方式来传递引用类型的参数在某种程度上具有相同的行为,前一种情况,out和ref关键字允 许被调用方法直接操作一个值类型实例,调用代码必须为该实例分配内存,被调用方法操作该内存。对于后一种情况,调用代码为引用类型对象分配内存,而被调用 方法通过传入的引用(指针)来操作对象。看下面代码:

代码

class Program { static void Main(string[] args) { FileStream fs; //打开第一个文件 StartProcessingFiles(ref fs); for (; fs != null; ContinueProcessingFiles(ref fs)) { //处理文件 fs.Read(); } } static void StartProcessingFiles(ref FileStream fs) { fs = new FileStream(); } static void ContinueProcessingFiles(ref FileStream fs) { fs.Close();//关闭上一次操作的文件 //打开下一个文件,没有返回null if (noFiles) fs = null; else { fs = new FileStream(); } } }

该代码最大的不同在于有着out和ref修饰的引用类型参数的方法创建一个对象后,指向新对象的指针会返回到调用代码。

下面代码演示了使用ref来交换两个引用:

static public void Swap(ref object a, ref object b) { object c = a; a = b; b = c; }

CLR为了确保类型安全,按引用传递的变量必须和方法声明的参数类型完全相同,如下面代码将会报错:

代码

class Program { static void Main(string[] args) { string s1 = "heaiping"; string s2 = "hap"; Swap(ref s1, ref s2); } static public void Swap(ref object a, ref object b) { object c = a; a = b; b = c; } }

因为object和string不匹配,要编译同步代码必须修改为:

代码

class Program { static void Main(string[] args) { string s1 = "heaiping"; string s2 = "hap"; Swap(ref s1, ref s2); Console.WriteLine(s1); Console.WriteLine(s2); } static public void Swap(ref string a, ref string b) { string c = a; a = b; b = c; } }

修正后编译通过并得到期望值。

欢迎转载,请注明出处

[转载]PowerDesigner教程系列(六

mikel阅读(932)

[转载]PowerDesigner教程系列(六) – ㊣尐懶蟲 – 博客园.

目标:本文主要介绍联系的定义及使用。

一、 联系
联系(Relationship)是 指实体集这间或实体集内部实例之间的连接。

实体之间可以通过联系来相互关联。与实体和实体集对应,联系也可以分为联系和联系集,联系集 是实体集之间的联系,联系是实体之间的联系,联系是具有方向性的。联系和联系集在含义明确的情况之下均可称为联系。

按照实体类型中实例 之间的数量对应关系,通常可将联系分为4类,即一对一(ONE TO ONE)联系、一对多(ONE TO MANY)联系、多对一(MANY TO ONE)联系和多对多联系(MANY TO MANY)。

二、 建立联系
在CDM工具选项板中除了公共的 工具外,还包括如下图所示的其它对象产生工具。

在图形窗口中创建两个实体后,单击“实体间建立联系”工具,单击一个实体,在按下鼠标左键的同时把光标拖至别一个实体上 并释放鼠标左键,这样就在两个实体间创建了联系,右键单击图形窗口,释放Relationship工具。如下图所示

三、 四种基本的联系
即一对一(ONE TO ONE)联系、一对多(ONE TO MANY)联系、多对一(MANY TO ONE)联系和多对多联系(MANY TO MANY)。如图所示

四、 其他几类特殊联系

除了4种基本的联系之外,实体集与实体集之间 还存在标定联系(Identify Relationship)、非标定联系(Non-Identify RelationShip)和递归联系(Recursive Relationship)。

标定联系:
每 个实体类型都有自己的标识符,如果两个实体集之间发生联系,其中一个实体类型的标识符进入另一个实体类型并与该实体类型中的标识符共同组成其标识符时,这 种联系则称为标定联系,也叫依赖联系。反之称为非标定联系,也叫非依赖联系。
注意:
在非标定联系中,一个实体集中的部分实例依赖于另一个实例集中的实例,在这种依赖联系中,每个实体必须至少有一个标识符。而在 标定联系中,一个实体集中的全部实例完全依赖于另个实体集中的实例,在这种依赖联系中一个实体必须至少有一个标识符,而另一个实体却可以没有自己的标识 符。没有标识符的实体用它所依赖的实体的标识符作为自己的标识符。

换句话来理解,在标定联系中,一个实体(选课)依赖 一个实体(学生),那么(学生)实体必须至少有一个标识符,而(选课)实体可以没有自己的标识符,没有标标识符的实体可以用实体(学生)的标识符作为自己 的标识符。

递归联系:
递归联系是实体集 内部实例之间的一种联系,通常形象地称为自反联系。同一实体类型中不同实体集之间的联系也称为递归联系。

例如:在“职工”实体集中存在很 多的职工,这些职工之间必须存在一种领导与被领导的关系。又如“学生”实体信中的实体包含“班长”子实体集与“普通学生”子实体集,这两个子实体集之间的 联系就是一种递归联系。创建递归联系时,只需要单击“实体间建立联系”工具从实体的一部分拖至该实体的别一个部分即可。如图

五、 定义联系的特性

在两个实体间建立了联系后,双击联系 线,打开联系特性窗口,如图所示。

六、 定义联系的角色名
在 联系的两个方向上各自包含有一个分组框,其中的参数只对这个方向起作用,Role Name为角色名,描述该方向联系的作用,一般用一个动词或动宾组表。
如: “学生 to 课目 ” 组框中应该填写“拥有”,而在“课目To 学生”组框中填写“属于”。(在此只是举例说明,可能有些用词不太合理)。

七、 定义联系的强制性
Mandatory 表洋这个方向联系的强制关系。选中这个复选框,则在联系线上产生一个联系线垂直的竖线。不选择这个复选框则表示联系这个方向上是可选的,在联系线上产生一 个小圆圈。

八、 有关联系的基数
联系具有方向性,每个方向上都有一个基数。

举 例,
“系”与“学生”两个实体之间的联系是一对多联系,换句话说“学生”和“系”之间的联系是多对一联系。而且一个学生必须属于一个系,并且只能 属于一个系,不能属于零个系,所以从“学生”实体至“系”实体的基数为“1,1”,从联系的另一方向考虑,一个系可以拥有多个学生,也可以没有任何学生, 即零个学生,所以该方向联系的基数就为“0,n”,如图所示

技术中的菜鸟!正在吸取知识的土壤,希望能取得成功!不嫌弃我是菜 鸟的,愿交天下好友!

[转载]WF4.0实战(十):分布式酒店订房系统

mikel阅读(1163)

[转载]WF4.0实战(十):分布式酒店订房系统 – 海纳百川 – 博客园.

这篇文章主要是实现一个分布式的酒店订房功能。主要阐述如何通过WCF加WF实现一个分布式系统模型。

这个Demo的场景说明:

一家酒店将房间信息存储在SQL Server数据库中,酒店的工作人员根据客户的要求的房间类型,查询出房间的价格,告诉用户价格,决定是否订房。

分布式订房系统系统设计图:

上图说明:

1、通过ADO.NET Data Serivce访问数据库,它使用ADO.NET Entity DataModel和WCF Data Service构建一个数据库增删查改的WCF服务。

2、WF4.0 Service是WF4.0的WCF服务,通过WCF访问ADO.NET Data Serivce。

3、WF4.0 Client和.NET Windows client是两个客户端,他们的功能是相同的,通过Internet访问WF4.0 Service。

传统的分布式设计图:

比较一下两张图片,你会发现:

1、传统的数据库访问是使用SQL Helper。

2、将业务逻辑宿主在Web Service中,现在是宿主在WF4.0和WCF结合的服务中。

实现:下面我将一步一步实现这个Demo

1、创建数据库,就一张表如下图:

2、创建ADO.NET Data Service:

新建一个RentRoom空项目,添加一个ASP.NET应用程序RentRoomDataService,删除所有的aspx和cs文件。添加一 个ADO.NET Entity DataModel项目,将其命名为myModel.edmx。如下图:

选择Generates from database,点下一步,新建一个连接字符串,如下图:

点击OK,点击next,选择表Room,点击完成,如下图:

在RentRoomDataService中添加一个WCF Data Service,命名为MyWcfDataService.svc,如下图:

修改MyWcfDataService.svc.cs代码:

1 public class MyWcfDataService : DataService<RentRoomEntities2>
2 {
3 // This method is called only once to initialize service-wide policies.
4 public static void InitializeService(DataServiceConfiguration config)
5 {
6 config.SetEntitySetAccessRule(*, EntitySetRights.All);
7 config.SetServiceOperationAccessRule(*, ServiceOperationRights.All);
8 }
9 }

这样ADO.NET Data Service创建完成。

3、创建RentRoomCustomActivities,它包括三个自定义活动:

先添加MyWcfDataService服务引用,如下图:

GetInput用于接收等待用户输入,代码如下:

1 public class GetInput : CodeActivity
2 {
3 OutArgument<string> data;
4 public OutArgument<string> Data
5 {
6 get { return data; }
7 set { data = value; }
8 }
9
10 protected override void Execute(CodeActivityContext context)
11 {
12 string input = Console.ReadLine();
13 context.SetValue(data, input);
14 }
15 }

CheckPrice用于查询房间的价格,调用ADO.NET Data Service,代码如下:

1 public class CheckPrice : CodeActivity
2 {
3 InArgument<string> roomID;
4 public InArgument<string> RoomID
5 {
6 get { return roomID; }
7 set { roomID = value; }
8 }
9
10 OutArgument<decimal> roomPrice;
11 public OutArgument<decimal> RoomPrice
12 {
13 get { return roomPrice; }
14 set { roomPrice = value; }
15 }
16
17 protected override void Execute(CodeActivityContext context)
18 {
19 string carId = RoomID.Get(context);
20
21 String urlstr = http://localhost:40438/MyWcfDataService.svc;
22 CarRentalReference.RentRoomEntities2 proxy = new CarRentalReference.RentRoomEntities2(new Uri(urlstr));
23
24 var query = (from c in proxy.Room
25 where c.RoomID == carId
26 select c).First();
27
28 decimal? price = query.RoomPrice;
29
30 context.SetValue(RoomPrice, price);
31 }
32
33
34 }

BookRoom用于确定订房,调用ADO.NET Data Service,代码如下:

1 public class BookRoom : CodeActivity
2 {
3 InArgument<string> roomId;
4 public InArgument<string> RoomId
5 {
6 get { return roomId; }
7 set { roomId = value; }
8 }
9
10 protected override void Execute(CodeActivityContext context)
11 {
12 string carId = RoomId.Get(context);
13
14 String urlstr = http://localhost:40438/MyWcfDataService.svc;
15 CarRentalReference.RentRoomEntities2 proxy = new CarRentalReference.RentRoomEntities2(new Uri(urlstr));
16
17 var query = (from c in proxy.Room
18 where c.RoomID == carId
19 select c);
20
21 foreach (CarRentalReference.Room room in query)
22 {
23 room.Quantity = room.Quantity 1;
24 proxy.UpdateObject(room);
25 proxy.SaveChanges();
26 break;
27 }
28
29 }
30
31
32 }

RentRoomCustomActivities创建完成。

4、创建RentRoomWFService。这是设计图上的WF4.0 Service。

这个服务由两个ReceiveAndSendReply构成,第一个ReceiveAndSendReply,如下图:

第二个ReceiveAndSendReply,如下图:

ReceiveAndSendReply具体的设置参考:WF4.0实战(三):WCF服务WF4.0 基础篇 (二十七) WCF Workflow Service 在WCF中使用WF

在Program.cs中写启动这个服务的代码,如下:

1 static void Main(string[] args)
2 {
3 string baseAddress = http://localhost:8090/RentRoomService;
4
5
6
7 using (WorkflowServiceHost host =
8 new WorkflowServiceHost(new Workflow1(), new Uri(baseAddress)))
9 {
10 host.Description.Behaviors.Add(new
11 ServiceMetadataBehavior() { HttpGetEnabled = true });
12 host.AddDefaultEndpoints();
13
14 host.Open();
15 Console.WriteLine(Rent Room service listening at: +
16 baseAddress);
17 Console.WriteLine(Press ENTER to exit);
18 Console.ReadLine();
19 host.Close();
20 }
21 }
22 }

这样RentRoomWFService完成。

5、客户端RentRoomWidowsClient

启动RentRoomWFService,在RentRoomWidowsClient添加RentRoomWFService引用,如下图:

使用下面代码模拟订房:

1 ServiceReference1.RentRoomDataContract contract = new ServiceReference1.RentRoomDataContract();
2 contract.RoomId = gaoji;
3 contract.CustomerId = 012;
4 ServiceReference1.ServiceClient proxy = new ServiceReference1.ServiceClient();
5 decimal? test = proxy.CheckPrice(contract);
6 bool? test1 = proxy.BookRoom(012);

6、客户端RentRoomWFClient

定义一个工作流调用WF服务,详见RentRoomWFClient.Workflow1.xaml。

启动RentRoomWFService服务,如下图:

运行效果图:

本文参考:

WF4.0 基础篇 (二十七) WCF Workflow Service 在WCF中使用WF

ADO.NET Data Service

Introducing WF4.0: Building Distributed Apps with WF 4.0 and WF 4.0 Services

代码:/Files/zhuqil/RentRoomWFClient.rar

数据库:/Files/zhuqil/db.rar

作者:朱祁林
出处:http://zhuqil.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]一步一步打造WebIM(2)——消息缓存

mikel阅读(842)

[转载]一步一步打造WebIM(2)——消息缓存 – 卢春城专栏 – 博客园.

一步一步打造WebIM(1)一文中,已经介绍了如何实现一个简单的WebIM,但是,这个WebIM有一个问 题,就是每一次添加消息监听器时,都必须访问一次数据库去查询是否有消息,显然,如果用户比较多时,必然对数据库的压力比较大。解决这个问题的一个方法就 是先将消息缓存在内存中,不立即写入数据库,等到缓存满了才写入数据库。本文将介绍如何实现 消息缓存。

基本思路

实现一个消息缓存管理类,以用户为单位缓存所有消息,每一个用户对应着一个 List<Message>,保存着该用户新收到的消息,消息缓存管理用一个Hashtable保存着所有用户对应的 List<Message>。

具体实现代码如下:

public class MessageCacheManagement
{
    static MessageCacheManagement m_Instance = new MessageCacheManagement();

    static public MessageCacheManagement Instance
    {
        get { return m_Instance; }
    }

    private MessageCacheManagement()
    {
    }

    Int32 m_Count = 0;
    Hashtable m_Cache = new Hashtable();

    List<Message> GetUserMessageCache(String user)
    {
        if (!m_Cache.ContainsKey(user))
        {
            m_Cache.Add(user, new List<Message>());
        }

        return m_Cache[user] as List<Message>;
    }

    /// <summary>
    /// 清除缓存
    /// </summary>
    public void Clear()
    {
        lock (m_Cache)
        {
            List<Message> msgs = new List<Message>();
            foreach (DictionaryEntry ent in m_Cache)
            {
                (ent.Value as List<Message>).Clear();
            }
            m_Count = 0;
        }
    }

    /// <summary>
    /// 获取所有缓存的消息
    /// </summary>
    /// <returns></returns>
    public List<Message> GetAll()
    {
        lock (m_Cache)
        {
            List<Message> msgs = new List<Message>();
            foreach (DictionaryEntry ent in m_Cache)
            {
                foreach (Message msg in ent.Value as List<Message>)
                {
                    msgs.Add(msg);
                }
            }
            return msgs;
        }
    }

    /// <summary>
    /// 获取某一用户缓存的消息的最小时间 
    /// </summary>
    public Nullable<DateTime> GetMinCreatedTime(string user)
    {
        lock (m_Cache)
        {
            List<Message> userMsgs = GetUserMessageCache(user);
            return userMsgs.Count == 0 ? null : new Nullable<DateTime>(userMsgs[0].CreatedTime);
        }
    }


    /// <summary>
    /// 在缓存中插入一条消息
    /// </summary>
    /// <param name="user"></param>
    /// <param name="msg"></param>
    public void Insert(String user, Message msg)
    {
        List<Message> userMsgs = null;

        lock (m_Cache)
        {
            userMsgs = GetUserMessageCache(user);
        }

        lock (userMsgs)
        {
            userMsgs.Add(msg);
            m_Count++;
        }
    }

    /// <summary>
    /// 查找缓存中接受者为user,发送时间大于from的消息
    /// </summary>
    public List<Message> Find(String user, DateTime from)
    {
        List<Message> userMsgs = null;

        lock (m_Cache)
        {
            userMsgs = GetUserMessageCache(user);
        }

        lock (userMsgs)
        {
            List<Message> msgs = new List<Message>();

            int i = 0;
            while (i < userMsgs.Count && userMsgs[i].CreatedTime <= from) i++;

            while (i < userMsgs.Count) { msgs.Add(userMsgs[i]); i++; }

            return msgs;
        }
    }

    /// <summary>
    /// 获取消息总量
    /// </summary>
    public Int32 Count
    {
        get { return m_Count; }
    }
}

添加消息监听器

增加消息缓存后,添加消息监听器的流程也要修改,具体思路是先获取消息接收者在缓存中发送时间最早的消息的发送时间,显然,如果监听器的From大于或等于这个最小发送时间时,无需访问数据库,可以直接访问缓存。具体代码修改 为:

/// <summary>
/// 添加消息监听器,如果查找到符合监听器条件的消息,返回false,此时不会添加监听器
/// 如果没有查找到符合监听器条件的消息,返回true,此时监听器将被添加到m_Listeners中
/// </summary>
public bool AddListener(String receiver, String sender, Nullable<DateTime> from, WebIM_AsyncResult asynResult)
{
    MessageListener listener = new MessageListener(receiver, sender, from, asynResult);
    lock (m_Lock)
    {
        if (!m_Listeners.ContainsKey(receiver))
        {
            m_Listeners.Add(receiver, new List<MessageListener>());
        }
        List<MessageListener> listeners = m_Listeners[receiver] as List<MessageListener>;

        //获取用户receiver缓存的消息的最小发送时间
        Nullable<DateTime> min = MessageCacheManagement.Instance.GetMinCreatedTime(receiver);

        List<Message> messages = new List<Message>();

        //当from >= 缓存在内存中的消息的最小时间时,不必查询数据库
        if (min == null || from == null || from.Value < min.Value)
        {
            //查询数据库
            messages.AddRange(Find(receiver, sender, from));
        }

        //在缓存中查询
        messages.AddRange(MessageCacheManagement.Instance.Find(receiver, from.Value));

        if (messages.Count == 0)
        {
            //插入监听器
            listeners.Add(listener);
        }
        else
        {
            //发送消息
            listener.Send(messages);
        }
        return messages.Count == 0;
    }
}

发送消息

增加消息缓存后,发送消息的流程也要修改,具体思路是:先将消息保存到缓存中,之后判断缓存的消息的 总数,如果超过设定的上限,就将消息写入数据库。具体代码修改为(您可以通过修改MAX_CACHE_COUNT修改缓存消息数的上限):

/// <summary>
/// 插入新的消息,插入消息后将查询m_Listeners中是否有符合条件的监听器,如存在,同时将消息发送出去
/// </summary>
public Message NewMessage(String receiver, String sender, DateTime createdTime, String content)
{
    lock (m_Lock)
    {
        Message message = new Message(sender, receiver, content, createdTime, ++m_MaxKey);

        List<Message> messages = new List<Message>();
        messages.Add(message);

        if (m_Listeners.ContainsKey(receiver))
        {
            List<MessageListener> listeners = m_Listeners[receiver] as List<MessageListener>;
            List<MessageListener> removeListeners = new List<MessageListener>();
            foreach (MessageListener listener in listeners)
            {
                if ((listener.Sender == "*" || String.Compare(listener.Sender, sender, true) == 0) &&
                    (listener.From == null || message.CreatedTime > listener.From))
                {
                    listener.Send(messages);
                    removeListeners.Add(listener);

                    System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(listener.Complete));
                }
            }

            foreach (MessageListener listener in removeListeners)
            {
                //移除监听器
                listeners.Remove(listener);
            }
        }

        MessageCacheManagement.Instance.Insert(receiver, message);

        if (MessageCacheManagement.Instance.Count >= MAX_CACHE_COUNT)
        {//超过缓存的最大值,将缓存中的消息全部写入数据库
            //启动事务
            SQLiteTransaction trans = m_Conn.BeginTransaction();

            try
            {
                List<Message> cacheMsgs = MessageCacheManagement.Instance.GetAll();

                foreach (Message msg in cacheMsgs)
                {
                    SQLiteCommand cmd = new SQLiteCommand(
                        "insert into Message (Receiver,Sender,Content,CreatedTime,Key) values (?,?,?,?,?)",
                        m_Conn
                    );
                    cmd.Parameters.Add("Receiver", DbType.String).Value = msg.Receiver;
                    cmd.Parameters.Add("Sender", DbType.String).Value = msg.Sender;
                    cmd.Parameters.Add("Content", DbType.String).Value = msg.Content;
                    cmd.Parameters.Add("CreatedTime", DbType.DateTime).Value = msg.CreatedTime;
                    cmd.Parameters.Add("Key", DbType.Int64).Value = msg.Key;

                    cmd.ExecuteNonQuery();
                }

                trans.Commit();
            }
            catch
            {
                trans.Rollback();
            }

            MessageCacheManagement.Instance.Clear();
        }

        return message;
    }
}

源代码下载此次源代码仅修改了MessageManagement.cs文件,如果您有任何问题,可通过WebIM和我联系

如果本文对您有所帮助,别忘了点推荐

[转载]一步一步打造WebIM(1)

mikel阅读(1046)

[转载]一步一步打造WebIM(1) – 卢春城专栏 – 博客园.

之前笔者发布的云翔在线软件平台中 已经包含了一个功能相对比较齐全的WebIM,这个系列的文章就是介绍如何开发出功能类似的WebIM,在文章开始前,先介绍一下相关的技术:

1.Comet

Comet 是一种新的 Web 应用架构。基于这种架构开发的应用中,服务器端会主动以异步的方式向客户端程序推送数据,而不需要客户端显式的发出请求。Comet 架构非常适合事件驱动的 Web 应用,以及对交互性和实时性要求很强的应用,如股票交易行情分析、聊天室和 Web 版在线游戏等。

在.NET 要实现Comet就要用到IHttpAsyncHandler,在开始阅读文章前,建议先了解一下IHttpAsyncHandler

2.Lesktop

Lesktop是一款用于开发RIA网站的开源JS界面库,Lesktop提供了一个功能强大的可视化开发工具帮助您快速的开发RIA网站。这个系列介绍的WebIM的前 台UI将使用Lesktop来开发。

接下来,将开始今天的主题,开发一个简单的 WebIM,这个WebIM将使用Comet技术,从而避免在客户端和服务端轮询,提高WebIM的性能(目 前主要实现能够聊天,其他功能会在以后不断完善)。客户端界面在这就不详细介绍了,用Lesktop拖拖控件就可以了,效果如下:

1

1.基本思路

Comet便 是指服务器推技术。它的实现方式是在浏览器与服务器之间建立一个长连接,待获得消息之后立即返回。否则持续 等待,直至超时。客户端得到消息或超时之后,又会立即建立另一个长连接。Comet技术的最大优势,自然就是很高的即使性。在.NET中实 现这种方式并不困难,用IHttpAsyncHandler即可。

接收消息的流程:

clip_image001

发送消息流程:

clip_image001[8]

发送消息和添加监听器将由一个类型为 MessageManagement对象来负责,

添加监听器代码如下:

/// <summary>
/// 添加消息监听器,如果查找到符合监听器条件的消息,返回false,此时不会添加监听器
/// 如果没有查找到符合监听器条件的消息,返回true,此时监听器将被添加到m_Listeners中
/// </summary>
public bool AddListener(String receiver, String sender, Nullable<DateTime> from, WebIM_AsyncResult asynResult)
{
    MessageListener listener = new MessageListener(receiver, sender, from, asynResult);
    lock (m_Lock)
    {
        if (!m_Listeners.ContainsKey(receiver))
        {
            m_Listeners.Add(receiver, new List<MessageListener>());
        }
        List<MessageListener> listeners = m_Listeners[receiver] as List<MessageListener>;

        //查找消息
        List<Message> messages = Find(receiver, sender, from);

        if (messages.Count == 0)
        {
            //插入监听器
            listeners.Add(listener);
        }
        else
        {
            //发送消息
            listener.Send(messages);
        }
        return messages.Count == 0;
    }
}

发送消息代码如下:

/// <summary>
/// 插入新的消息,插入消息后将查询m_Listeners中是否有符合条件的监听器,如存在,同时将消息发送出去
/// </summary>
public Message NewMessage(String receiver, String sender, DateTime createdTime, String content)
{
    lock (m_Lock)
    {
        Message message = new Message(sender, receiver, content, createdTime, ++m_MaxKey);

        SQLiteCommand cmd = new SQLiteCommand(
            "insert into Message (Receiver,Sender,Content,CreatedTime,Key) values (?,?,?,?,?)",
            m_Conn
        );
        cmd.Parameters.Add("Receiver", DbType.String).Value = message.Receiver;
        cmd.Parameters.Add("Sender", DbType.String).Value = message.Sender;
        cmd.Parameters.Add("Content", DbType.String).Value = message.Content;
        cmd.Parameters.Add("CreatedTime", DbType.DateTime).Value = message.CreatedTime;
        cmd.Parameters.Add("Key", DbType.Int64).Value = message.Key;

        cmd.ExecuteNonQuery();

        List<Message> messages = new List<Message>();
        messages.Add(message);

        if (m_Listeners.ContainsKey(receiver))
        {
            List<MessageListener> listeners = m_Listeners[receiver] as List<MessageListener>;
            List<MessageListener> removeListeners = new List<MessageListener>();
            foreach (MessageListener listener in listeners)
            {
                if ((listener.Sender == "*" || String.Compare(listener.Sender, sender, true) == 0) && 
                    (listener.From == null || message.CreatedTime > listener.From))
                {
                    listener.Send(messages);
                    removeListeners.Add(listener);

                    System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(listener.Complete));
                }
            }

            foreach (MessageListener listener in removeListeners)
            {
                //移除监听器
                listeners.Remove(listener);
            }
        }

        return message;
    }
}

2.使用IHttpAsyncHandler实现Comet

IHttpAsyncHandler的介绍可以查阅下msdn,以下是接收消息的源代码,主要是重写BeginProcessRequest和 EndProcessRequest:

public class WebIM_ReceiveHandler : IHttpAsyncHandler
{
    public WebIM_ReceiveHandler()
    {
    }

    HttpContext m_Context = null;

    IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
    {
        m_Context = context;

        System.IO.Stream inputStream = context.Request.InputStream;
        Byte[] buffer = new Byte[inputStream.Length];
        inputStream.Read(buffer, 0, (int)inputStream.Length);
        string content = context.Request.ContentEncoding.GetString(buffer);
        Hashtable data = Utility.ParseJson(content) as Hashtable;

        WebIM_AsyncResult asyncResult = new WebIM_AsyncResult(cb, extraData);
        Nullable<DateTime> from = data.ContainsKey("From") ? new Nullable<DateTime>((DateTime)data["From"]) : null;

        if (!MessageManagement.Instance.AddListener(data["Receiver"] as string, data["Sender"] as string, from, asyncResult))
        {
            //已有消息,发送消息并结束链接
            asyncResult.Complete(null);
        }

        return asyncResult;
    }

    void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
    {
        //将消息发送到客户端
        WebIM_AsyncResult asyncResult = result as WebIM_AsyncResult;
        asyncResult.Send(m_Context);
    }

    void IHttpHandler.ProcessRequest(HttpContext context)
    {
    }

    bool IHttpHandler.IsReusable
    {
        get { return true; }
    }
}

public class WebIM_AsyncResult : IAsyncResult
{
    AsyncCallback m_AsyncCallback = null;
    object m_Data = null;
    bool m_IsCompleted = false;

    public WebIM_AsyncResult(AsyncCallback callback, Object extraData)
    {
        m_Data = extraData;
        m_AsyncCallback = callback;
    }

    bool IAsyncResult.IsCompleted { get { return m_IsCompleted; } }

    bool IAsyncResult.CompletedSynchronously { get { return false; } }

    WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }

    Object IAsyncResult.AsyncState { get { return m_Data; } }

    StringBuilder m_Cache = new StringBuilder();

    public void Write(object content)
    {
        m_Cache.Append(content.ToString());
    }

    public void Send(HttpContext context)
    {
        context.Response.Write(m_Cache.ToString());
    }

    public void Complete(object data)
    {
        m_AsyncCallback(this);
        m_IsCompleted = true;
    }
}

3.客户端接收消息

客户端接收消息并不复杂,只需要发送请求,返回后在发送另一个请求即可,代码如下:

function Receive()
{
    var data = {
        Receiver: User,
        Sender: Peer,
        From: m_From
    };

    function Receive_Error(ex)
    {
        alert(ex);
        m_ErrorCount++;
        if (m_ErrorCount < 5)
        {
            //发送下一个请求
            setTimeout(Receive, 1000);
        }
    }

    function Receive_Callback(xml, text)
    {
        m_ErrorCount = 0;
        
        //将JSON转成数据
        var ret = System.ParseJson(text);
        
        //显示消息
        for (var i in ret.Messages)
        {
            m_MsgPanel.AddMessage(ret.Messages[i]);
        }
        if (ret.Messages.length > 0)
        {
            m_From = ret.Messages[ret.Messages.length - 1].CreatedTime;
        }
        
        //发送下一个请求
        setTimeout(Receive, 50);
    }

    System.Post(Receive_Callback, Receive_Error, "recevie.aspx", System.RenderJson(data));
}

WebIM 源代码(注意:开发时起始页为WebIM_Dev.htm,直接打开起始页为WebIM.htm)

一个简单的WebIM就先介绍到这里,如果您有任何问题,可以通过WebIM与我联系。


以下是广告时间,主要介绍云翔在线软 件平台的最新动态。此次更新主要修改了平台的操作界面,新的界面将平台所有功能入口放到了网页的上部分,其余版面用于浏览网页,您可以自己设定一 个常用的页面作为首页,系统启动后将自动打开您设置的页面。如图所示:

image17

尽管使用了新的界面,原来的桌面模式仍然保留,您可以在登录时切换登录模式:

image

[转载]PowerDesigner教程系列(三)概念数据模型

mikel阅读(822)

[转载]PowerDesigner教程系列(三)概念数据模型 – Kevin Yin – 博客园.

目标:
本文主要介绍属性的标准检查约束、如何定义属性的附加检查。

一、 定义属性的标准检查约束
标准检查约束是一组确保属性有效的表达式。在实体属性的特性窗口,打开如图所示的检查选项卡。

在这个选项卡可以定义属性的标准检查约束,窗口中每项的参数的含义,如下

参数 说明
Minimum 属性可接受的最小数
Maximum 属性可接受的最大数
Default 属性不赋值时,系统提供的默认值
Unit 单位,如公里、吨、元
Format 属性的数据显示格式
Lowercase 属性的赋值全部变为小写字母
Uppercase 属性的赋值全部变为大写字母
Cannot modify 该属性一旦赋值不能再修改
List Of Values 属性赋值列表,除列表中的值,不能有其他的值
Label 属性列表值的标签


二、定义属性的附加检查
当Standard checks 或Rules 不能满足检查的要求时,可以在Additional Checks选项卡的Server子页上,通过SQL语句中使用%MINMAX%、%LISTVAL%、%RULES%、%UPPER%、%LOWER% 几个变量来定义Standard和Rule,如图所示

%MINMAX%、%LISTVAL%、%UPPER%、%LOWER%
在Standard Check中定义的Minimum 和Maximum、List values 、uppervalues、lowervalues

%RULES%
在 Rules特性窗口Expression选项卡中定义的有效性规则表达式

待续。

[转载]PowerDesigner教程系列(二)概念数据模型

mikel阅读(894)

[转载]PowerDesigner教程系列(二)概念数据模型 – Kevin Yin – 博客园.

目标:
本文主要介绍PowerDesigner概念数据模型以及实体、属性创建。

一、 新建概念数据模型
1)选择File–>New,弹出如图所示对话框,选择CDM模型(即概念数据模型)建立模型。

2)完成概念数据模型的创建。以下图示,对当前的工作空间进行简单介绍。(以后再更详细说明)

3)选择新增的CDM模型,右击,在弹出的菜单中选择“Properties”属性项,弹出如图所示对话框。在 “General”标签里可以输入所建模型的名称、代码、描述、创建者、版本以及默认的图表等等信息。在“Notes”标签里可以输入相关描述及说明信 息。当然再有更多的标签,可以点击 “More>>”按钮,这里就不再进行详细解释。

二、创建新实体
1) 在CDM的图形窗口中,单击工具选项版上的Entity工具,再单击图形窗口的空白处,在单击的位置就出现一个实体符号。点击Pointer工具或右击鼠 标,释放Entitiy工具。如图所示

2)双击刚创建的实体符号,打开下列图标窗口,在此窗口“General”标签中可以输入实体的名称、代码、描述等信息。

三、添加实体属性
1)在上述窗口的“Attribute”选项标签上 可以添加属性,如下图所示。

注意:
数据项中的“添加属性”和“重用已有数据项”这两项功能与模型中Data Item的Unique code 和Allow reuse选项有关。
P列表示该属性是否为主标识符;D列表示该属性是否在图形窗口中显示;M列表示该属性是否为强制 的,即该列是否为空值。

如果一个实体属性为强制的,那么, 这个属性在每条记录中都必须被赋值,不能为空。

2)在上图所示窗口中,点击插入属性按钮,弹出属性对话框,如下图所示。

注意:这里涉及到域的概念,即一种标准的数据结构,它可应用至数据项或实体的属性上。在以下的 教程中将另立章节详细说明。

待续。

[转载]PowerDesigner教程系列(一)概念数据模型

mikel阅读(845)

[转载]PowerDesigner教程系列(一)概念数据模型 – Kevin Yin – 博客园.

目标:
本 文主要介绍PowerDesigner中概念数据模型 CDM的基本概念。

一、概念数据模型概述
数 据模型是现实世界中数据特征的抽象。数据模型应该满足三个方面的要求:
1)能够比较真实地模拟现实世界
2)容易为人所理解
3)便 于计算机实现

概念数据模型也称信息模型,它以实体-联系(Entity-RelationShip,简称E-R)理论为基础,并对这一理 论进行了扩充。它从用户的观点出发对信息进行建模,主要用于数据库的概念级设计。

通常人们先将现实世界抽象为概念世界,然后再将概念世界 转为机器世界。换句话说,就是先将现实世界中的客观对象抽象为实体(Entity)和联系(Relationship),它并不依赖于具体的计算机系统或 某个DBMS系统,这种模型就是我们所说的CDM;然后再将CDM转换为计算机上某个DBMS所支持的数据模型,这样的模型就是物理数据模型,即PDM。

CDM 是一组严格定义的模型元素的集合,这些模型元素精确地描述了系统的静态特性、动态特性以及完整性约束条件等,其中包括了数据结构、数 据操作和完整性约束三部分。
1)数据结构表达为实体和属性;
2)数据操作表达为实体中的记录的插入、删除、修改、查询等操作;
3) 完整性约束表达为数据的自身完整性约束(如数据类型、检查、规则等)和数据间的参照完整性约束(如联系、继承联系等);

二、 实体、属性及标识符的定义
实体(Entity),也称为实例,对应现实世界中可区别于其他对象的“事件”或“事物”。例如,学 校中的每个学生,医院中的每个手术。
每个实体都有用来描述实体特征的一组性质,称之为属性,一个实体由若干个属性来描述。如学生实体可由学号、姓 名、性别、出生年月、所在系别、入学年份等属性组成。

实体集(Entity Set)是具体相同类型及相同性质实体的集合。例如学校所有学生的集合可定义为“学生”实体集,“学生”实体集中的每个实体均具有学号、姓名、性别、出生 年月、所在系别、入学年份等性质。

实体类型(Entity Type)是实体集中每个实体所具有的共同性质的集合,例如“患者”实体类型为:患者{门诊号,姓名,性别,年龄,身份证 号………….}。实体是实体类型的一个实例,在含义明确的情况下,实体、实体类型通常互换使用。

实体类型中的每个实体包 含唯一标识它的一个或一组属性,这些属性称为实体类型的标识符(Identifier),如“学号”是学生实体类型的标识符,“姓名”、“出生日期”、 “信址”共同组成“公民”实体类型的标识符。

有些实体类型可以有几组属性充当标识符,选定其中一组属性作为实体类型的主标识符,其他的作 为次标识符。

三、实体、属性及标识符的表达

待续!

[转载]>>关于MVC与三层架构

mikel阅读(983)

[转载]>>关于MVC与三层架构 – ProLiuzt – 博客园.

我认为MVC设计模式,关键在于构建Model,Model就是MVC模式的灵魂,他包含了三层架构里面的 “实体规范层”、“行为规则层”、“数据访问层”;控制器(Controller)用来收集View提供的用户数据,传递给Model,同时返回 Model处理后的数据给View。Model的设计可以参考三层架构的设计方法,将实体、行为规则(业务逻辑)和数据访问分开,在数据访问上可以应用 ORM框架。三层架构同样可以应用ORM框架。个人认为三层架构和MVC都是很好的设计方法,目的都是降低系统的耦合性,提高重用率,提高系统的可维护 性,可以根据喜好进行选择。

如何在三层架构和mvc之间进行取舍呢?或者说它们就和我所理解的一样,根据喜好选择,没有实质的优劣。同样是架构级 别的,相同的地方在于他们都有一个表现层,但是他们不同的地方在于其他的两个层。在三层架构中没有定义Controller的概念。这是我认为最不同的地 方。而MVC也没有把业务的逻辑访问看成两个层,这是采用三层架构或MVC搭建程序最主要的区别。当然了。在三层中也提到了Model,但是三层架构中 Model的概念与MVC中Model的概念是不一样的,“三层”中典型的Model层是以实体类构成的,而MVC里,则是由业务逻辑与访问数据组成的。

三层架构是最基本的项目分层结果,而MVC则是三层架构的一个变体,MVC是一种好的开 发模式。首先你要明白MVC分别代表的是什么意思:

M 即Model(模型层),主要负责出来业务逻辑以及数据库的交互 
V 即View(视图层),主要用于显示数据和提交数据 
C 即Controller(控制器),主要是用作捕获请求并控制请求转发

三层:UI 界面层 BLL 业务逻辑层,DAL数据访问层,Model 实体层 
MVC中的的M 不是三层中的Model(实体层),他其实包括三层中的 BLL,DAL,Model,这是非常要注意的,这也是他们之间的区别的关键所在。其优点如下: A、低耦合性; B、高重用性和可适用性; C、较低的生命周期成本;D、快速的部署;E、可维护性;F、有利于软件工程化管理。当然优点也有缺点,那就是内部结构复杂,不容易理解,文件数量大,管 理难度自然也就大。

仔细研究了一下Microsoft ASP.NET MVC 1.0源代码, 
并在两个网站项目中进行了具体应用。大致的总体技术框架结构如下: 
基础结构: 全局接口、常量、公共工具类 
数据实体:定义数据操作实体,主要用于业务逻辑层与数据访问层交互 
业务模型层:定义具体业务需要的和View需要使用的模型类,用于业务逻辑层、 View(视图层)、Controller(控制器)三方交互 
数据访 问层:修改RepositoryFactory,直接延用大体框架 
业 务逻辑层:提供业务处理服务,操作业务模型和数据实体

View(视图层):主要用于显示数据和提交数据,引用业务模型层 
Controller(控制器):主要是用作捕获请求并控制请求转发,调用业务逻辑层,引用业务模型层

个人感觉对于预期扩展性很强的系统,采用这种架构还不错


MVC设计模式… 

三层架构… 
他们细分之后得 到的是:View(UI)、BIZ(BLL)、DAO(DAL)、Entity(Model)、Controller 
MVC把 BIZ(BLL)、DAO(DAL)、Model(Entity) 统一称之为 模型(MODEL),得到:View、Controller、模型(MODEL) 
三层 在我使用中 暂未体会到控制器的存在,完全是:UI、DAO、BLL

他们相同的设计理念就是:把视图设计与数据持久化进行分离,从而降低耦合性,易于扩展,提高团队开发效率。