Click here to Skip to main content
15,885,365 members
Articles / Programming Languages / C# 4.0

RPC Plumming (Common tools and utilities)

Rate me:
Please Sign up or sign in to vote.
4.25/5 (4 votes)
12 Aug 2009CPOL5 min read 26.5K   262   8   4
Plumming, tools, utilities.

Introduction

When writing applications, you always find yourself doing the same mundane tasks, like converting between data types or accessing data in a database, just to mention a few things.

I have decided to prep some of my utility classes with comments and publish them here at The Code Project. Most isn't rocket science and nothing you can't do with a few lines of code when needed - but when you write the same few lines of code several times, it starts to become annoying, and the utility assembly emerges.

NameFunction
RPC ConvertAlternative to the Convert class provided by Microsoft, but with additional tool methods.
RPC DatabaseClasses that can be used to manage databases, tables, and records.
RPC DirectoryClasses that make it easy to work with containers, groups, users, and computers in Active Directory.
RPC SCAClass to handle keys and encrypt/decrypt text using a Symmetric Cryptographic Algorithm.
RPC TripleThis can best be described as a tree state boolean.

You can find the release history at the bottom of this article.

RPC Convert

I like the Microsoft Convert class, but I find it inadequate and would have liked to create my own RpcConvert inheriting from Convert, but Microsoft sealed their Convert class. Instead, I decided to rewrite my RpcConvert class with Extension Methods, which is available with version 3.5 of the .NET framework.

I wanted one class for my convert-methods, so I decided to include most of the conversions you'll find in Microsoft's Convert class; some might call me crazy - but that's how I want it. Over time, my RpcConvert class has evolved to a Swiss army knife, with non-converting methods.

Often when I convert from one data type to another, I don't want an exception to be thrown if the conversion fails; instead, I want a default value returned on exceptions.

Here is the example code converting from String to Int32, both as a normal call or as an Extension Method:

C#
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);

These are some of the conversions my RpcConvert class performs:

  • Convert between basic types (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, UInt64, Single, Double, Decimal)
  • Convert between most basic types and DateTime, Guid, String, and Object
  • Split and merge conversions on String lists (List<String>, String[], StringCollection)
  • String to and from file
  • String from URL (download)
  • String to and from Base64
  • String to and from Rijndael
  • String to and from RTF
  • Generate random 'password' string
  • IsMatch testing for wildcard (* and ?) match in a list of strings

RPC Database

This is some of my old code that goes back to the days of Borland Delphi. I started programming in C# right from the beginning, and one of my first projects was to port my Pascal database code to C#. A lot has happened since, because C# and .NET offer some nice features not available in Pascal at the time - one of these things is generics.

I haven't studied the new LINQ functionality that comes with .NET 3.5, so perhaps Microsoft has made my code redundant.

How it Works

The basic idea is that I want each record I work with in an object, and I want to be able to change the object properties and commit the change to the database by invoking a Save method. The same way, I want to be able to delete the record by invoking a Delete method.

And so, to use an RPC database, you write a class for each table you want to work with. This class is decorated with an attribute. Each field in the table is created as a property in the class, and each property that maps to a field in the database is decorated with an attribute.

Consider that I want to work with a table named "CountryInfo" with an auto-numeric key "Code", a "CountryName", and finally a "Population" field. The class will look something like this:

C#
// 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

Often, I override the "BeforeSave" method, and validate the data here. If the data isn't valid, the method can return false or throw an exception. There are many similar methods to override.

Here is some example code that shows how to use the RpcDatabase class and the Countries class shown above.

C#
// 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();

I am still using the code, and sometimes implement new features. At this time, I still need to support threading, transactions, and indexes.

RPC Directory

RPC Directory consists of a main class and a few classes encapsulating some of the basic data classes in the Active Directory:

  • RpcDirectoryContainer
  • RpcDirectoryGroup
  • RpcDirectoryUser
  • RpcDirectoryComputer

The 'DomainDNS', 'Container', and 'OrganizationalUnit' are all treated as a container in my code. When a new container is created, it is created as an OU.

C#
// 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"});

There is one thing I still need to implement in RPC Directory:

  • Encode special characters in the ID with backslash, when creating a new container, group, and user.

RPC SCA (Symmetric Cryptographic Algorithm)

Once again, Microsoft .NET makes it easy to use Cryptography, but again, I like to have that one class offering the basics.

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

RPC Triple

Ever wanted a tree state boolean? Well, the Triple class is just that. I considered using the CheckState class, but ended up writing my own class with a lot of operator methods.

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

Believe it or not, it is actually useful sometimes.

History

DateComments
11-08-2009RPC Database: code added to the article. RPC Directory: Added code that finds the mailbox store, when mailbox enabling users or groups. This article by LiQuick showed me how to implement it.
09-08-2009First public release of some of my RPC Plumming code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Systems / Hardware Administrator
Denmark Denmark
See https://rpc-scandinavia.dk/

Comments and Discussions

 
GeneralRPC Triple Pin
DaveHoganIW10-Aug-09 22:25
DaveHoganIW10-Aug-09 22:25 
GeneralRe: RPC Triple Pin
René Paw Christensen11-Aug-09 6:52
René Paw Christensen11-Aug-09 6:52 
GeneralRe: RPC Triple Pin
DaveHoganIW11-Aug-09 22:25
DaveHoganIW11-Aug-09 22:25 
GeneralRe: RPC Triple Pin
René Paw Christensen12-Aug-09 4:36
René Paw Christensen12-Aug-09 4:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.