using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Runtime.Remoting;
namespace WcfServiceHost
{
public static class WcfGenerator
{
///<summary>
/// Generate type for WCF service. This type serves as proxy of a true type.
/// </summary>
/// <param name="path">Path to an assembly file containing the true type.</param>
/// <param name="className">Name of a generated WCF proxy type.</param>
/// <param name="sessionAllowed">Session support indicator.</param>
/// <returns>Type for WCF service.</returns>
static public Type GenerateServiceType(string path, string className, bool sessionAllowed)
{
Type retType = null;
Assembly assm = Assembly.LoadFrom(path);
Type serviceContract;
string csCode = GenerateInterfaceAndClass(path, className, assm, sessionAllowed, out serviceContract);
if (!string.IsNullOrEmpty(csCode))
{
CompilerResults res = BuildGeneratedCode(csCode, path, className, assm, serviceContract, false);
foreach (Type type in res.CompiledAssembly.GetTypes())
if (type.Name.Contains(className) && type.IsClass)
{
retType = type;
break;
}
}
return retType;
}
/// <summary>
/// Generate a string containing C# code for WCF service type and its base interface and
/// produce type of generated WCF service.
/// </summary>
/// <param name="path">Path to an assembly file containing the true type.</param>
/// <param name="genClassName">Name of a generated WCF proxy type. It is extended with "_PerCall suffix in appropriate case.</param>
/// <param name="assm">Assembly containing the true type; located by the "path" parameter.</param>
/// <param name="sessionAllowed">Session support indicator.</param>
/// <param name="serviceContract">Type of generated WCF service.</param>
/// <returns>String containing C# code for WCF service type and its base interface.</returns>
static private string GenerateInterfaceAndClass(string path, string genClassName, Assembly assm, bool sessionAllowed, out Type serviceContract)
{
string className = sessionAllowed ? genClassName : genClassName + "_PerCall";
string serviceContractForInterface = "[ServiceContract(SessionMode = SessionMode.Allowed)]";
string serviceBehaviorForClass =
"[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, " +
string.Format("InstanceContextMode = InstanceContextMode.Per{0})]", sessionAllowed ? "Session" : "Call");
string sr = null;
List<MethodInfo> lstMethod = new List<MethodInfo>();
Dictionary<string, StringBuilder> dctSbMethodBody = new Dictionary<string, StringBuilder>();
Dictionary<string, string> dctClassMethod = new Dictionary<string, string>();
StringBuilder sb = new StringBuilder();
serviceContract = null;
// Find class with name matched to given
foreach (Type type in assm.GetTypes())
if (type.Name.Contains(genClassName) && !type.IsInterface)
{
serviceContract = type;
break;
}
if (null != serviceContract)
{
foreach (MethodInfo method in serviceContract.GetMethods())
if (IsExposed(method))
lstMethod.Add(method);
if (lstMethod.Count > 0)
{
string interfaceName = string.Format("I{0}", className);
sb.Append("$Prefix$\npublic $Class$$BaseInterface$\n");
sb.Append("{\n");
sb.Append("$Ctor$");
foreach (MethodInfo method in lstMethod)
{
StringBuilder sbMethod = new StringBuilder();
sbMethod.AppendFormat("$OperationContractAttribute$\n$Public${0} {1}(", method.ReturnType.FullName.ToString(), method.Name);
Dictionary<int, ParameterInfo> dctParam = new Dictionary<int, ParameterInfo>();
foreach (ParameterInfo param in method.GetParameters())
dctParam.Add(param.Position, param);
for (int i = 0; i < dctParam.Count; i++)
{
ParameterInfo param = dctParam[i];
string prefix = string.Empty;
if (param.IsOut)
prefix = "out ";
if (param.IsIn)
prefix = "ref ";
sbMethod.AppendFormat("{0}{1} {2}", prefix,
param.ParameterType.FullName.ToString(),
param.Name);
if (i + 1 < dctParam.Count)
sbMethod.Append(",\n");
}
sbMethod.Append(")");
sbMethod.Append("$BeforeMethodBody$");
sbMethod.AppendFormat("$MethodBody_{0}$", method.Name);
StringBuilder sbMethodBody = new StringBuilder();
sbMethodBody.Append("ArrayList alParam = new ArrayList();\n");
for (int i = 0; i < dctParam.Count; i++)
sbMethodBody.AppendFormat("alParam.Add({0});\n", dctParam[i].Name);
string returnTypeName = method.ReturnType.FullName.ToString();
string sInvoke = string.Format("typeof({0}).GetMethod(\"{1}\").Invoke(obj, alParam.ToArray());\n", serviceContract.FullName, method.Name);
if ("System.Void" == returnTypeName)
sbMethodBody.Append(sInvoke);
else
sbMethodBody.AppendFormat("return ({0}){1}", returnTypeName, sInvoke);
dctSbMethodBody.Add(method.Name, sbMethodBody);
sbMethod.Append("$AfterMethodBody$");
dctClassMethod.Add(method.Name, sbMethod.ToString());
sb.AppendFormat("${0}$\n", method.Name);
}
sb.Append("}\n");
string sTemp = sb.ToString();
string sTemp1 = sTemp;
foreach (string methodName in dctClassMethod.Keys)
{
sTemp1 = sTemp.Replace(string.Format("${0}$", methodName), dctClassMethod[methodName]);
sTemp = sTemp1;
}
string sInterface, sClass;
sInterface = sClass = sTemp;
string interfaceBody = sInterface.
Replace("$Prefix$", serviceContractForInterface).
Replace("$BaseInterface$", string.Empty).
Replace("$OperationContractAttribute$", "[OperationContract]").
Replace("$Public$", string.Empty).
Replace("$BeforeMethodBody$", ";\n").
Replace("$AfterMethodBody$", string.Empty).
Replace("$Ctor$", string.Empty).
Replace("$Class$", string.Format("interface {0}", interfaceName));
string classBody = sClass.
Replace("$Prefix$", serviceBehaviorForClass).
Replace("$BaseInterface$", string.Format(" : {0}", interfaceName)).
Replace("$OperationContractAttribute$", string.Empty).
Replace("$Public$", "public ").
Replace("$BeforeMethodBody$", "\n{\n").
Replace("$AfterMethodBody$", "}").
Replace("$Ctor$", string.Format(
"\n{0} obj;\n\npublic {1}()\n{2}\nSystem.Reflection.Assembly assm = System.Reflection.Assembly.GetAssembly(typeof({0}));\nobj = ({0})assm.CreateInstance(\"{0}\");\n{3}\n",
serviceContract.FullName, className, "{", "}")).
Replace("$Class$", string.Format("class {0}", className));
foreach (string methodName in dctSbMethodBody.Keys)
{
string pattern = string.Format("$MethodBody_{0}$", methodName);
sInterface = interfaceBody.Replace(pattern, string.Empty);
sClass = classBody.Replace(pattern, dctSbMethodBody[methodName].ToString());
interfaceBody = sInterface;
classBody = sClass;
}
StringBuilder sbInterface = new StringBuilder(interfaceBody);
StringBuilder sbClass = new StringBuilder(classBody);
sb.Remove(0, sb.Length);
sb.Append("using System;\nusing System.ServiceModel;\nusing System.Collections;\n\n");
sb.AppendFormat("{0}\n\n{1}", interfaceBody, classBody);
sr = sb.ToString().Replace("System.Void", "void");
}
else
Console.WriteLine("ERROR! No methods exposed.\n");
}
else
Console.WriteLine("ERROR! Unable to generate Service Contract.\n");
return sr;
}
/// <summary>
/// Perform build of the generated C# of WCF proxy type.
/// </summary>
/// <param name="csCode">C# code for WCF service type and its base interface.</param>
/// <param name="path">Path to an assembly file containing the true type.</param>
/// <param name="className">Name of a generated WCF proxy type.</param>
/// <param name="assm">Assembly containing the true type; located by the "path" parameter.</param>
/// <param name="serviceContract"></param>
/// <param name="debugInfo">Debug info and the temporary files generation indicator.</param>
/// <returns>Result of the generated WCF type in-memory compilation.</returns>
static private CompilerResults BuildGeneratedCode(string csCode, string path, string className, Assembly assm, Type serviceContract, bool debugInfo)
{
CompilerResults res = null;
try
{
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
// Add references for compilation
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add(@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.ServiceModel.dll");
cp.ReferencedAssemblies.Add(@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Runtime.Serialization.dll");
cp.ReferencedAssemblies.Add(path);
AddReferencedAssm(cp, serviceContract);
cp.GenerateExecutable = false;
cp.IncludeDebugInformation = debugInfo;
cp.GenerateInMemory = true;
cp.WarningLevel = 3;
cp.TreatWarningsAsErrors = false;
cp.CompilerOptions = "/optimize";
if (cp.IncludeDebugInformation)
// Set a temporary files collection.
// The TempFileCollection stores the temporary files
// generated during a build in the current directory,
// and does not delete them after compilation.
cp.TempFiles = new TempFileCollection(@"..\_Temp", true);
else
{
cp.TempFiles.Delete();
cp.TempFiles = null;
}
res = codeProvider.CompileAssemblyFromSource(cp, new string[] { csCode });
if (res.Errors.HasErrors)
{
Console.WriteLine("{0} Compilation ERROR(S):\n", res.Errors.Count);
foreach (CompilerError ce in res.Errors)
Console.WriteLine(" Line {0}. {1}\n", ce.Line, ce.ErrorText);
}
}
catch (Exception e)
{
Console.WriteLine("ERROR while trying to build generated code: {0}\n", e.Message);
}
return res;
}
/// <summary>
/// Rucursive method to add references assemblies to compiler parameters.
/// </summary>
/// <param name="cp">Compiler parameters.</param>
/// <param name="type">Type that belongs to the assembly to be added.</param>
static private void AddReferencedAssm(CompilerParameters cp, Type type)
{
Assembly assm = Assembly.GetAssembly(type);
if (null != assm)
foreach (AssemblyName refAssmName in assm.GetReferencedAssemblies())
if (refAssmName.Name.ToLower() != "mscorlib")
{
cp.ReferencedAssemblies.Add(assm.Location.Replace(assm.GetName().Name, refAssmName.Name));
Type baseType = assm.GetType(type.FullName).BaseType;
if (null != baseType)
{
Assembly assmBase = Assembly.GetAssembly(baseType);
if (assm != assmBase)
AddReferencedAssm(cp, baseType);
}
}
}
/// <summary>
/// Filter the true type methods to be exposed with WCF service.
/// </summary>
/// <param name="method">Method info.</param>
/// <returns></returns>
static private bool IsExposed(MethodInfo method)
{
return method.IsPublic &&
method.Name != "Equals" &&
method.Name != "GetType" &&
method.Name != "GetHashCode" &&
method.Name != "ToString" &&
method.Name != "CreateObjRef" &&
method.Name != "GetLifetimeService" &&
method.Name != "InitializeLifetimeService";
}
}
}