C#4.0 新玩法

记得在三年前.net3.5库开始公布的时候,其实里面最新增加的最主要的东西是linq,当时看过一from开头的查询表达式以后,就表示出了对Linq的厌恶,其内对xml和ado.net的支持,也还算不错,但是当初有一个跨越oracle和sql server的项目,发现linq for oracleDB支持非常的尴尬,最后直接放弃重写一个,至此对3.5的最新这块内容,就搁置下来了。后来最近有空,开始研究4.0的新库,发现在无论在语言解释器还是在语法特性上,c#都增加不少亮色,于是乎都拿过来自己写了一遍,也算是大致了解了一番,的确增色不少。
1.4.0里面的C#提供了可选的函数变量
记得最早的语言中,对函数的重载都是需要一个一个自己写的,果断来说,就是同名不同参数的函数,要自己写成多个,但是今天的语言里面,你发现你可以在定义传入参数的时候,事先为参数定义好默认的值,这样的话,在某些情况下,你就可以忽略这些参数的值,而达到原来重载的效果了,下面来一段代码,大家可能就比较容易了解了。

public class OptionClasssOld
{
public void Process(string data)
{
Process(data, false);
}

public void Process(string data, bool ignoreWS)
{
Process(data, ignoreWS, null);
}

public void Process(string data, bool ignoreWS, ArrayList moreData)
{
Console.WriteLine("call the function with parameter 1,2,3");
}
}

public class OptionClassNew
{
public void Process(string data, bool ignoreWs = false, ArrayList moreData = null)
{
Console.WriteLine("call the option function with 1={0},2={1},3={2}", data, ignoreWs, moreData);
}
}

调用
OptionClasssOld oldCall = new OptionClasssOld();
oldCall.Process("call 1 parameter");
oldCall.Process("call 2 parameter", true);
oldCall.Process("call 3 parameter", true, new System.Collections.ArrayList());

OptionClassNew newCall = new OptionClassNew();
newCall.Process("call 1 parameter");
newCall.Process("call 2 parameter", true);
newCall.Process("call 3 parameter", true, new System.Collections.ArrayList());
newCall.Process("call last 2parameter", moreData: new System.Collections.ArrayList());

2,提供了动态对象
很多时候我们需要在对应的时候知道对象的类型,比如我们在用反射调用invoke方法的时候,或者是我们预先定义好了object,之后在依据需要as 成我们想要的对象的类型,再进行调用,这些都是在编译的时候完成的,而dynamic提供了一种在运行时动态匹配变换的功能,能够让我们的对象在运行时里动态的改变,而不需要我们再去关注他到底是什么类型,其实按照我个人的理解来看,就是var的延伸,只不过一个是在编译时,一个是在运行时,给一段代码可能大家就比较容易理解了。

Type myType = typeof(Customer);
ConstructorInfo consInfo = myType.GetConstructor(new Type[] { });
object cust = consInfo.Invoke(new object[] { });
((Customer)cust).FirstName = "foo";
((Customer)cust).Process();

dynamic dcust = GetCustomer();
dcust.FirstName = "foo"; // works as expected
dcust.Process(); // works as expected

对比以上两种方式,大家就可能了解到动态运行时的力量之所在了。
337595910445090
这个就是支持上面的动态效果的运行时的框架,这个将会为以后的晚绑定的动态语言来服务。

3,并行的PLinq处理。
现在大家都提出并行计算,在多核的机器里面,很多时候是由操作系统来控制CPU的资源调度和使用的,但是我们会发现很多时候程序在运行的时候并没有对CPU进行充分的利用,其间原来看过intel的cpu级别的针对C++的开发包,对细节处理的控制可以充分利用多核的威力,但是在.net框架下跑的东西,所有的内容就只能依赖于运行时的执行。庆幸.net framework里面现在添加了并行处理的il编译,这样的话就可以支持不同的语义器了。
91661210281795
对于并行的执行,最方便于程序员使用,和在代码当中最占资源的地方应该就是for语句里面了,所以在目前标准的并行库的使用来说的话,大家主要可以专注在for这个语句上,实际这里用了并行,就是用了重写的for,foreach,由于for就是一个枚举器的遍历,所以在这里提升的效率还是只是简单的枚举的效率,对于计算和多任务数据的处理的话,并没有做到真正的指令级别和内核级别的优化。具体内部的实现也没有看到,所以在这里也只是妄加臆断而已,请勿见笑。
用起来也很简单 就是原来传统的语句前面加了个Parallel,简单易容,还是上一段代码吧

public static void MultiplyMatricesSequential(double[,] matA, double[,] matB,
double[,] result)
{
int matACols = matA.GetLength(1);
int matBCols = matB.GetLength(1);
int matARows = matA.GetLength(0);

for (int i = 0; i < matarows;="">
{
for (int j = 0; j < matbcols;="">
{
double temp = 0;
for (int k = 0; k < matacols;="">
{
temp += matA[i, k] * matB[k, j];
}
result[i, j] = temp;
}
}
}
#endregion

public static void MultiplyMatricesParallel(double[,] matA, double[,] matB, double[,] result)
{
int matACols = matA.GetLength(1);
int matBCols = matB.GetLength(1);
int matARows = matA.GetLength(0);

// A basic matrix multiplication.
// Parallelize the outer loop to partition the source array by rows.
Parallel.For(0, matARows, i =>
{
for (int j = 0; j < matbcols;="">
{
// Use a temporary to improve parallel performance.
double temp = 0;
for (int k = 0; k < matacols;="">
{
temp += matA[i, k] * matB[k, j];
}
result[i, j] = temp;
}
}); // Parallel.For
}

可以看第二段代码的效率提升了一倍,
Task Parallel Library (TPL)和PLinq用到的最多的就是lambda表达式,大家可以的看到里面=>等符号,就代表的是这种表达式开始发威了,具体的拉姆达表达式可以写成一个单独的文章,在这里就不具体的在说了。
9537498486032
对于小量数据的使用基本上是没有什么反应的,但是对于大量数据的使用来说,就有不小的增长,自己曾用官方的demo跑了一下,的确是快了很多。

4. 泛型定义里面的out 和in关键字(我日covariance contravariance 反变性-这什么翻译)
这里在重写的.net4.0的里面,枚举器和比较器的泛型定义被改为了如下的方式,
public interface IEnumerable
public interface IComparable
顾名思义,在使用out 关键字的时候,表示这个集合的T只能是输出而不能是输入
而使用Out关键字的时候,表示这个集合的T可以向上转型。我们来看下面这一段代码,原来这样实现可是想都不敢想的。

public void Test1()
{
IEnumerable custEnum = getCustomer();
IEnumerable perEnum = custEnum;
}

public List getCustomer()
{
List custCol = new List();
custCol.Add(new Customer("1", "a"));
custCol.Add(new Customer("2", "b"));
custCol.Add(new Customer("3", "c"));
return custCol;
}

反之如果使用的是in关键字,表示这个集合的T可以向下转型。把上面的代码加以改变就可。

public void Test2()
{
IComparable perEnum = getPerson();
IComparable custEnum = perEnum;
}

写了那么多粗浅的文章,对新特性也只是一个皮毛的介绍而已,后面深入的理解和使用,还在于平时的应用和项目。还是那句话,技术无关好坏,能满足需求,就是可用。

BOE里面的用户控制

在新版本的BOE所提供的12版本的SDK中,控制用户,组和报表的内容,主要还是依赖于InfoObject的对象来进行处理。也可以说,在BOE的托管代码中,InfoObject类,就是一切对象的根源,上次有说过那个很长的类图,要做开发的一定得仔细研究。
   在原来我们看到的类图当中,可以看到,对于User和Group,在BOE当中同样也是借助于query查询出来的,本质同样还是存储于数据库当中的object, 对于所有在BOE当中的对象,都是InfoObject, 为了便于操作,系统提供了一堆相对应的对象模型。使用的时候可以强制类型转化过去。
   在BOE里面的用户和组的信息,提供两种方式的存储,对于普通的访问账户来说,在BO里面存储的就是一个简单的帐户名对象,而对于域内的账户来说,在BO里面还需要存储相关的映射信息,
  在创建用户对象的过程当中,必须借助于PluginInfo 来创建对应的CrystalEnterprise.User 型的InfoObject具体的就看代码吧,没有什么神奇的地方。
   这个图是BO的SDK的开发框架,可以看出整个BOE的框架主要由三大部分组成,第一块为BOE,第二块为RAS,第三块为Webi,其间为了扩展开发,扩展了cr的control以及.net的一些bo对应的控件。其实本身这个框架也就是BOE产品展现所依赖的类库。
15688479657275
  如果转化为继承的层次结构的化,显示的就是下面的类型图。
42125595424188  
  这段代码是从SDK里面搜刮出来的,稍微的做了一些个人的小的改动,为了避免别人说抄袭的情况,在这里还是说明一下。不过命名空间肯定是我的 ChinaSoft.Donegal哈哈。
  
 

 using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Web;
  using CrystalDecisions.Enterprise;
  using System.Collections;
  
  namespace ChinaSoft.Donegal.ReportView
  {
   public class Utitily
   {
  
  
   ///
   /// In this method, a SortedList upcast to IDictionary is declared
   /// and instantiated. A query is run that retrieves a list of all
   /// ServerGroups in the CMS. The query is retrieved into an InfoObjects
   /// indexed class, and a foreach loop goes through the class and
   /// puts each ID property and Title property of the InfoObject
   /// instance into the SortedList key/value pair. The SortedList
   /// instance is then returned from the method.
   ///
   /// Manages access to the CMS repository.
   /// The IDictionary interface, a generic key/value collection.
   public static IDictionary GetServerGroups(InfoStore infoStore)
   {
   IDictionary sortedList = new SortedList();
  
   string query = "Select SI_SERVER_GROUP, SI_ID, SI_NAME "
   + "From CI_SYSTEMOBJECTS "
   + "Where SI_KIND='ServerGroup'";
  
   InfoObjects infoObjects = infoStore.Query(query);
  
   if (infoObjects.Count > 0)
   {
   foreach (InfoObject infoObject in infoObjects)
   {
   sortedList.Add(infoObject.ID, infoObject.Title);
   }
   }
   else
   {
   sortedList.Add("0", SampleCodeMessages.NO_MATCHES_FOUND);
   }
  
   return sortedList;
   }
  
  
   ///
   /// In this method, the ServerGroup instance is retrieved from the
   /// CMS server based on the serverGroupID. The retrieved ServerGroup
   /// instance is deleted and the enclosing InfoObjects indexed class
   /// is committed back to the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the currently selected server group.
   public static void DeleteServerGroup(InfoStore infoStore, string serverGroupID)
   {
   string query = "Select SI_SERVER_GROUP From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + serverGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   infoObjects.Delete("#" + serverGroupID);
  
   infoStore.Commit(infoObjects);
   }
  
  
  
   ///
   /// In this method, the current ServerGroup instance is retrieved
   /// from the CMS server based on the serverGroupID and placed in
   /// an InfoObjects indexed class instance. The InfoObject is
   /// retrieved from the first index of InfoObjects and downcast to
   /// ServerGroup. The GroupServers instance is retrieved from the
   /// Servers property of ServerGroup. The Delete method of GroupServers
   /// is called, passing in the serverName string parameter. Finally,
   /// the enclosing InfoObjects indexed class is committed back to
   /// the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The name (Title property of InfoObject) of the server to be added.
   /// The ID of the currently selected server group.
   public static void RemoveServerFromServerGroup(InfoStore infoStore, string serverName, string serverGroupID)
   {
   string query = "Select SI_GROUP_MEMBERS From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + serverGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
  
   ServerGroup serverGroup = (ServerGroup)infoObject;
   GroupServers groupServers = serverGroup.Servers;
   groupServers.Delete(serverName);
  
   infoStore.Commit(infoObjects);
   }
  
  
   ///
   /// In this method, the current ServerGroup instance is retrieved
   /// from the CMS server based on the serverGroupID and placed in
   /// an InfoObjects indexed class instance. The InfoObject is
   /// retrieved from the first index of InfoObjects and downcast to
   /// ServerGroup. The ServerGroupAssociates instance is retrieved
   /// from the SubGroups property of ServerGroup. The Add method of
   /// ServerGroupAssociates is called. Finally, the enclosing
   /// InfoObjects indexed class is committed back to the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the server group to be nested in the curent server group.
   /// The ID of the currently selected server group.
   public static void AddNestedServerGroupToServerGroup(InfoStore infoStore, string nestedServerGroupID, string serverGroupID)
   {
   string query = "Select SI_SUBGROUPS From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + serverGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
  
   ServerGroup serverGroup = (ServerGroup)infoObject;
   ServerGroupAssociates serverGroupAssociates = serverGroup.SubGroups;
   serverGroupAssociates.Add(Convert.ToInt32(nestedServerGroupID));
  
   infoStore.Commit(infoObjects);
   }
  
  
  
   ///
   /// In this method, the current ServerGroup instance is retrieved
   /// from the CMS server based on the serverGroupID and placed in
   /// an InfoObjects indexed class instance. The InfoObject is
   /// retrieved from the first index of InfoObjects and downcast to
   /// ServerGroup. The ServerGroupAssociates instance is retrieved
   /// from the SubGroups property of ServerGroup. The Clear method
   /// of ServerGroupAssociates is called. Finally, the enclosing
   /// InfoObjects indexed class is committed back to the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the currently selected server group.
   public static void ClearAllNestedGroupsFromServerGroup(InfoStore infoStore, string serverGroupID)
   {
   string query = "Select SI_SUBGROUPS From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + serverGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
  
   ServerGroup serverGroup = (ServerGroup)infoObject;
   ServerGroupAssociates serverGroupAssociates = serverGroup.SubGroups;
   serverGroupAssociates.Clear();
  
   infoStore.Commit(infoObjects);
   }
  
  
   ///
   /// In this method, a SortedList class upcast to an IDictionary
   /// interface is declared and instantiated. A query is run that
   /// retrieves a list of all Users in the CMS server. The
   /// query is retrieved into an InfoObjects indexed class, and a
   /// foreach loop goes through the class and puts each ID property
   /// and Title property of the InfoObject instance into the SortedList
   /// instance key/value pairs. The SortedList instance is then
   /// returned from the method.
   ///
   /// Manages access to the CMS repository.
   /// The IDictionary interface, a generic key/value collection.
   public static IDictionary GetUsers(InfoStore infoStore)
   {
   IDictionary sortedList = new SortedList();
   string query = "Select SI_ID, SI_NAME From CI_SYSTEMOBJECTS "
   + "Where SI_KIND='User'";
  
   InfoObjects infoObjects = infoStore.Query(query);
  
   foreach (InfoObject infoObject in infoObjects)
   {
   sortedList.Add(infoObject.ID, infoObject.Title);
   }
  
   return sortedList;
   }
  
   ///
   /// In this method, a PluginManager instance is retrieved from the
   /// PluginManager property of InfoStore. The PluginInfo instance is
   /// retrieved by passing the string 慍CrystalEnterprise.User?to the
   /// GetPluginInfo method of the PluginManager instance. A new InfoObjects
   /// indexed class is created by calling the NewInfoObjectsCollection
   /// method of InfoStore and the PluginInfo instance is then added
   /// to the InfoObjects instance. The InfoObject is then retrieved
   /// from the first index of the InfoObjects indexed class. InfoObject
   /// is downcast to User. Several properties of the User instance
   /// are then set. The InfoObjects instance containing the User
   /// instance is then committed back to the CMS server using the
   /// Commit method of InfoStore.
   ///
   /// Manages access to the CMS repository.
   /// The struct for transferring user information.
   public static void AddUser(InfoStore infoStore, UserDataTransfer userDataTransfer)
   {
   PluginManager pluginManager = infoStore.PluginManager;
   PluginInfo pluginInfo = pluginManager.GetPluginInfo("CrystalEnterprise.User");
   InfoObjects infoObjects = infoStore.NewInfoObjectCollection();
   infoObjects.Add(pluginInfo);
  
   InfoObject infoObject = infoObjects[1];
  
   CrystalDecisions.Enterprise.Desktop.User user =
   (CrystalDecisions.Enterprise.Desktop.User)infoObject;
  
   user.Title = userDataTransfer.AccountName;
   user.Description = userDataTransfer.Description;
   user.FullName = userDataTransfer.FullName;
   user.Connection = (CeConnectionType)(userDataTransfer.UserType + 1);
   user.PasswordExpires = userDataTransfer.PasswordExpires;
   user.ChangePasswordAtNextLogon = userDataTransfer.ChangePasswordAtNextLogon;
   user.AllowChangePassword = userDataTransfer.AllowChangePassword;
   user.NewPassword = userDataTransfer.Password;
  
   infoStore.Commit(infoObjects);
   }
  
  
   ///
   /// In this method, a SortedList upcast to IDictionary is declared
   /// and instantiated. A query is run that retrieves a list of all
   /// user groups for a particular user in the CMS. The query is
   /// retrieved into an InfoObjects indexed class, and the individual
   /// InfoObject is retrieved out of the first index in the InfoObjects
   /// class. That InfoObject is then downcast to User, and the Groups
   /// property of User is called to retrieve the Groups indexed class
   /// containing group IDs as Integers. A foreach loop requeries the
   /// CMS for each iteration, retrieving each group Title by the group
   /// ID. Each ID and Title is added into the SortedList key/value
   /// pair. The SortedList instance is then returned from the method.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the user whose groups are being retrieved.
   /// The IDictionary interface, a generic key/value collection.
   public static IDictionary GetMemberGroups(InfoStore infoStore, string userID)
   {
   IDictionary sortedList = new SortedList();
   string query = "Select Top 1* From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + userID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
   CrystalDecisions.Enterprise.Desktop.User user =
   (CrystalDecisions.Enterprise.Desktop.User)infoObject;
  
   Groups groups = user.Groups;
  
   foreach (int group in groups)
   {
   query = "Select SI_NAME From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + group;
   infoObjects = infoStore.Query(query);
   infoObject = infoObjects[1];
  
   sortedList.Add(infoObject.ID, infoObject.Title);
   }
  
   return sortedList;
   }
  
   ///
   /// In this method, the User instance is retrieved from the CMS
   /// server based on the user ID. The Groups property of the User
   /// instance is retrieved, and the group ID for the group to be
   /// added is passed into the Add method of Groups. The User instance
   /// is then saved back to the CMS server by calling the Commit
   /// method of InfoStore and passing in the enclosing InfoObjects
   /// indexed class.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the user added to a group.
   /// The ID of the group the user is added to.
   public static void AddUserToGroup(InfoStore infoStore, string userID, int groupID)
   {
   string query = "Select SI_ID From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + userID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
   CrystalDecisions.Enterprise.Desktop.User user =
   (CrystalDecisions.Enterprise.Desktop.User)infoObject;
  
   Groups groups = user.Groups;
   groups.Add(groupID);
  
   infoStore.Commit(infoObjects);
   }
  
   ///
   /// In this method, a PluginManager instance is retrieved from the
   /// PluginManager property of InfoStore. The PluginInfo instance
   /// is retrieved by passing the string 慍CrystalEnterprise.UserGroup?
   /// to the GetPluginInfo method of the PluginManager instance. A new
   /// InfoObjects indexed class is created by calling the
   /// NewInfoObjectsCollection method of InfoStore and the PluginInfo
   /// instance is then added to the InfoObjects instance. The InfoObject
   /// is then retrieved from the first index of the InfoObjects indexed
   /// class. InfoObject is downcast to UserGroup. The Title property
   /// of the UserGroup is assigned from a method parameter and the
   /// Users property calls its Add method to add the userID passed in
   /// from the a method parameter. The InfoObjects instance containing
   /// the UserGroup instance is then committed back to the CMS server
   /// using the Commit method of InfoStore.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the user added to a group.
   /// The name of the new group.
   public static void AddUserToNewGroup(InfoStore infoStore, string userID, string groupName)
   {
   PluginManager pluginManager = infoStore.PluginManager;
   PluginInfo pluginInfo = pluginManager.GetPluginInfo("CrystalEnterprise.UserGroup");
   InfoObjects infoObjects = infoStore.NewInfoObjectCollection();
   infoObjects.Add(pluginInfo);
  
   InfoObject infoObject = infoObjects[1];
   UserGroup userGroup = (UserGroup)infoObject;
  
   userGroup.Title = groupName;
  
   int userIDInt = Convert.ToInt32(userID);
   userGroup.Users.Add(userIDInt);
  
   infoStore.Commit(infoObjects);
   }
  
   ///
   /// In this method, the User instance is retrieved from the CMS
   /// server based on the user ID. The Groups property of the User
   /// instance is retrieved, and the group ID for the group to be
   /// deleted is passed into the Delete method of Groups, preceeded
   /// by the # symbol to distinguish the group ID from an index. The
   /// User instance is then saved back to the CMS server by calling
   /// the Commit method of InfoStore and passing in the enclosing
   /// InfoObjects indexed class.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the user removed from the group.
   /// The ID of the group the user is removed from.
   public static void RemoveUserFromGroup(InfoStore infoStore, string userID, string groupID)
   {
   string query = "Select SI_ID, SI_USERGROUPS From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + userID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
   CrystalDecisions.Enterprise.Desktop.User user =
   (CrystalDecisions.Enterprise.Desktop.User)infoObject;
  
   Groups groups = user.Groups;
   groups.Delete("#" + groupID);
  
   infoStore.Commit(infoObjects);
   }
  
  
   ///
   /// In this method, the User instance is retrieved from the CMS
   /// server based on the user ID. If the Title property is set to
   /// Administrator, an exception is thrown, otherwise the retrieved
   /// User instance is deleted and the enclosing InfoObjects indexed
   /// class is committed back to the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the user removed from the group.
   public static void DeleteUser(InfoStore infoStore, string userID)
   {
   string query = "Select * From CI_SYSTEMOBJECTS "
   + "Where SI_KIND='User' "
   + "And SI_ID=" + userID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
  
   if (infoObject.Title != "Administrator")
   {
   infoObjects.Delete(infoObject);
   infoStore.Commit(infoObjects);
   }
   else
   {
   throw new Exception(SampleCodeMessages.NOT_ALLOWED);
   }
   }
  
  
   ///
   /// In this method, the User instance is retrieved from the CMS
   /// server based on the user ID. If an alternate password has been
   /// provided, the alternate password is assigned to the NewPassword
   /// property of the User instance, else 揘NewPassword?is assigned
   /// to the NewPassword property of the User instance. The User
   /// instance is then saved back to the CMS server by calling the
   /// Commit method of InfoStore and passing in the enclosing
   /// InfoObjects indexed class.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the user removed from the group.
   /// A supplied password string to be used.
   public static void ResetUserPassword(InfoStore infoStore, string userID, string alternatePassword)
   {
   string query = "Select SI_NEW_PASSWORD From CI_SYSTEMOBJECTS "
   + "Where SI_KIND='User' "
   + "And SI_ID=" + userID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
   CrystalDecisions.Enterprise.Desktop.User user =
   (CrystalDecisions.Enterprise.Desktop.User)infoObject;
  
   if (alternatePassword != "")
   {
   user.NewPassword = alternatePassword;
   }
   else
   {
   user.NewPassword = "NewPassword";
   }
  
   infoStore.Commit(infoObjects);
   }
  
   ///
   /// In this method, a jagged array is created containing
   /// multiple inner arrays of two columns each. The array
   /// is then passed through a for loop to assign the inner
   /// array values to the key/value pairs of a sortedList
   /// instance upcast to the generic IDictionary interface.
   ///
   /// The IDictionary interface, a generic key/value collection.
   public static IDictionary GetAuthenticationTypes()
   {
   IDictionary sortedList = new SortedList();
  
   string[][] outerJaggedArray =
   {
   new string[] {"secEnterprise", "Enterprise"},
   new string[] {"secWindowsNT", "Windows NT"}
   };
  
   foreach (string[] innerArray in outerJaggedArray)
   {
   sortedList.Add(innerArray[0], innerArray[1]);
   }
  
   return sortedList;
   }
  
  
  
   ///
   /// In this method, a SortedList upcast to IDictionary is declared
   /// and instantiated. A query is run that retrieves a list of all
   /// Servers in the CMS. The query is retrieved into an InfoObjects
   /// indexed class, and a foreach loop goes through the class and
   /// puts each ID property and Title property of the InfoObject
   /// instance into the SortedList key/value pair. The SortedList
   /// instance is then returned from the method.
   ///
   /// Manages access to the CMS repository.
   /// The IDictionary interface, a generic key/value collection.
   public static IDictionary GetServers(InfoStore infoStore)
   {
   IDictionary sortedList = new SortedList();
  
   string query = "Select * From CI_SYSTEMOBJECTS "
   + "Where SI_KIND='Server'";
  
   InfoObjects infoObjects = infoStore.Query(query);
  
   if (infoObjects.Count > 0)
   {
   foreach (InfoObject infoObject in infoObjects)
   {
   sortedList.Add(infoObject.ID, infoObject.Title);
   }
   }
   else
   {
   sortedList.Add("0", SampleCodeMessages.NO_MATCHES_FOUND);
   }
  
   return sortedList;
   }
  
  
  
   ///
   /// In this method, a SortedList upcast to IDictionary is declared
   /// and instantiated. A query is run that selects subgroups by
   /// serverGroupID. The query is retrieved into an InfoObjects
   /// indexed class, and an InfoObject instance is retrieved from the
   /// first index and then downcast to a ServerGroup instance. A
   /// ServerGroupAssociates indexed class is retrieved from the
   /// SubGroups property of the ServerGroup instance. If the count
   /// of ServerGroupAssociates is greater than 0, then a for each loop
   /// loops through the ServerGroupAssociates indexed class to retrieve
   /// individual subServerGroupID strings. For each retrieved string,
   /// a new query is run and a new InfoObjects indexed class instance
   /// is retrieved. The first index of the InfoObjects instance is
   /// retrieved into an InfoObject instance, and the ID and Title are
   /// added to the SortedList. The SortedList instance is then returned
   /// from the method.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the currently selected server group.
   /// The IDictionary interface, a generic key/value collection.
   public static IDictionary GetGroupsInServerGroup(InfoStore infoStore, string serverGroupID)
   {
   IDictionary sortedList = new SortedList();
  
   string query = "Select SI_SUBGROUPS From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + serverGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
  
   InfoObject infoObject = infoObjects[1];
   ServerGroup serverGroup = (ServerGroup)infoObject;
   ServerGroupAssociates serverGroupAssociates = serverGroup.SubGroups;
  
   if (serverGroupAssociates.Count > 0)
   {
   foreach (int subServerGroupID in serverGroupAssociates)
   {
   query = "Select SI_ID, SI_NAME From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + subServerGroupID;
   infoObjects = infoStore.Query(query);
   InfoObject subServerGroupInfoObject = infoObjects[1];
   sortedList.Add(subServerGroupInfoObject.ID, subServerGroupInfoObject.Title);
   }
   }
   else
   {
   sortedList.Add("0", SampleCodeMessages.NO_MATCHES_FOUND);
   }
  
   return sortedList;
   }
  
   ///
   /// In this method, a PluginManager instance is retrieved from the
   /// PluginManager property of InfoStore. The PluginInfo instance is
   /// retrieved by passing the string 慍CrystalEnterprise.ServerGroup?
   /// to the GetPluginInfo method of the PluginManager instance. A
   /// new InfoObjects indexed class is created by calling the
   /// NewInfoObjectsCollection method of InfoStore and the PluginInfo
   /// instance is then added to the InfoObjects instance. The InfoObject
   /// is then retrieved from the first index of the InfoObjects indexed
   /// class. InfoObject is downcast to ServerGroup. Two properties of
   /// the ServerGroup instance are then set. The InfoObjects instance
   /// containing the ServerGroup instance is then committed back to
   /// the CMS server using the Commit method of InfoStore.
   ///
   /// Manages access to the CMS repository.
   /// The name of the server group to be created.
   /// The description of the server group to be created.
   public static void CreateNewServerGroup(InfoStore infoStore, string name, string description)
   {
   PluginManager pluginManager = infoStore.PluginManager;
   PluginInfo pluginInfo = pluginManager.GetPluginInfo("CrystalEnterprise.ServerGroup");
   InfoObjects infoObjects = infoStore.NewInfoObjectCollection();
   InfoObject infoObject = infoObjects.Add(pluginInfo);
   ServerGroup serverGroup = (ServerGroup)infoObject;
  
   serverGroup.Title = name;
   serverGroup.Description = description;
  
   infoStore.Commit(infoObjects);
   }
  
  
  
   ///
   /// In this method, the current ServerGroup instance is retrieved
   /// from the CMS server based on the serverGroupID and placed in
   /// an InfoObjects indexed class instance. The InfoObject is retrieved
   /// from the first index of InfoObjects and downcast to ServerGroup.
   /// The GroupServers instance is retrieved from the Servers property
   /// of ServerGroup. The Add method of GroupServers is called, passing
   /// in the serverName string parameter. Finally, the enclosing
   /// InfoObjects indexed class is committed back to the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The name (Title property of InfoObject) of the server to be added.
   /// The ID of the currently selected server group.
   public static void AddServerToServerGroup(InfoStore infoStore, string serverName, string serverGroupID)
   {
   string query = "Select SI_GROUP_MEMBERS From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + serverGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
  
   ServerGroup serverGroup = (ServerGroup)infoObject;
   GroupServers groupServers = serverGroup.Servers;
   groupServers.Add(serverName);
  
   infoStore.Commit(infoObjects);
   }
  
  
   ///
   /// In this method, the current ServerGroup instance is retrieved
   /// from the CMS server based on the serverGroupID and placed in
   /// an InfoObjects indexed class instance. The InfoObject is retrieved
   /// from the first index of InfoObjects and downcast to ServerGroup.
   /// The GroupServers instance is retrieved from the Servers property
   /// of ServerGroup. The Clear method of GroupServers is called.
   /// Finally, the enclosing InfoObjects indexed class is committed
   /// back to the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the currently selected server group.
   public static void ClearAllServersFromServerGroup(InfoStore infoStore, string serverGroupID)
   {
   string query = "Select SI_GROUP_MEMBERS From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + serverGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
  
   ServerGroup serverGroup = (ServerGroup)infoObject;
   GroupServers groupServers = serverGroup.Servers;
   groupServers.Clear();
  
   infoStore.Commit(infoObjects);
   }
  
  
  
   ///
   /// In this method, the current ServerGroup instance is retrieved
   /// from the CMS server based on the serverGroupID and placed in
   /// an InfoObjects indexed class instance. The InfoObject is retrieved
   /// from the first index of InfoObjects and downcast to ServerGroup.
   /// The ServerGroupAssociates instance is retrieved from the SubGroups
   /// property of ServerGroup. The Delete method of ServerGroupAssociates
   /// is called, passing in the # symbol and the nestedServerGroupID.
   /// Finally, the enclosing InfoObjects indexed class is committed back
   /// to the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the server group to be nested in the curent server group.
   /// The ID of the currently selected server group.
   public static void RemoveNestedServerGroupFromServerGroup(InfoStore infoStore, string nestedServerGroupID, string serverGroupID)
   {
   string query = "Select SI_SUBGROUPS From CI_SYSTEMOBJECTS "
   + "Where SI_ID=" + serverGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
  
   ServerGroup serverGroup = (ServerGroup)infoObject;
   ServerGroupAssociates serverGroupAssociates = serverGroup.SubGroups;
   serverGroupAssociates.Delete("#" + nestedServerGroupID);
  
   infoStore.Commit(infoObjects);
   }
  
  
  
  
   ///
   /// In this method, a SortedList upcast to IDictionary is declared
   /// and instantiated. A query is run that retrieves a list of all
   /// user groups in the CMS. The query is retrieved into an
   /// InfoObjects indexed class, and a foreach loop goes through the
   /// class and puts each ID property and Title property of the
   /// InfoObject instance into the SortedList key/value pair. The
   /// SortedList instance is then returned from the method.
   ///
   /// Manages access to the CMS repository.
   /// The IDictionary interface, a generic key/value collection.
   public static IDictionary GetUserGroups(InfoStore infoStore)
   {
   IDictionary sortedList = new SortedList();
   string query = "Select SI_ID, SI_NAME From CI_SYSTEMOBJECTS "
   + "Where SI_KIND='UserGroup'";
  
   InfoObjects infoObjects = infoStore.Query(query);
  
   foreach (InfoObject infoObject in infoObjects)
   {
   sortedList.Add(infoObject.ID, infoObject.Title);
   }
  
   return sortedList;
   }
  
  
   ///
   /// In this method, a PluginManager instance is retrieved from the
   /// PluginManager property of InfoStore. The PluginInfo instance
   /// is retrieved by passing the string 慍CrystalEnterprise.UserGroup?
   /// to the GetPluginInfo method of the PluginManager instance. A
   /// new InfoObjects indexed class is created by calling the
   /// NewInfoObjectsCollection method of InfoStore and the PluginInfo
   /// instance is then added to the InfoObjects instance. The InfoObject
   /// is then retrieved from the first index of the InfoObjects indexed
   /// class. InfoObject is downcast to UserGroup. Two properties of
   /// the UserGroup instance are then set. The InfoObjects instance
   /// containing the UserGroup instance is then committed back to the
   /// CMS server using the Commit method of InfoStore.
   ///
   /// Manages access to the CMS repository.
   /// The name of the group to be added.
   /// The description of the group to be added.
   public static void AddUserGroup(InfoStore infoStore, string name, string description)
   {
   PluginManager pluginManager = infoStore.PluginManager;
   PluginInfo pluginInfo = pluginManager.GetPluginInfo("CrystalEnterprise.UserGroup");
   InfoObjects infoObjects = infoStore.NewInfoObjectCollection();
   infoObjects.Add(pluginInfo);
  
   InfoObject infoObject = infoObjects[1];
   UserGroup userGroup = (UserGroup)infoObject;
  
   userGroup.Title = name;
   userGroup.Description = description;
  
   infoStore.Commit(infoObjects);
   }
  
  
   ///
   /// In this method, the UserGroup instance is retrieved from the
   /// CMS server based on the user group ID. If the Title property
   /// is set to Administrators, an exception is thrown, otherwise
   /// the retrieved UserGroup instance is deleted and the enclosing
   /// InfoObjects indexed class is committed back to the CMS server.
   ///
   /// Manages access to the CMS repository.
   /// The ID of the user group to be deleted.
   public static void DeleteUserGroup(InfoStore infoStore, string userGroupID)
   {
   string query = "Select * From CI_SYSTEMOBJECTS "
   + "Where SI_KIND='UserGroup' "
   + "And SI_ID=" + userGroupID;
  
   InfoObjects infoObjects = infoStore.Query(query);
   InfoObject infoObject = infoObjects[1];
   UserGroup userGroup = (UserGroup)infoObject;
  
   if (userGroup.Title != "Administrators")
   {
   infoObjects.Delete(userGroup);
   infoStore.Commit(infoObjects);
   }
   else
   {
   throw new Exception(SampleCodeMessages.NOT_ALLOWED);
   }
   }
   }
  }