LiteMDA中支持Generic的BusinessObjectFactory实现
在C# 2.0中提供了Generic支持,我们的Factory就也应该支持Generic,以下是我在LiteMDA的BusinessObjectFactory中的实现,同时支持Generic和非Generic的类。
关于Interface及其对应的Assembly和Class的描述信息存在以下的BusinessObjectConfiguration.config中:
以上的Configuration中定义了三个可以被BusinessObjectFactory构造的类,构造时只需提供Interface,这里给出了三种典型的情况,一个是非Generic、一个是接口为Generic,类也为Generic,第三个是接口非Generic,类Generic。
BusinessObjectFactory的源码如下:
几个测试类和接口代码如下:
ITestClass.cs
ITestClass2.cs
IGenericTestClass.cs
TestClass.cs
GenericTestClass.cs
测试代码如下:
--
顺便说一下,以上的Configuration使用EnterpriseLibrary读写的。在vs.net 2005 beta2中使用EntLib我费了一番波折才搞定,这里说以下怎样设置,免得大家再走弯路。
你需要先用操作系统的搜索功能搜索entlib中src目录下所有包含"define VS2003"的.cs文件,大概十几个,把所有这些文件顶部的"VS2003"改成"VS2005B2",然后用vs.net 2005 beta2(vc#2005beta2也一样)打开EnterpriseLibrary.sln重新编译所有程序集,同时请使用重新编后的Enterprise Library Configuration Tool来建立Application的App.config或web.config。
//文章结束
关于Interface及其对应的Assembly和Class的描述信息存在以下的BusinessObjectConfiguration.config中:
<?xml version="1.0" encoding="utf-8"?>
<BusinessObjectConfiguration>
<xmlSerializerSection type="LiteMDA.Configuration.BusinessObjectConfiguration, LiteMDA.Configuration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<BusinessObjectConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<VisibleBusinessObjects>
<BusinessObject>
<InterfaceName>LiteMDA.BusinessObjectFactory.Test.ITestClass</InterfaceName>
<Class AssemblyName="LiteMDA.BusinessObjectFactory.Test.exe" ClassName="LiteMDA.BusinessObjectFactory.Test.TestClass" />
</BusinessObject>
<BusinessObject>
<InterfaceName>LiteMDA.BusinessObjectFactory.Test.IGenericTestClass`2[System.String,System.Object]</InterfaceName>
<Class AssemblyName="LiteMDA.BusinessObjectFactory.Test.exe" ClassName="LiteMDA.BusinessObjectFactory.Test.GenericTestClass`2[System.String,System.Object]" />
</BusinessObject>
<BusinessObject>
<InterfaceName>LiteMDA.BusinessObjectFactory.Test.ITestClass2</InterfaceName>
<Class AssemblyName="LiteMDA.BusinessObjectFactory.Test.exe" ClassName="LiteMDA.BusinessObjectFactory.Test.GenericTestClass`2[System.String,System.Object]" />
</BusinessObject>
</VisibleBusinessObjects>
</BusinessObjectConfiguration>
</xmlSerializerSection>
</BusinessObjectConfiguration>
<BusinessObjectConfiguration>
<xmlSerializerSection type="LiteMDA.Configuration.BusinessObjectConfiguration, LiteMDA.Configuration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<BusinessObjectConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<VisibleBusinessObjects>
<BusinessObject>
<InterfaceName>LiteMDA.BusinessObjectFactory.Test.ITestClass</InterfaceName>
<Class AssemblyName="LiteMDA.BusinessObjectFactory.Test.exe" ClassName="LiteMDA.BusinessObjectFactory.Test.TestClass" />
</BusinessObject>
<BusinessObject>
<InterfaceName>LiteMDA.BusinessObjectFactory.Test.IGenericTestClass`2[System.String,System.Object]</InterfaceName>
<Class AssemblyName="LiteMDA.BusinessObjectFactory.Test.exe" ClassName="LiteMDA.BusinessObjectFactory.Test.GenericTestClass`2[System.String,System.Object]" />
</BusinessObject>
<BusinessObject>
<InterfaceName>LiteMDA.BusinessObjectFactory.Test.ITestClass2</InterfaceName>
<Class AssemblyName="LiteMDA.BusinessObjectFactory.Test.exe" ClassName="LiteMDA.BusinessObjectFactory.Test.GenericTestClass`2[System.String,System.Object]" />
</BusinessObject>
</VisibleBusinessObjects>
</BusinessObjectConfiguration>
</xmlSerializerSection>
</BusinessObjectConfiguration>
以上的Configuration中定义了三个可以被BusinessObjectFactory构造的类,构造时只需提供Interface,这里给出了三种典型的情况,一个是非Generic、一个是接口为Generic,类也为Generic,第三个是接口非Generic,类Generic。
BusinessObjectFactory的源码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using LiteMDA.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Configuration;
namespace LiteMDA.BusinessObjectFactory
{
/// <summary>
/// ObjectFactory is the only gateway for creating Business Objects.
/// </summary>
public sealed class ObjectFactory
{
private ObjectFactory()
{
}
/// <summary>
/// _ObjectMap saves all the BOI/BO mapping pairs, this field will
/// be constructed from the BOI exposing configuration file at the
/// first time when the CreateObject method is invoked.
/// </summary>
private static BusinessObjectConfiguration _BOC = null;
/// <summary>
/// Create a Business Object.
/// </summary>
/// <typeparam name="TInterface">An exposed Business Object Interface.</typeparam>
/// <returns>Return Business Object instance that implementing TInterface.</returns>
/// <exception cref="LiteMDA.BusinessObjectFactory.InterfaceNotExposedException">
/// Thrown when the specified TInterface is not in the exposed interface list.
/// </exception>
/// <exception cref="LiteMDA.BusinessObjectFactory.ConfigurationFileFormatErrorException">
/// Thrown when the the format of the configuration file for BO/BOI mapping is error.
/// </exception>
public static TInterface CreateObject<TInterface>()
{
if (_BOC == null)
{
// if this is the first time invoking CreateObject, load BOI/BO
// mapping pairs from the configuration file first.
_BOC = ConfigurationManager.GetConfiguration("BusinessObjectConfiguration") as BusinessObjectConfiguration;
}
AssemblyClass ac = _BOC[typeof(TInterface).ToString()];
if (ac != null)
{
//create object instance from the specified assemblyName and className
Assembly ass = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + ac.AssemblyName);
if (ac.ClassName.Contains("`"))
{
//It is a generic type, so fetch Type Parameters and bind then to the type before CreateInstance
Type t = ass.GetType(ac.ClassName.Substring(0, ac.ClassName.IndexOf("[")));
string typeParamListStr = ac.ClassName.Substring(ac.ClassName.IndexOf("[") + 1).TrimEnd(']');
string[] typeParamNames = typeParamListStr.Split(',');
Type[] typeParams = new Type[typeParamNames.Length];
for (int i = 0; i < typeParamNames.Length; i++)
{
typeParams[i] = Type.GetType(typeParamNames[i].Trim());
}
t = t.MakeGenericType(typeParams);
return (TInterface)Activator.CreateInstance(t);
}
else
{
return (TInterface)ass.CreateInstance(ac.ClassName);
}
}
else
{
throw new InterfaceNotExposedException(typeof(TInterface).ToString());
}
}
}
}
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using LiteMDA.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Configuration;
namespace LiteMDA.BusinessObjectFactory
{
/// <summary>
/// ObjectFactory is the only gateway for creating Business Objects.
/// </summary>
public sealed class ObjectFactory
{
private ObjectFactory()
{
}
/// <summary>
/// _ObjectMap saves all the BOI/BO mapping pairs, this field will
/// be constructed from the BOI exposing configuration file at the
/// first time when the CreateObject method is invoked.
/// </summary>
private static BusinessObjectConfiguration _BOC = null;
/// <summary>
/// Create a Business Object.
/// </summary>
/// <typeparam name="TInterface">An exposed Business Object Interface.</typeparam>
/// <returns>Return Business Object instance that implementing TInterface.</returns>
/// <exception cref="LiteMDA.BusinessObjectFactory.InterfaceNotExposedException">
/// Thrown when the specified TInterface is not in the exposed interface list.
/// </exception>
/// <exception cref="LiteMDA.BusinessObjectFactory.ConfigurationFileFormatErrorException">
/// Thrown when the the format of the configuration file for BO/BOI mapping is error.
/// </exception>
public static TInterface CreateObject<TInterface>()
{
if (_BOC == null)
{
// if this is the first time invoking CreateObject, load BOI/BO
// mapping pairs from the configuration file first.
_BOC = ConfigurationManager.GetConfiguration("BusinessObjectConfiguration") as BusinessObjectConfiguration;
}
AssemblyClass ac = _BOC[typeof(TInterface).ToString()];
if (ac != null)
{
//create object instance from the specified assemblyName and className
Assembly ass = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + ac.AssemblyName);
if (ac.ClassName.Contains("`"))
{
//It is a generic type, so fetch Type Parameters and bind then to the type before CreateInstance
Type t = ass.GetType(ac.ClassName.Substring(0, ac.ClassName.IndexOf("[")));
string typeParamListStr = ac.ClassName.Substring(ac.ClassName.IndexOf("[") + 1).TrimEnd(']');
string[] typeParamNames = typeParamListStr.Split(',');
Type[] typeParams = new Type[typeParamNames.Length];
for (int i = 0; i < typeParamNames.Length; i++)
{
typeParams[i] = Type.GetType(typeParamNames[i].Trim());
}
t = t.MakeGenericType(typeParams);
return (TInterface)Activator.CreateInstance(t);
}
else
{
return (TInterface)ass.CreateInstance(ac.ClassName);
}
}
else
{
throw new InterfaceNotExposedException(typeof(TInterface).ToString());
}
}
}
}
几个测试类和接口代码如下:
ITestClass.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public interface ITestClass
{
void TestMethod();
}
}
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public interface ITestClass
{
void TestMethod();
}
}
ITestClass2.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public interface ITestClass2
{
void TestMethod2();
}
}
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public interface ITestClass2
{
void TestMethod2();
}
}
IGenericTestClass.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public interface IGenericTestClass<T, U>
{
void TestMethod();
}
}
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public interface IGenericTestClass<T, U>
{
void TestMethod();
}
}
TestClass.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public class TestClass : ITestClass
{
#region ITestClass Members
public void TestMethod()
{
System.Windows.Forms.MessageBox.Show("OK");
}
#endregion
}
}
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public class TestClass : ITestClass
{
#region ITestClass Members
public void TestMethod()
{
System.Windows.Forms.MessageBox.Show("OK");
}
#endregion
}
}
GenericTestClass.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public class GenericTestClass<T, U> : IGenericTestClass<T, U>, ITestClass2
{
public void TestMethod()
{
System.Windows.Forms.MessageBox.Show("OK2");
}
public void TestMethod2()
{
System.Windows.Forms.MessageBox.Show("OK3");
}
}
}
using System.Collections.Generic;
using System.Text;
namespace LiteMDA.BusinessObjectFactory.Test
{
public class GenericTestClass<T, U> : IGenericTestClass<T, U>, ITestClass2
{
public void TestMethod()
{
System.Windows.Forms.MessageBox.Show("OK2");
}
public void TestMethod2()
{
System.Windows.Forms.MessageBox.Show("OK3");
}
}
}
测试代码如下:
private void btnTestCreateBusinessObject_Click(object sender, EventArgs e)
{
ITestClass tc = ObjectFactory.CreateObject<ITestClass>();
tc.TestMethod();
IGenericTestClass<string, object> gtc = ObjectFactory.CreateObject<IGenericTestClass<string, object>>();
gtc.TestMethod();
ITestClass2 tc2 = ObjectFactory.CreateObject<ITestClass2>();
tc2.TestMethod2();
}
{
ITestClass tc = ObjectFactory.CreateObject<ITestClass>();
tc.TestMethod();
IGenericTestClass<string, object> gtc = ObjectFactory.CreateObject<IGenericTestClass<string, object>>();
gtc.TestMethod();
ITestClass2 tc2 = ObjectFactory.CreateObject<ITestClass2>();
tc2.TestMethod2();
}
--
顺便说一下,以上的Configuration使用EnterpriseLibrary读写的。在vs.net 2005 beta2中使用EntLib我费了一番波折才搞定,这里说以下怎样设置,免得大家再走弯路。
你需要先用操作系统的搜索功能搜索entlib中src目录下所有包含"define VS2003"的.cs文件,大概十几个,把所有这些文件顶部的"VS2003"改成"VS2005B2",然后用vs.net 2005 beta2(vc#2005beta2也一样)打开EnterpriseLibrary.sln重新编译所有程序集,同时请使用重新编后的Enterprise Library Configuration Tool来建立Application的App.config或web.config。
//文章结束