.NET Remoting 基础结构需要正确的元数据,以便将一个应用程序域中的对象连接到另一个域中的对象。通常我们将包含远程类型的程序集同时发布到服务器和客户端,但这并不是一个好主意。有太多的原因阻止我们这么做:
1. 我们并不想客户端开发人员知道远程对象的内部细节,诸如私有成员内容等。
2. 我们不希望每次升级都更新客户端文件。
.NET Remoting 基础结构需要正确的元数据,以便将一个应用程序域中的对象连接到另一个域中的对象。通常我们将包含远程类型的程序集同时发布到服务器和客户端,但这并不是一个好主意。有太多的原因阻止我们这么做:
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
1. 我们并不想客户端开发人员知道远程对象的内部细节,诸如私有成员内容等。
2. 我们不希望每次升级都更新客户端文件。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
Soapsuds
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
Remoting 为我们提供了一个工具 "Soapsuds"。不要被它的名字所迷惑,它同样适用于二进制序列化的远程对象,因为我们只是用它来创建一个远程代理而已。假设远程类型存放在 RemoteLibrary 中,类型全名是 RemoteLibrary.Data。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
RemoteLibrary.csproj
namespace RemoteLibrary
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public class Data : MarshalByRefObject
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public void Test()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Console.WriteLine("Test AppDomain:{0}", AppDomain.CurrentDomain.FriendlyName);
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
在 "Visual Studio 2005 命令提示" 窗口中用 Soapsuds 创建客户端代理类型。
c:\> Soapsuds -types:RemoteLibrary.Data,RemoteLibrary -gc
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
你会看到生成的 RemoteLibrary.cs 文件,打开看一下。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Metadata;
using System.Runtime.Remoting.Metadata.W3cXsd2001;
using System.Runtime.InteropServices;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace RemoteLibrary
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
[SoapType(
)]
[ComVisible(true)]
public class Data : System.Runtime.Remoting.Services.RemotingClientProxy
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// Constructor
public Data()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public Object RemotingReference
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{return(_tp);}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
[SoapMethod(
)]
public void Test()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
((Data) _tp).Test();
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
将这个文件拷贝到客户端目录,并添加到项目中。开始编写客户端代码。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterActivatedClientType(typeof(RemoteLibrary.Data), "tcp://localhost:801/test");
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
RemoteLibrary.Data data = new RemoteLibrary.Data();
data.Test();
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
其实还可以使用 System.Runtime.Remoting.MetadataServices 名字空间中的相关类用代码来创建这些代理源码或者程序集。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.IO;
using System.Runtime.Remoting.MetadataServices;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
ArrayList list = new ArrayList();
FileStream fs = new FileStream("test.xml", FileMode.OpenOrCreate);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
// 将远程类型转换为 XML 架构。
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
MetaData.ConvertTypesToSchemaToStream(new Type[]
{ typeof(Data) }, SdlType.Wsdl, fs);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
// 将 XML 架构流中转换为代理源码文件。
fs.Seek(0, SeekOrigin.Begin);
MetaData.ConvertSchemaStreamToCodeSourceStream(true, AppDomain.CurrentDomain.BaseDirectory, fs, list);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
// 显示所生成的代理源码文件名。
foreach (string s in list) Console.WriteLine(s);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
// 将生成的源码转换为程序集文件。
MetaData.ConvertCodeSourceStreamToAssemblyFile(list, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.dll"), null);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
接口隔离
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
还有一个方式就是使用接口进行隔离,这样客户端就不会看到远程类型。我们继续用上面的例子做演示。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
1. 创建接口类型项目。项目中包含接口和一个工厂类型,由工厂类型负责创建目标类型。(注意为避免循环引用,我们通过配置文件读取目标类型信息。)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
RemoteInterface.projc
namespace RemoteInterface
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public interface IData
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
void Test();
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public class Factory : MarshalByRefObject
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public IData NewData()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return (IData)Activator.CreateInstance(Type.GetType(ConfigurationManager.AppSettings["data"]));
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="data" value="RemoteLibrary.Data,RemoteLibrary"/>
</appSettings>
</configuration>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2. 修改原类型使其实现 IData 接口,注意添加 RemoteInterface.dll 引用。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
RemoteLibrary.csproj
namespace RemoteLibrary
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public class Data : MarshalByRefObject, RemoteInterface.IData
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public void Test()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Console.WriteLine("Test AppDomain:{0}", AppDomain.CurrentDomain.FriendlyName);
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
3. 在服务器端,除了 RemoteLibrary.Data,还要注册 RemoteInterace.Factory。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
Server.csproj
TcpServerChannel channel = new TcpServerChannel(801);
ChannelServices.RegisterChannel(channel, false);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
RemotingConfiguration.ApplicationName = "test";
RemotingConfiguration.RegisterActivatedServiceType(typeof(Factory));
RemotingConfiguration.RegisterActivatedServiceType(typeof(Data));
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4. 将 RemoteInterface.dll 提供给客户端,添加引用后开始编码。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
Client.csproj
RemotingConfiguration.RegisterActivatedClientType(typeof(Facade), "tcp://localhost:801/test");
RemotingConfiguration.RegisterActivatedClientType(typeof(IData), "tcp://localhost:801/test");
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
Factory factory = (Facade)Activator.CreateInstance(typeof(Factory), null);
IData data = factory.NewData();
data.Test();
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这种方式稍显复杂,但从架构模式上来说要更好一些。它隔绝了目标类型和客户端的联系,服务器可以更灵活地变化和升级。
下载示例源代码
posted on
2007-06-05 09:16
编程山人
阅读(
352)
评论()
编辑
收藏
举报