在这个小应用中,要用到webservice,因此简单介绍一下webservice。在实际应用中从客户端传送信息至服务端,使用webservice是一个不错的方法,它是一种构建应用程序的普遍模型,可以在任何支持网络通信的操作系统中实施运行。NET平台内也建立了对Web Service的支持,包括Web Service的构建和使用。NET Framework本身就全面支持Web Service,包括服务器端的请求处理器和对客户端发送和接受SOAP消息的支持。因此不需要其他的工具或者SDK就可以完成Web Service的开发了。
那么下面我们看看如何在服务器统计用户的在线时间。
环境:visual studio 2008;.net framework 2.0;Oracle 10g
功能实现:有两个表usertable a,userinfotable b, a存放用户基本固有信息,b表存放用户使详细信息及时间。当用户上线时程序中的webservice上传一次用户启动时间,当用户下线程序中webservice再次发送用户停止使用时间,进行保存,记录了一次用户使用时间,方便进行后续处理。
设计过程如下:
一、数据库设计
1、设计数据表
字段 | 类型 | 说明 |
id | integer | 表id,自增序列,主键 |
usersequence | varchar2(100) | 用户QQ号码,非空,唯一 |
uip | varcha2r(50) | 用户ip地址,从客户端获取 |
uaddress | varchar2(100) | 用户所在地址,从客户端根据ip获取 |
usystem | varcar2(32) | 用户使用的操作系统的位数,从客户端获取 |
useraddtime | date | 用户注册时间,也就是第一次插入数据的时间 |
字段 | 类型 | 说明 |
id | integer | 表id,自增序列,主键 |
consumerid | integer | 用户id,外键,usertable表userid关联 |
ustarttime | date | 用户本次启动软件时间 |
uendtime | date | 用户本次关闭软件时间 |
usertotaltimes | integer | 用户使用累计时间 |
useraddtime | date | 用户最后一次使用时间 |
2、详细SQL
(1)、创建表,序列
create table usertable
(
id INTEGER not null,
usersequence varchar2(100),
uip varchar2(50),
uaddress VARCHAR2(100),
usystem VARCHAR2(32),
useraddtime date
)
create table userinfotable
(
id INTEGER not null,
consumerid INTEGER,
ustarttime date,
uendtime date,
usertotaltimes number(30),
useraddtime date
)
CREATE SEQUENCE USERTABLE_SEQUENCES
INCREMENT BY 1
START WITH 1
MAXVALUE 9999999999999999999
CREATE SEQUENCE USERINFOTABLE_SEQUENCES
INCREMENT BY 1
START WITH 1
MAXVALUE 9999999999999999999
(2)、存储过程
开启qq时发送信息的存储过程
create or replace procedure startqqprocedure
(qq in varchar2,ip in varchar2,address in varchar2,systemname in varchar2)
as
userid number;
infoid number;
begin
select count(a.id) into userid from usertable a where a.usersequence=qq;
if userid >0
then
update usertable a set a.uip =ip,a.uaddress=address,a.usystem=systemname where a.usersequence = qq;
else
select usertable_sequences.nextval into userid from dual;
insert into usertable (id,usersequence,uip,uaddress,usystem,useraddtime)
values(userid,qq,ip,address,systemname,sysdate);
end if;
insert into userinfotable(id,consumerid,ustarttime,uendtime,usertotaltimes,useraddtime)
values(userinfotable_sequences.nextval,userid,sysdate,sysdate,0,sysdate);
commit;
exception
when others then
rollback;
end startqqprocedure;
关闭qq时发送信息的存储过程
create or replace procedure closeqqprocedure
(qq in varchar2,ip in varchar2,address in varchar2,systemname in varchar2)
as
userid number;
begin
select count(a.id) into userid from usertable a where a.usersequence=qq;
if userid >0
then
update usertable a set a.uip =ip,a.uaddress=address,a.usystem=systemname where a.usersequence = qq;
else
select usertable_sequences.nextval into userid from dual;
insert into usertable (id,usersequence,uip,uaddress,usystem,useraddtime)
values(userid,qq,ip,address,systemname,sysdate);
insert into userinfotable(id,consumerid,ustarttime,uendtime,usertotaltimes,useraddtime)
values(userinfotable_sequences.nextval,userid,sysdate,sysdate,0,sysdate);
end if;
update userinfotable c set c.uendtime=sysdate,c.usertotaltimes= round(c.usertotaltimes)+
round(to_number(sysdate-c.ustarttime)*1440)where c.consumerid = userid;
commit;
exception
when others then
rollback;
end closeqqprocedure;
二、代码实现
1、服务器端webservice实现。
数据库部分已完成,下面来进行程序实现,使用MVC设计模式,初步订三个类,webservice,DAO层QQWebServiceDao,执行sql语句SDProvider,实现各功能分离。
看看webservice如何写。
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using QQWebApp.WebWork.Dao;
namespace QQWebApp.Service
{
/// <summary>
/// LMWebService 的摘要说明
/// </summary>
[WebService(Namespace = "QQWebservice")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class QQWebservice: System.Web.Services.WebService
{
[WebMethod]
//启动软件后
public bool FirstStart(string qq, string ip, string address,string systemname)
{
bool result = false;
result = QQWebServiceDao.UploadStartCloseSoftInfo(qq, ip, address, systemname,true);
return result;
}
[WebMethod]
//关闭软件
public bool EndStop(string qq, string ip, string address,string systemname)
{
bool result = false;
result = QQWebServiceDao.UploadStartCloseSoftInfo(qq, ip, address, systemname,false);
return result;
}
}
那么c#如何执行存储过程呢?写数据库执行类,包括数据库连接,执行sql。
using System.Data;
using System.Configuration;
using System.Collections.Generic;
using System.Data.OracleClient;
using System.Diagnostics;
using System;
namespace QQWebApp.WebWork.Data
{
public class SDProvider
{
public static string GetConnectionString()
{
string conStr = "User ID=qqsoft;data source=orcl;password=sys";
return conStr ;
}
public static int RunProcedure(string storedProcName, IDataParameter[] parameters)
{
int result = 0;
try
{
using (OracleConnection connection = new OracleConnection(GetConnectionString()))
{
connection.Open();
OracleCommand command = BuildQueryCommand(connection, storedProcName, parameters);
result = command.ExecuteNonQuery();//返回受影响的行数
}
}
catch (Exception e)
{
Debug.Assert(false, e.Message);
}
return result;
}
private static OracleCommand BuildQueryCommand(OracleConnection connection, string storedProcName,
IDataParameter[] parameters)
{
OracleCommand command = new OracleCommand(storedProcName, connection);
command.CommandType = CommandType.StoredProcedure;
foreach (OracleParameter parameter in parameters)
{
command.Parameters.Add(parameter);
}
return command;
}
}
这个类完成了存储过程的执行,参数的传递。
下面可以开始写DAO层,该层负责参数的处理,并调用SDProvider执行存储过程。
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using QQWebApp.WebWork.Data;
using System.Data.OracleClient;
namespace QQWebApp.WebWork.Dao
public class QQWebServiceDao
{
public static bool UploadStartCloseSoftInfo(string qq, string ip,string address,string systemname,bool flag)
{
int result = 0;
OracleParameter qqParameter = new OracleParameter("qq", OracleType.VarChar, 100);
qqParameter.Value = qq;
OracleParameter ipParameter = new OracleParameter("ip", OracleType.VarChar, 100);
ipParameter.Value = ip;
OracleParameter addressParameter = new OracleParameter("address", OracleType.VarChar, 100);
addressParameter.Value = address;
OracleParameter systemnameParameter = new OracleParameter("systemname", OracleType.VarChar, 100);
systemnameParameter.Value = systemname;
OracleParameter[] parameter = { qqParameter, ipParameter, addressParameter, systemnameParameter };
if (flag)
{
result = SDProvider.RunProcedure("startqqprocedure", parameter);
}
else
{
result = SDProvider.RunProcedure("closeqqprocedure", parameter);
}
return result > 0 ? true : false;
}
}
}
这样就完成了服务端的程序。webservice、存储过程实现了融合使用。在这部分有几点注意的地方。
(1)、写存储过程中,定义的参数是不需要定义长度的。在存储过程中声明的变量不用加in|out。
(2)、DAO层中GetConnectionString()获取数据库的连接,这个连接字符串可以通过加密存放在Web.config中,更安全一些。
2、客户端调用
这样客户端的调用就很简单,首先要添加刚才写好的webservice,webservice写好发布之后有一个地址,假设是:http://testQQ.WebService/QQWebService.asmx,在本地添加引用。
声明并实例化,
TestQQ.QQWebService service = new TestQQ.QQWebService ();
就可以调用service.看到FirstStart和endstop方法。不过在客户端还有一个任务,就是获取客户端的ip,省市地址和所使用的系统。获取ip参见http://blog.csdn.net/yysyangyangyangshan/article/details/6803787,获取省市参见http://blog.csdn.net/yysyangyangyangshan/article/details/6773815,获取使用的系统是32还是64位如下:
public class OSDetectQuery
{
public static string QueryOSVersion()
{
try
{
ConnectionOptions oConn = new ConnectionOptions();
System.Management.ManagementScope oMs = new System.Management.ManagementScope("\\\\localhost", oConn);
System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select AddressWidth from Win32_Processor");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = null;
try
{
oReturnCollection = oSearcher.Get();
}
catch (System.Exception ex)
{
}
string addressWidth = null;
foreach (ManagementObject oReturn in oReturnCollection)
{
addressWidth = oReturn["AddressWidth"].ToString();
}
return addressWidth;
}
catch (System.Exception ex)
{
return "32";
}
}
public static string OsVersion()
{
string queryos = QueryOSVersion();
string osVersion = Environment.OSVersion.ToString();
if (osVersion.Equals("Microsoft Windows NT 6.1.7600.0"))
{
if (queryos.Equals("64"))
{
return "Win7-64";
}
if (queryos .Equals("32"))
{
return "Win7-32";
}
return "Win7";
}
else if (osVersion.Equals("Microsoft Windows NT 6.1.7600.0"))
{
if (queryos.Equals("64"))
{
return "Win7-64";
}
if (queryos.Equals("32"))
{
return "Win7-32";
}
return "Win7";
}
return "WinXP";
}
}
那么需要上传的客户端信息以获取完毕,直接调用service方法上传就可以了。