CCF webservice续章

Posted on 2008-05-07 17:30  A.Z  阅读(1583)  评论(6编辑  收藏  举报
我又来讲CCF了,其实我也很难过,讲大家都没有碰到过的东西,不过不管怎么样,我觉得内容的90%都对你有用。
自从上篇的初尝试之后,大家一定已经有一个共识,创建一个WCF服务是一件非常容易而且有乐趣的事情...(99%的基于配置的东西都会给你这种错觉...
上次的代码的确是可行的,但是有一些问题它无法克服:
1. 无法满足一个实际映射在多个虚拟目录的配置。(我不知道这样说大家理解没有,因为在绝大多数生产环境ws是附加在IIS上的,而配置中可能有冲突而且无法合并的地方)
2. dll载入简单却不是最快的。(确是最懒的一种方式...)

以下的代码加上原先的注释掉的部分可以形成对比参照。
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Configuration;
  4 using System.Diagnostics;
  5 using System.IO;
  6 using System.Linq;
  7 using System.Reflection;
  8 using System.ServiceModel;
  9 using System.ServiceModel.Configuration;
 10 using System.Threading;
 11 
 12 namespace Eclipse.CCF
 13 {
 14     class Program
 15     {
 16         [LoaderOptimization(LoaderOptimization.MultiDomain)]
 17         static void Main(string[] args)
 18         {
 19             //LoadAssembliesInExecutingDirtory();
 20             var resetEvents = new List<AutoResetEvent>();
 21             try
 22             {
 23                 foreach (string name in ConfigurationManager.AppSettings.Keys)
 24                 {
 25                     Console.WriteLine("Loading {0}", name);
 26                     var resetEvent = new AutoResetEvent(false);
 27                     resetEvents.Add(resetEvent);
 28                     new Thread(obj =>
 29                                    {
 30                                        var objName = (obj as object[])[0].ToString();
 31                                        var domainSetup = new AppDomainSetup
 32                                                              {
 33                                                                  ConfigurationFile = objName + ".config",
 34                                                                  ApplicationBase =
 35                                                                      AppDomain.CurrentDomain.SetupInformation.
 36                                                                      ApplicationBase,
 37                                                                  ApplicationName = objName
 38                                                              };
 39                                        var domain = AppDomain.CreateDomain(objName, AppDomain.CurrentDomain.Evidence,
 40                                                                            domainSetup);
 41                                        domain.SetData("resetEvent", (obj as object[])[1]);
 42                                        domain.DoCallBack(CreateHosts);
 43                                    }).Start(new object[] { name, resetEvent });
 44                 }
 45                 WaitHandle.WaitAll(resetEvents.ToArray());
 46             }
 47             finally
 48             {
 49                 foreach (var resetEvent in resetEvents)
 50                 {
 51                     resetEvent.Close();
 52                 }
 53                 resetEvents.Clear();
 54             }
 55             Console.ForegroundColor = ConsoleColor.Green;
 56             Console.WriteLine("Host Started");
 57             Console.ForegroundColor = ConsoleColor.DarkGray;
 58             Console.WriteLine("Press any key to exit");
 59             Console.ResetColor();
 60             Console.ReadKey();
 61         }
 62 
 63         static void CreateHosts()
 64         {
 65             var hostTypeList = from ServiceEndpointElement endpoint
 66                                    in (from ServiceElement service in
 67                                         ((ServicesSection)
 68                                          ConfigurationManager.GetSection(
 69                                              "system.serviceModel/services")).Services
 70                                     let Endpoints = service.Endpoints
 71                                     select (from ServiceEndpointElement endpoint in Endpoints
 72                                             where
 73                                                 endpoint.Address.OriginalString.StartsWith("http")
 74                                             select endpoint).First())
 75                                select
 76                                    new
 77                                        {
 78                                            Type = FindTypeInRefAssemblies(endpoint.Contract),
 79                                            Uri = endpoint.Address
 80                                        };
 81             foreach (var hostType in hostTypeList)
 82             {
 83                 var serviceHost = new ServiceHost(hostType.Type, hostType.Uri);
 84                 serviceHost.Open();
 85             }
 86             (AppDomain.CurrentDomain.GetData("resetEvent"as AutoResetEvent).Set();
 87             AppDomain.CurrentDomain.SetData("resetEvent"null);
 88         }
 89 
 90 
 91         //static void LoadAssembliesInExecutingDirtory()
 92         //{
 93         //    var assemblies = AppDomain.CurrentDomain.GetAssemblies();
 94         //    foreach (var path in Directory.GetFiles(AppDomain.CurrentDomain.SetupInformation.ApplicationBase))
 95         //    {
 96         //        if (Path.GetExtension(path).Equals(".dll", StringComparison.CurrentCultureIgnoreCase))
 97         //        {
 98         //            if (!assemblies.Any(ass => ass.GetName().Name == Path.GetFileNameWithoutExtension(path)))
 99         //                try
100         //                {
101         //                    Assembly.LoadFile(path);
102         //                }
103         //                catch { Debug.Fail("loaded an unknown dll"); }
104         //        }
105         //    }
106         //}
107 
108         static Type FindTypeInRefAssemblies(string typeName)
109         {
110             Type type = null;
111             foreach (var assemblyName in ConfigurationManager.AppSettings["BindAssemblies"].Split(';'))
112             {
113                 type = Type.GetType(Assembly.CreateQualifiedName(assemblyName, typeName));
114                 if (type != null)
115                     break;
116             }
117             Debug.Assert(null != type);
118             //Debug.Assert(AppDomain.CurrentDomain.GetAssemblies().Any(ass =>
119             //                                                {
120             //                                                    if (!ass.FullName.StartsWith("Microsoft.Ccf"))
121             //                                                        return false;
122             //                                                    type = ass.GetType(typeName);
123             //                                                    return type != null;
124             //                                                })); ;
125             return type;
126         }
127     }
128 }

这里可以很清晰的看到.net配置与程序集载入边界抽象对应关系。(看官自行深刻理解)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="AdminConsoleWS"/>
    <add key="AgentCredentialsWS"/>
    <add key="AgentLoginManagerWS"/>
    <add key="ContactCenterAifWS"/>
    <add key="ContactCenterWS"/>
    <add key="GatewayWS"/>
    <add key="MultichannelServerWS"/>
  </appSettings>
</configuration>

app.config变成这样子了...
原先config文件基本保持不变,少许组合,放置在项目内一并复制到执行目录。
这样就完美的把服务运行在了xp上了。


小小的通告:在经过对azman的配置和对CCF几个见不得人的dll少许修改以及若干应用程序配置文件的替换和修改了CCF数据库部署script之后(sqlexpress一样很好很强大),已经可以非常逼真的在本机上运行adminconsole和agentdesktop了。这无疑是迈向火星的第一步。接着等外国人感兴趣时再写个guide给他们,也算是这几天辛勤劳动的见证...



最后不得不说这几天非常愉悦的心情(就上面这点东西做出来是蛮开心的)被2k行代码彻底的击溃了。
我有了见到真人带丝袜提麻袋,抗拖把的冲动,我还是那句话,这个看起来像印度人写的...
为了保护公司机密只能复制少许给大家雅俗共赏

 1 
 2        /// <summary>
 3        /// THis returns the list of Search Columns for MSCRM. 
 4        /// </summary>
 5        /// <returns>string array of Columns to return in the search request</returns>
 6        private string[] GetSearchCols()
 7        {
 8            return new string[]  
 9                 {
10                     "fullname"
11                     "lastname"
12                     "firstname"
13                     "contactid",
14                     "telephone1",
15                     "telephone2",
16                     "mobilephone",
17                     "address1_line1" , 
18                     "address1_line2" , 
19                     "address1_line3"  ,
20                     "address1_name",
21                     "address1_postalcode",
22                     "address1_city",
23                     "address1_stateorprovince",
24                     "address1_country" , 
25                     "emailaddress1" , 
26                     "parentcustomerid"
27                 };
28        }
29 
30        #endregion

这是2444到2472的代码片断,我不太喜欢读超过1k行的代码,看到这种2-3k行的代码一般都跳到最后,正好今天比较无聊,就开始看看the others是怎么写代码的和写了什么有意思的功能。
我们开始详细的分析这几行代码,首先这是一个私有方法,方法名字很好,Getxxxxx,大小写非常符合预期,然后我们继续看这个方法上面,写着华丽的注释,对我这个吐代码不加注释真是一个刺激。然后观察了一下,这里被引用了3次,在我的心里性能早已经被抛弃掉了。然后我拖上去验证我的预期:这几k行的代码是需要慢慢的磨的。
磨了一些我羞愧了,这些代码无论如何都不能贴上来,我只有冲动,手里不能捏东西。好吧就当篇末的笑话。

有多少人有同样的痛苦?寻求共勉。