现在做一个remoting的小东西,我的项目构成是这样的,如图:
在AttendanceManage类库中Attendance.cs是数据访问类。Base类库主要是写一个抽象父类,并由Attendance继承。AttendanceInfo.cs是实体类。下面的控制台应用程序相当于服务器端。
首先,在AttendanceInfo.cs代码如下:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Base
{
[Serializable]
public class AttendanceInfo : MarshalByRefObject
{
/// <summary>
/// 出勤编号
/// </summary>
public int AttendanceID
{
set;
get;
}
/// <summary>
/// 学生学号
/// </summary>
public string StudentCode
{
set;
get;
}
/// <summary>
/// 班级编号
/// </summary>
public int ClassID
{
set;
get;
}
/// <summary>
/// 所属学期
/// </summary>
public int SemesterID
{
set;
get;
}
/// <summary>
/// 所学课程
/// </summary>
public int CourseID
{
set;
get;
}
/// <summary>
/// 出勤情况
/// </summary>
public string Situation
{
set;
get;
}
/// <summary>
/// 出勤日期
/// </summary>
public DateTime AttendanceTime
{
set;
get;
}
/// <summary>
/// 出勤备注
/// </summary>
public string AttendanceDesc
{
set;
get;
}
}
}
AttendanceBase.cs代码如下:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Base
{
[Serializable]
public abstract class AttendanceBase : MarshalByRefObject
{
public abstract int InsertAttendance(AttendanceInfo model);
public abstract string get(int i);
}
}
Attendance.cs代码如下:(类库已经正确引用)
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using Base;
namespace AttendanceManage
{
[Serializable]
public class Attendance : AttendanceBase
{
SqlHelper helper;
public Attendance()
{
helper = new SqlHelper();
}
/// <summary>
///
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public override int InsertAttendance(AttendanceInfo model)
{
SqlParameter[] parm = new SqlParameter[]
{
new SqlParameter("@situation",SqlDbType.VarChar),
new SqlParameter("@attendancetime",SqlDbType.DateTime),
new SqlParameter("@attendanceDesc",SqlDbType.VarChar)
};
parm[0].Value = model.Situation;
parm[1].Value = model.AttendanceTime;
parm[2].Value = model.AttendanceDesc;
parm[0].Direction = ParameterDirection.Input;
parm[1].Direction = ParameterDirection.Input;
parm[2].Direction = ParameterDirection.Input;
Console.WriteLine("服务器端运行");
return helper.ExecuteNonQuery("pro_AllAttendance", parm, CommandType.StoredProcedure);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string get(int i)
{
Console.WriteLine(i);
return "aaaaaabbbbbbbbbbbbbbbbb";
}
}
}
SqlHelper.cs就不用看了。
控制台应用程序中,App.config
Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="ConnectionString" connectionString="server=192.168.8.105;uid=sa;pwd=000000;database=AttendanceManage" />
</connectionStrings>
<system.runtime.remoting>
<application>
<service>
<wellknown type="AttendanceManage.Attendance,AttendanceManage" objectUri="Attendance" mode="SingleCall"/>
</service>
<channels>
<channel ref="http" port="1234">
<serverProviders>
<provider ref="soap" typeFilterLevel="Full"/>
</serverProviders>
<!--<clientProviders>
<provider ref="binary"/>
</clientProviders>-->
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Program.cs代码如下:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
RemotingConfiguration.Configure("ConsoleApplication1.exe.config", false);
Console.WriteLine("aaaaaaaaaa");
Console.ReadLine();
}
}
}
在这里,服务器端代码完毕。
来看客户端:
客户端很简单,使用控制台应用程序,先将Base.dll添加应用,配置下APP.CONFIG:
Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown type="AttendanceManage.AttendanceBase,AttendanceManage" url="http://localhost:1234/AttendanceBase" />
</client>
<channels>
<channel ref="soap">
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
在客户端的program.cs中代码如下:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Serialization;
using System.IO;
namespace timeclient
{
class Program
{
static void Main(string[] args)
{
Base.AttendanceBase ba = (Base.AttendanceBase)Activator.GetObject(typeof(Base.AttendanceBase), "http://localhost:1234/Attendance");
Base.AttendanceInfo bai = new Base.AttendanceInfo();
bai.Situation = "出勤";
bai.AttendanceTime = DateTime.Now;
bai.AttendanceDesc = "客户端测试";
FileStream fs = new FileStream("model.xml", FileMode.Create);
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(Base.AttendanceInfo));
x.Serialize(fs, bai);
Console.WriteLine(ba.InsertAttendance(bai));
Console.WriteLine(ba.get(11));//这句可以正常执行
Console.ReadLine();
}
}
}
此时,问题来了,最先,我在服务器端的App.config文件中并没有写:
Code
<serverProviders>
<provider ref="soap" typeFilterLevel="Full"/>
</serverProviders>
这时,报出一个安全什么的错误来,我查了查,貌似是需要填写上面的代码即可,我也写了,也过去了。
————————————————分割线,总结——————————————
我这样写的目的是,分离出一个抽象类来,使用实体类继承抽象类,当客户端引用DLL时,只是引用抽象类的DLL,根本拿不到实体类的DLL。其实重要的的配置文件的配置和这句:
Code
Base.AttendanceBase ba = (Base.AttendanceBase)Activator.GetObject(typeof(Base.AttendanceBase), "http://localhost:1234/Attendance");
要确定确实调用的是服务器端方法。而不是本地引用的DLL中的方法。
————————分割线,提出问题————————
结果一运行又出了一个问题,那就是当我实例化实体类对象,并往对象属性中传值后,希望将此对象传递给服务器端方法进行相应操作,结果出了这样的错误:
说实话,我很惊讶,并不太清楚怎么回事。个人感觉,可能是由于在客户端传递对象时,没有进行序列化的原因。
既然需要进行序列化,那么就序列化吧,打算将对象序列化成XML格式,就是这几句
Code
FileStream fs = new FileStream("model.xml", FileMode.Create);
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(Base.AttendanceInfo));
x.Serialize(fs, bai);
成是成了,但是我如何传递呢?那不是需要将XML格式的字符串传递到服务器端,在进行反序列化么?
但是这样就失去了我的本意了啊,我是打算将实体类型的对象传递过去到服务器端的,按照较为传统的方式传递。
就是InsertAttendance()方法中的参数还是AttendanceInfo格式的。
————————解决方案——————————
回复中5楼朋友【@yuguohua】 解答:
因為 AttendanceInfo : MarshalByRefObject
所以bai 的instance是建立在client, 當bai傳至server時, 只有reference.要讀取bai的內容時, 須再與client溝通, 而client並沒有提供溝通channel.
請把 MarshalByRefObject 拿掉就可以解決問題.
————————————————
6楼朋友【@一叶一菩提,一花一世界 】解答:
很简单,我跟你调了一下结果把一个dll的名字写错了,搞了我个把小时,nnd,真郁闷啊,看来晚上不适宜看程序。
你所有的server端的类都即继承自MarshalByRefObject 又把它mark成Serializable,这里太乱,你说它在rpc的时候到底是By Value还是By Reference呢? 这个就不得而知了,我也不知道,如果这样的romting的框架会怎么样去选择,如果它也不知道哦怎么选择,那它不就要出错了么?哈哈
你可以把AttendanceInfo这个类就Mark成Serializable,然后把AttendanceBase继承与自MarshalByRefObject,Attendance继承自AttendanceBase(去掉Serializable)就好了。
哈哈,你要用remoting,就要明明白白的告诉Remoting Framework你到底想干什么?By Value or By
————————————————————
上面两位的解决方案是正确的,在此感谢2位。