我又来讲CCF了,其实我也很难过,讲大家都没有碰到过的东西,不过不管怎么样,我觉得内容的90%都对你有用。
自从上篇的初尝试之后,大家一定已经有一个共识,创建一个WCF服务是一件非常容易而且有乐趣的事情...(99%的基于配置的东西都会给你这种错觉...)
上次的代码的确是可行的,但是有一些问题它无法克服:
1. 无法满足一个实际映射在多个虚拟目录的配置。(我不知道这样说大家理解没有,因为在绝大多数生产环境ws是附加在IIS上的,而配置中可能有冲突而且无法合并的地方)
2. dll载入简单却不是最快的。(确是最懒的一种方式...)
以下的代码加上原先的注释掉的部分可以形成对比参照。
这里可以很清晰的看到.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行代码彻底的击溃了。
我有了见到真人带丝袜提麻袋,抗拖把的冲动,我还是那句话,这个看起来像印度人写的...
为了保护公司机密只能复制少许给大家雅俗共赏
这是2444到2472的代码片断,我不太喜欢读超过1k行的代码,看到这种2-3k行的代码一般都跳到最后,正好今天比较无聊,就开始看看the others是怎么写代码的和写了什么有意思的功能。
我们开始详细的分析这几行代码,首先这是一个私有方法,方法名字很好,Getxxxxx,大小写非常符合预期,然后我们继续看这个方法上面,写着华丽的注释,对我这个吐代码不加注释真是一个刺激。然后观察了一下,这里被引用了3次,在我的心里性能早已经被抛弃掉了。然后我拖上去验证我的预期:这几k行的代码是需要慢慢的磨的。
磨了一些我羞愧了,这些代码无论如何都不能贴上来,我只有冲动,手里不能捏东西。好吧就当篇末的笑话。
有多少人有同样的痛苦?寻求共勉。
自从上篇的初尝试之后,大家一定已经有一个共识,创建一个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 }
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
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行的代码是需要慢慢的磨的。
磨了一些我羞愧了,这些代码无论如何都不能贴上来,我只有冲动,手里不能捏东西。好吧就当篇末的笑话。
有多少人有同样的痛苦?寻求共勉。