65.9K
CodeProject 正在变化。 阅读更多。
Home

RPC 管道(通用工具和实用程序)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.25/5 (4投票s)

2009年8月9日

CPOL

5分钟阅读

viewsIcon

27040

downloadIcon

279

管道,工具,实用程序。

引言

在编写应用程序时,您会发现自己总是要执行一些相同的单调任务,例如数据类型之间的转换或访问数据库中的数据,仅举几例。

我决定在一些实用程序类中添加注释,并将它们发布到 The Code Project。其中大多数并非高深莫测,也不是您在需要时无法用几行代码完成的事情 - 但当您编写相同的几行代码数次时,它就会变得令人厌烦,这时实用程序程序集就应运而生了。

名称 函数
RPC 转换 Microsoft 提供的 Convert 类的替代品,但具有额外的工具方法。
RPC 数据库 可用于管理数据库、表和记录的类。
RPC 目录 使处理 Active Directory 中的容器、组、用户和计算机变得容易的类。
RPC SCA 用于处理密钥并使用对称加密算法加密/解密文本的类。
RPC Triple 这最好描述为布尔的三种状态。

您可以在本文底部找到 发布历史记录

RPC 转换

我喜欢 Microsoft 的 Convert 类,但我发现它不够用,并且希望创建我自己的 RpcConvert 类继承自 Convert,但 Microsoft 封锁了他们的 Convert 类。因此,我决定使用扩展方法重写我的 RpcConvert 类,这在 .NET Framework 3.5 版本中可用。

我想要一个类来处理我的转换方法,所以我决定包含 Microsoft Convert 类中的大多数转换;有些人可能会认为我疯狂 - 但我就是这样想的。随着时间的推移,我的 RpcConvert 类已经发展成了一个瑞士军刀,并包含非转换方法。

通常,当我从一种数据类型转换为另一种数据类型时,我不希望在转换失败时抛出异常;相反,我希望在异常情况下返回一个默认值。

这是从 String 转换为 Int32 的示例代码,包括正常调用和扩展方法调用。

String str = "1717";
Int32 i = 0;
// Convert the String to Int32, returns 0 on exceptions.
i = RpcConvert.ToInt32(str);

// Convert the String to Int32, returns -1 on exceptions.
i = RpcConvert.ToInt32(str, -1);

// Convert the String to Int32 (Extension method), returns 0 on exceptions.
i = str.ToInt32();

// Convert the String to Int32 (Extension method), returns 0 on exceptions.
i = str.ToInt32(0);

以下是我的 RpcConvert 类执行的一些转换:

  • 基本类型之间的转换(SByteInt16Int32Int64ByteUInt16UInt32UInt64SingleDoubleDecimal
  • 大多数基本类型与 DateTimeGuidStringObject 之间的转换
  • String 列表(List<String>String[]StringCollection)的字符串拆分和合并转换
  • 字符串与文件之间的转换
  • 从 URL 获取字符串(下载)
  • 字符串与 Base64 之间的转换
  • 字符串与 Rijndael 之间的转换
  • 字符串与 RTF 之间的转换
  • 生成随机“密码”字符串
  • IsMatch 测试通配符(* 和 ?)在字符串列表中的匹配

RPC 数据库

这是我的一些旧代码,可以追溯到 Borland Delphi 时代。我从一开始就学习 C#,我的第一个项目之一就是将我的 Pascal 数据库代码移植到 C#。此后发生了许多变化,因为 C# 和 .NET 提供了一些 Pascal 当时没有的优秀功能 - 其中之一就是泛型。

我没有研究 .NET 3.5 随附的新 LINQ 功能,所以也许 Microsoft 已经使我的代码变得多余。

工作原理

基本思想是,我希望我处理的每个记录都在一个对象中,并且我希望能够更改对象的属性并通过调用 Save 方法将更改提交到数据库。同样,我希望通过调用 Delete 方法来删除记录。

因此,要使用 RPC 数据库,您需要为要处理的每个表编写一个类。该类带有属性。表中的每个字段都作为类中的属性创建,并且每个映射到数据库字段的属性都带有属性。

考虑我想要处理一个名为“CountryInfo”的表,该表有一个自动编号键“Code”,一个“CountryName”,最后还有一个“Population”字段。该类看起来大致如下。

// The class is decorated with a RpcDataTable.
// It represents a table named 'CountryInfo' in the database.
// If the table name is omitted, the class name is used as the table name.
[RpcDataTable("CountryInfo")]
public class Countries : RpcDataRecord {
 // The property is decorated with a RpcDataColumn.
 // It represents a field named 'Code' in the table.
 // This field is a INTEGER/SERIAL and a unique required key.
 [RpcDataColumn("Code", RpcDataField.Serial, RpcDataAttribute.Id | 
                RpcDataAttribute.Required | RpcDataAttribute.Unique)]
 public Int32 CountryCode
 {
     get
     {
         return RpcConvert.ToInt32(this["Code"]);
     }
     set
     {
         this["Code"] = value;
     }
 } // CountryCode
 [RpcDataColumn(RpcDataField.String, RpcDataAttribute.Required)]
 public String CountryName
 {
     get
     {
         return RpcConvert.ToString(this["CountryName"]);
     }
     set
     {
        this["CountryName"] = value;
     }
 } // CountryName
 [RpcDataColumn("Population", RpcDataField.Int32, RpcDataAttribute.Required)]
 public Int32 CountryPopulation
 {
     get
     {
         return RpcConvert.ToInt32(this["Population"]);
     }
     set
     {
         this["Population"] = value;
     }
 } // CountryPopulation
 // Static factory method to create a new object.
 public static Countries New(RpcDatabase db, String name, 
               Int32 population, Boolean saveTheRecord)
 {
     Countries record = new Countries();
     record.Database = db;
     record.CountryName = name;
     record.CountryPopulation = population;
     if (saveTheRecord == true)
     {
        record.Save();
     }
     return record; } // New
}// Countries

通常,我会在“BeforeSave”方法中覆盖,并在那里验证数据。如果数据无效,该方法可以返回 false 或抛出异常。有许多类似的方法可以覆盖。

以下是一些示例代码,展示了如何使用上面所示的 RpcDatabase 类和 Countries 类。

// Create a new Access database on the desktop, or open a existing one.
// Notice when working with Access databases, the host is actually the
// directory where the database file is, and the database is the name
// of the database file without the extension.
RpcDatabase db = new RpcDatabase( RpcDataEngine.MicrosoftAccessFile, 
                 Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), 
                 "Country Database", null, null, 15);
db.DatabaseCreate();
db.Connect();

// Create the table.
db.TableCreate(typeof(Countries));
Console.WriteLine(String.Format("{0} tables in the database", db.TableCount()));

// Create a few records in the database.
Countries.New(db, "Norge", 4525116, true);
Countries.New(db, "Denmark", 5368854, true);
Countries.New(db, "Faroe Islands", 46011, true);
Countries.New(db, "Greenland", 56376, true);
Countries.New(db, "Sweden", 8876744, true);
Countries.New(db, "Iceland", 279384, true);
Countries.New(db, "Finland", 5183545, true);

// Get and update a record.
// I don't know the CountryCode because it is a serial, so in
// real life another key would have been better.
// The QueryFilter method accepts a filter which is the text you
// put after the WHERE word in a complete SQL query.
RpcDataList<Countries> countries = db.QueryFilter<Countries>("[CountryName] = 'Norge'");
Countries country = countries[0];
country.CountryName = "Norway";
country.Save();

// Get and show all records.
countries = db.QueryAll<Countries>();
countries.ApplySort("CountryPopulation", 
          System.ComponentModel.ListSortDirection.Descending);

foreach (Countries countryRecord in countries)
{
    Console.WriteLine(String.Format("{0} has a population of {1:n0}", 
                      countryRecord.CountryName, countryRecord.CountryPopulation));
}

// Delete the table.
db.TableDelete(typeof(Countries));
Console.WriteLine(String.Format("{0} tables in the database", db.TableCount()));

// Delete the database.
// This disconnects tha database first, but if the application don't
// quit, .NET keeps the MDB file open for a long time
// incase the database is opened again.
// I don't know how to force close the Access database.
db.DatabaseDelete();

我仍在继续使用这些代码,有时会实现新功能。目前,我仍然需要支持线程、事务和索引。

RPC 目录

RPC Directory 由一个主类和几个封装 Active Directory 中一些基本数据类的类组成。

  • RpcDirectoryContainer
  • RpcDirectoryGroup
  • RpcDirectoryUser
  • RpcDirectoryComputer

在我的代码中,“DomainDNS”、“Container”和“OrganizationalUnit”都被视为容器。当创建一个新容器时,它会被创建为一个 OU。

// Create a new user in a new container in the AD.
RpcDirectory ad = new RpcDirectory("admin@domain", "adminPassword");
RpcDirectoryContainer container = ad.CreateContainer("Test OU");
RpcDirectoryUser user = ad.CreateUser("myUserId", container);

// Set some user properties.
user.FirstName = "Håkon";
user.LastName = "Hansen";
user.Description = "New user created by my application.";
user.Phone = "012 3456 7890";
user.Password = "Password1234";
user.PasswordChangeForced = true;  // User must change password at next login.
user.Enabled = true;  // Enable the user account.
user.LogonScript = "sampleUsers.cmd";
user.ProfileDirectory = "\\\\Server\\Profiles\\myUserId";

// Mailbox enable the user account (Microsoft Exchange).
// The mailbox is added to the first Store in the first Storage Group
// on the first Exchange server.
user.MailboxEnable(true);

// Create two SMTP e-mail addresses.
// The first e-mail address is the default address.
user.SmtpAddresses = RpcConvert.ToStringList(new String[] {"myUserId@domain", 
                                "Haakon-Hansen@domain"});

RPC Directory 中还有一项我仍然需要实现的功能:

  • 在创建新容器、组和用户时,使用反斜杠对 ID 中的特殊字符进行编码。

RPC SCA(对称加密算法)

再次,Microsoft .NET 使加密的使用变得容易,但同样,我希望有一个类提供基础功能。

// Encrypt a string.
String str = "My readable text";
RpcSCA sca = RpcSCA.TrippleDES();
sca.PrivateKey = "Password";
str = sca.Encrypt(str);

RPC Triple

曾经想要一个布尔的三种状态?那么,Triple 类就是这样。我考虑过使用 CheckState 类,但最终写了自己的类,并有很多运算符方法。

Triple t1 = true;
Triple t2 = Triple.Unknown;
if (t1 == false) { ... }
if (t2.IsUnknown == true) { ... }

信不信由你,它有时确实很有用。

历史

日期 注释
11-08-2009 RPC 数据库:已向文章添加代码。RPC 目录:添加了查找邮箱存储的代码,用于为用户或组启用邮箱。LiQuick 的这篇 文章 向我展示了如何实现它。
09-08-2009 我的部分 RPC Plumming 代码首次公开发布。
© . All rights reserved.