我希望能够编写优美的代码。4 @7 {) a5 V; E. {2 a
优美的代码就像一篇散文,易懂易读,而且看起来很漂亮。在《代码之美》一书中,收录了Ruby之父松本行宏的一篇文章,名为《把代码当作文章》,大约表达了同样的含义。Thoughtworks的一位工程师在《软件开发沉思录》一书中提出,每个类的方法最好不要超过5行。最初让我感觉很惊诧,继而觉得不可能。虽然这位工程师言之凿凿,提到在自己参与的项目中,所有代码都完全遵循了这一规范,我仍然表示怀疑。最近,阅读了Robert C. Martin的著作《代码整洁之道》(英文版名为Clean Code),看到Uncle Bob演示的代码,真是漂亮极了。仔细一看,这些好的代码在每个方法中大多数都没有超过5行。诀窍在哪里?那就是重构手法中最常用的Extract Method。进一步讲,如果我们能够为每个类与方法以及变量定义出好的名字,代码确实可以变成一篇散文。当然,是英文散文。" l @% {! Y( P7 \0 x( _+ D
今天,我在重温.NET的序列化时,在MSDN上找到一篇演示Xml序列化的示范代码。或许是因为示范代码的缘故,这一段代码写得极其地不优雅,甚至显得有些丑陋:public class Test {% \* W I; a+ X: D0 y) @
public static void Main() {/ k8 N$ \3 \- P: | V. P# _
// Read and write purchase orders. / T( y% t, f. f1 S0 L* O+ @ Test t = new Test();9 u) g8 D. D. ^
t.CreatePO("po.xml"); 8 U& }7 a& ], D% h# s9 y& \# e t.ReadPO("po.xml");: w$ X. f- Y* x2 o! a% P
}6 Y; n+ f/ Z7 x; C( }
* o. I2 O2 ~% J6 g/ F
private void CreatePO(string filename) {; |( P- q' j. s
// Create an instance of the XmlSerializer class;9 B$ h0 q$ H! n+ H4 [5 K( y" [, m. a
// specify the type of object to serialize. * U" M& z. v) v0 f XmlSerializer serializer = ; R) P8 f* _9 }) o new XmlSerializer(typeof(PurchaseOrder));3 b Y& u( J1 t: j- c
TextWriter writer = new StreamWriter(filename); . |! d' O. U4 S* [9 s PurchaseOrder po = new PurchaseOrder(); + o( D% S% H8 r 5 {- J& V/ U, P3 ]5 ? // Create an address to ship and bill to.; w5 |; ]$ T! ]
Address billAddress = new Address();6 T* E- i0 E6 w0 {- p4 d
billAddress.Name = "Teresa Atkinson";+ F; N8 R3 s* N( p% R$ Q+ X; X
billAddress.Line1 = "1 Main St.";$ G4 ]& |, t0 u: Y% o2 w# H
billAddress.City = "AnyTown";* ? G& M& T5 F, `! `
billAddress.State = "WA"; - v! ^# R5 D# n8 @& h: e& f7 ] billAddress.Zip = "00000"; - c {; c% J g; v2 P // Set ShipTo and BillTo to the same addressee./ N+ A: w" J* v% {' F N" @0 Y! D
po.ShipTo = billAddress;& z* s. d" r& Y/ K
po.OrderDate = System.DateTime.Now.ToLongDateString();1 O- Z2 B6 q c
& i7 h+ P$ a- a4 b
// Create an OrderedItem object.* e S3 Q6 x$ l
OrderedItem i1 = new OrderedItem(); 8 W) R, m% C) Y0 ]: n i1.ItemName = "Widget S"; + K/ t5 t% f, q0 L. X i1.Description = "Small widget";9 s) u2 O- d& n7 ?: ?. j7 i1 J# h
i1.UnitPrice = (decimal)5.23;5 v. F+ a* L# _. j! C# Q1 m# x: D7 G5 N
i1.Quantity = 3;( L, d( f+ V9 j9 C
i1.Calculate(); . H8 T& @+ g* _ N & E* C2 ~2 }. F5 ~+ L. v4 ?) T9 D6 R // Insert the item into the array.1 D `0 s- o9 t# G
OrderedItem[] items = { i1 };8 t2 @6 T2 g1 j Y% r7 |
po.OrderedItems = items; " [4 g: q* k8 ^; F9 D k$ A; K // Calculate the total cost. : f) q2 O( r3 D+ g% [: e decimal subTotal = new decimal(); " d( b# ^' a, [" y* w foreach (OrderedItem oi in items) {! }+ x! ]/ k4 d8 X% n" L
subTotal += oi.LineTotal; ( ^( B/ q' _# j5 x% b }7 _ {8 h k$ X @: h
po.SubTotal = subTotal;. J- U' ?% U+ e- R8 ]' ]
po.ShipCost = (decimal)12.51;7 \, p% _1 o/ l: [0 B, {& d+ b: L
po.TotalCost = po.SubTotal + po.ShipCost;* K) V9 c/ |0 E# [5 H: A, D, w0 n
// Serialize the purchase order, and close the TextWriter. , [" ~$ r8 v( O/ d7 j serializer.Serialize(writer, po); 6 ?' D7 i7 m' {" [5 n+ o) l writer.Close();$ ]: d. b+ s. K c% X+ S
}, ]% M$ Y6 d3 c7 b; b8 p6 j: S% x
1 J2 |1 e9 R% B' i; C+ ^ protected void ReadPO(string filename) { 1 F! `# t9 W" k: { // Create an instance of the XmlSerializer class;% H+ O; Z }& N t0 z9 }
// specify the type of object to be deserialized.$ o- O; x6 p5 d: v
XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder)); 6 H. x" n, I& l s' [ /* If the XML document has been altered with unknown 3 A- ]/ n: J3 e: Z7 O nodes or attributes, handle them with the 2 a$ I- P3 E, T, t) r
UnknownNode and UnknownAttribute events.*/ * n! e/ \3 F4 u: v+ x serializer.UnknownNode += new 1 t% v# b% k( W1 ?% ` XmlNodeEventHandler(serializer_UnknownNode);2 z' @) a V1 S) z
serializer.UnknownAttribute += new7 Z) e5 E% Z. d5 n
XmlAttributeEventHandler(serializer_UnknownAttribute); 3 C. T8 N! h4 W* v 4 M7 Q n8 a; j" w8 `9 o# _8 Y // A FileStream is needed to read the XML document. 4 i/ m* O& S$ }3 s( z# E; {7 E FileStream fs = new FileStream(filename, FileMode.Open); J- n9 a. v: V- z& u // Declare an object variable of the type to be deserialized. 6 g s3 H& V- A' p* U8 O5 \ PurchaseOrder po;. {) K7 J n" l
/* Use the Deserialize method to restore the object's state with 8 w2 S, d' _" L7 j* V data from the XML document. */ $ P7 V$ h b6 X po = (PurchaseOrder)serializer.Deserialize(fs); # T- Q+ s2 D. @' X0 E // Read the order date. % _: i% K3 h! B7 N& [8 w Console.WriteLine("OrderDate: " + po.OrderDate); 3 l' Z$ h0 ]8 \6 o; e2 ? % ~5 S: |5 x& \0 y7 d4 p; w1 n // Read the shipping address. # {+ J7 v2 v5 ~4 p: B4 @ Address shipTo = po.ShipTo; - A* i1 t! \( g* B ReadAddress(shipTo, "Ship To:");4 K6 }& E+ Z( G' {0 A
// Read the list of ordered items. 3 H6 s2 N5 h! r& a OrderedItem[] items = po.OrderedItems; 9 o6 f, |* t9 e' z5 e' S9 a3 C Console.WriteLine("Items to be shipped:");9 v3 F/ V6 }0 C/ Z& Q
foreach (OrderedItem oi in items) { * a6 g6 S6 ~! v8 l9 w6 m* Z Console.WriteLine("\t" + / Z) |& c0 q$ O7 N6 U, u/ I oi.ItemName + "\t" +* g4 t3 Q7 K" K5 l9 x
oi.Description + "\t" + * G- P, m4 G' v# h, ^% D8 W oi.UnitPrice + "\t" + 9 g- l1 h4 X# N3 v9 `/ {; w oi.Quantity + "\t" +; a: G2 D$ ^# J$ V* A5 @; v
oi.LineTotal);9 }. K2 P H% O2 T
}. K, Y9 ?7 F0 j6 r6 p" r5 b
// Read the subtotal, shipping cost, and total cost. ' D2 v4 \% q7 `4 M Console.WriteLine("\t\t\t\t\t Subtotal\t" + po.SubTotal); 2 d, o* k) F( b) s# V& \# ~ Console.WriteLine("\t\t\t\t\t Shipping\t" + po.ShipCost);9 B7 b a* g. Q& s( t/ E) E( ~
Console.WriteLine("\t\t\t\t\t Total\t\t" + po.TotalCost); 2 J# K! n1 o- f( t2 h) u! {' Q+ t }3 H% x2 Z* A# E1 o& G5 R- Q0 o
1 s7 Z$ L) F$ z' l, Y protected void ReadAddress(Address a, string label) { $ q/ H2 L' S9 m0 i // Read the fields of the Address object.& A/ O! J0 C* X3 q% ]; s2 M; V
Console.WriteLine(label);9 f$ R0 ]: L' @# z
Console.WriteLine("\t" + a.Name);7 ^$ k) I- x3 [* K6 o
Console.WriteLine("\t" + a.Line1); ) O! n: Y8 b1 D& D6 K) e Console.WriteLine("\t" + a.City);' o3 |; B7 |8 h# I8 T
Console.WriteLine("\t" + a.State);. G9 p2 C' b# g4 F4 I: n( k
Console.WriteLine("\t" + a.Zip); & Z1 |; ~: x/ e3 t( U Console.WriteLine(); ' o" C; e$ I3 F& r% B, z# o }, B7 N) m- o; P
3 I @9 i) m) } ' A6 U+ \* u) H: p9 a) I7 E8 `9 [5 Q3 G6 q2 O& L; D
看看CreatePO()和ReadPO(),多么地冗长。虽然这个实现极为简单,但对于代码的阅读者而言,想要一下子抓住该方法的中心思想,仍然比较困难。此外,方法中的注释也显得多余,因为,代码本身就可以给予很好的说明。 - r$ M1 ^) j- B E& t. H* S下面,是我对这段代码的重构,大家可以对比对比,是否更加容易阅读呢? public static class PurchaseOrderHandler { 7 H, g, ~# f5 O/ R- q0 ^, ?- A public static void CreatePurchaseOrder(string filename) { , Y. M$ S( A! L# ?+ A PurchaseOrder po = BuildPurchaseOrder();2 X4 L2 p- @- c/ v/ N- u- j
XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder)); . L! l/ U+ S3 b0 B) Z using (var writer = new StreamWriter(filename)) {0 o) A! l" Q+ c1 J4 ^
serializer.Serialize(writer, po);. @9 Y. | U6 n" x& U/ C
} 4 [0 s% N# z: g3 P) \ }+ a/ j$ x0 V( z8 Y! |) h& Q- R5 m
+ c7 ^8 Q6 I9 u private static PurchaseOrder BuildPurchaseOrder() { # j% H3 B, Q& a% U2 m Address address = CreateAddress();* m, H) U* z: U5 Z* b5 T9 X# f) a6 m
OrderedItem i1 = CreateOrderedItem(); W+ l/ n- z; ~0 T- V* K9 L- v OrderedItem[] items = { i1 };5 F- S2 O" c, S* z
, r% ^6 j) r" n( \7 _
PurchaseOrder po = new PurchaseOrder(); 3 \) `: v( ^ W7 q7 \' c! H, N po.ShipTo = address;9 b) ]2 z9 P% n' r" C* _! Q
po.OrderDate = System.DateTime.Now.ToLongDateString();5 V- G8 Q5 L% R& D: t) Z: R/ j
po.OrderedItems = items; % ~2 X# ~/ T2 U9 l* S po.SubTotal = CalculateSubTotal(items);: J- }5 @1 C. V5 S/ m/ M
po.ShipCost = (decimal)12.51;& q- s, w, y1 g4 a7 W) U
po.TotalCost = po.SubTotal + po.ShipCost; 3 ?7 o/ D5 Y+ b, [3 I) k' [ * Y0 c( c2 p C5 X8 r return po;/ q8 k& `7 ]* G
}& ^; y9 N& H! K
8 V, U; R! r9 o% d7 v) O private static decimal CalculateSubTotal(OrderedItem[] items) { ; c9 ]0 J5 |( S' H5 d; n; E decimal subTotal = new decimal();0 k/ U. ^7 M/ z0 O# y% N( N
foreach (OrderedItem oi in items) {2 p, [. q7 v& J5 b
subTotal += oi.LineTotal; 7 t1 W4 L, G- E) x }% \, F9 A0 W: j; A# Q
$ }7 N5 F. n1 A# K8 ~4 `# j return subTotal; & d5 M u; f8 b4 U X } # a; s! V$ l- k! g$ u' Z/ l5 ]% w& U9 h0 e9 d5 j9 H
9 k/ J7 v" A: e K# H
private static OrderedItem CreateOrderedItem() {5 a# U) \/ F- W1 g7 v
OrderedItem i1 = new OrderedItem(); 3 Q- n. f, ?4 }% [6 y% v i1.ItemName = "Widget S"; 3 e# Y7 u# j1 z+ P i1.Description = "Small widget";* o1 Q4 w% A; l% c$ L4 D2 D6 M, z5 H$ j' G
i1.UnitPrice = (decimal)5.23;9 P/ ]# C. P4 @
i1.Quantity = 3; 3 j3 N+ d4 r V' L: H7 w3 O i1.Calculate(); 5 H$ _# Y3 v: C4 d return i1;/ K8 `! I/ z+ k5 m' E
}2 @$ H8 k6 j6 \2 c( b8 @) e
, F2 M% g# s: R I0 n. ~# J private static Address CreateAddress() {' P( y7 O( A+ j" P" Y" ?
Address billAddress = new Address(); ' I$ B$ f1 L* ]: X# j billAddress.Name = "Bruce Zhang"; 6 T% w* Z8 X! O billAddress.Line1 = "1 Main St."; , r; j0 i: {/ X* F# v) y billAddress.City = "Chong Qing"; 5 ~$ v5 y. C: d& @: G billAddress.State = "Chong Qing";1 S- X2 j6 D' J
billAddress.Zip = "400000"; ! @: z# \* u& Y7 M5 l0 h8 l* {/ \2 ?$ N
return billAddress; - t) B" j( u! _! k% M7 s; F } % o# \; R0 X4 o! L2 v & }0 {& _. o6 E! W% E! Q x public static void ReadPurchaseOrder(string filename) {* v8 o. i7 V6 n w) s0 N4 J5 y v
XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder)); % C2 N& o+ a3 c8 }& F * P3 L6 H9 b% m6 {& I! l* y serializer.UnknownNode += new XmlNodeEventHandler(serializer_UnknownNode); ( t* _: h% E! _4 {7 { serializer.UnknownAttribute += new XmlAttributeEventHandler(serializer_UnknownAttribute);9 w) m( H9 X9 r: m
# `0 u, j3 w. W
FileStream fs = new FileStream(filename, FileMode.Open); w) W! q' |0 ?/ W7 C ; P. @: V* D! L- a% [ PurchaseOrder po;# D. U( @$ f$ K3 Q
po = (PurchaseOrder)serializer.Deserialize(fs); 0 A. W/ z- E* p' p, r5 l PurchaseOrderPrinter.PrintPurchaseOrder(po);" s0 W6 J% o- M* e2 ?4 [
}% c( ?( Y" p" \' K1 s4 p$ Z3 [
, C \7 e. s8 c4 k
* C4 B( N! m- G$ H' r0 k
private static void serializer_UnknownNode ' N' g( e% r7 D) s) S6 y0 Y (object sender, XmlNodeEventArgs e) { " _2 Q' C) Q7 p4 c! [ Console.WriteLine("Unknown Node:" + e.Name + "\t" + e.Text);, n1 F4 _+ Y- d1 f8 |" ^- S9 y
} / [3 {5 t! `( E% x 8 I5 C$ ?+ i+ A private static void serializer_UnknownAttribute3 {: v' M5 Z) b% f1 W
(object sender, XmlAttributeEventArgs e) { 6 Z9 B9 [+ G4 \) F" Z System.Xml.XmlAttribute attr = e.Attr;% }' g/ g6 N1 S# h s
Console.WriteLine("Unknown attribute " + ; q$ w% C$ v; z attr.Name + "='" + attr.Value + "'");7 R) {; Y! Q. d/ _% v
} 3 B: _) x) F3 v- {6 y( b6 Z7 F, n4 c# s7 |9 Z
private static class PurchaseOrderPrinter {! o9 r R: [! e% l5 M7 r% {
public static void PrintPurchaseOrder(PurchaseOrder po) { 9 b+ L" _4 o' u- s8 u PrintOrderDate(po); % U3 c2 y: \! q$ o9 m$ Z) k. q PrintAddress(po.ShipTo); / N* u0 U2 r0 F0 Y$ r% U PrintOrderedItem(po.OrderedItems);9 w6 |0 `$ e( ~% b# q9 \# z
PrintOrderCost(po); $ q, y8 Q& V5 h0 }7 v( y }; w' p- e, N8 L, ? a# ~8 k
8 F% j0 k: j1 H- G7 I7 E private static void PrintOrderCost(PurchaseOrder po) {- S6 Y0 \/ p/ I- o9 f% [7 Z+ A$ L' a- p
Console.WriteLine("\t\t\t\t\t Subtotal\t" + po.SubTotal); 3 h5 I, [0 I. j+ s" T0 M Console.WriteLine("\t\t\t\t\t Shipping\t" + po.ShipCost); ) _ q8 P. l( d Z; W+ v. M" j; _ Console.WriteLine("\t\t\t\t\t Total\t\t" + po.TotalCost);- ]) x, \3 p" @1 m* c* ^/ H4 @& i2 q
}! ?9 z- W6 k P
! @) d$ D' A# l9 n private static void PrintOrderDate(PurchaseOrder po) { - t. |5 u- ? q* c# g7 ~; a Console.WriteLine("OrderDate: " + po.OrderDate);6 Y3 q+ l5 |2 c9 ?4 y+ @3 u
} : }- @6 H* L) W+ }6 S. y - s3 q* a% \9 Y, p: J' |( E2 [( ^5 b private static void PrintOrderedItem(OrderedItem[] items) { 7 |) S9 \3 L/ N8 |, F- | Console.WriteLine("Items to be shipped:");) x" m/ s/ [2 `5 p3 L+ \! f0 _
foreach (OrderedItem oi in items) { 8 E1 |) Q" a* L c5 \ Console.WriteLine("\t" + / v9 f8 A+ Y7 y! ~5 ^9 X oi.ItemName + "\t" + ) h9 B! P7 }7 c2 @ oi.Description + "\t" + 7 a0 \3 m6 Y0 D3 j) M oi.UnitPrice + "\t" + & D9 e/ C" P) ^6 F, `+ ~0 i' p oi.Quantity + "\t" + : @% W9 C, R" x6 S: ?# i5 V oi.LineTotal); : t# Z3 m& S+ |4 V' e p1 t4 p }0 n9 I2 K/ s1 I6 o
}( ^+ q- f/ ^6 ]! H$ }9 F
' ^! v* ]8 L1 a' u private static void PrintAddress(Address a) { 4 d0 h u! _# L: A% T // Read the fields of the Address object. 0 n8 J3 x) Q/ w3 e, |% {! A Console.WriteLine("Ship To:");9 J( N4 L# P' V0 E% |" S E; `
Console.WriteLine("\t" + a.Name);$ B& a, N7 b! M+ c+ t/ F% w! c' p
Console.WriteLine("\t" + a.Line1);0 f' q$ P) f$ d% K) O3 o) P: J) Y
Console.WriteLine("\t" + a.City);$ A. p7 N+ ?+ o9 W9 `1 a
Console.WriteLine("\t" + a.State); 0 f1 ?0 ?% B) H! R, P Console.WriteLine("\t" + a.Zip);0 i" j T8 J& o6 L8 _6 e: h! k6 ?
Console.WriteLine(); + ?* z- O1 R6 s" g5 G+ \ } 3 Q" _( z' D0 \. t3 x* p8 D } ; {" G+ B* O# I2 l( B8 v9 L }$ w0 Q8 g5 v( D# ?8 ?: Z# {6 N
8 l2 A2 k' e2 w! } . ]& Q; `6 ?+ z4 G" \* g: J4 ^7 F+ a4 J9 }4 A. Z: G o! b
阅读代码时,我们可以先关注最主要的方法,即CreatePurchaseOrder()和ReadPurchaseOrder()方法。如果并不希望了解过多构造PO对象的细节,通过阅读这样简短的方法,可以很容易地抓住这两个方法的实现,那就是通过构建一个PO对象,进行序列化,而在反序列化时,将获得的PO对象信息打印出来。: W! R6 d' o3 ^3 ~
其实,糟糕的代码不一定就是初学者的“专利”,让我们看看NHibernate中的一段代码:public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)- E: T: |6 j6 q
{ 9 z1 n* s6 R$ N+ b% U* U% h Init();5 E$ D, p" x4 F7 V4 Y
log.Info("building session factory");! r5 A2 \2 x. k2 b- W' w
. e; e* @: Z7 f% j+ ~
properties = new Dictionary<string, string>(cfg.Properties);8 X* b* R, Q, A# F4 b ~
interceptor = cfg.Interceptor;! D) [4 [: ^8 B2 a" N. w
this.settings = settings; . s) A- j% y6 l% y3 K# L" H sqlFunctionRegistry = new SQLFunctionRegistry(settings.Dialect, cfg.SqlFunctions); ) g+ P Y- l9 x7 w7 } eventListeners = listeners;) }. Q2 Z7 h; f6 N D3 P8 g
filters = new Dictionary<string, FilterDefinition>(cfg.FilterDefinitions);8 s. d1 d+ Y5 B8 \+ L; J
if (log.IsDebugEnabled)8 ~; o3 l; b) Y& k9 S$ }
{ ( b& d! a2 e L2 o, C; g% W0 O log.Debug("Session factory constructed with filter configurations : " + CollectionPrinter.ToString(filters));+ C# ?8 |) r7 P; h8 u( r
}1 U* H! _$ p3 Y) n: ~6 y
' M9 T' u+ W+ j! ]7 Z& [ if (log.IsDebugEnabled)- }6 ]) F. z7 {/ j5 I
{ / V1 T6 \7 D6 v% h1 V log.Debug("instantiating session factory with properties: " + CollectionPrinter.ToString(properties)); 7 o5 w$ G2 @6 Q; P. Y2 D, u3 E }! n) ^0 ]/ E- E) c* Q' W
6 @4 N7 L1 j! S
try , r+ \6 `& S! a9 b' m7 g {! V( Q3 v3 d- y
if (settings.IsKeywordsImportEnabled)8 y) z! f& l1 g. a' H' Z3 g
{1 R3 Z* ]. j, J$ Y. z
SchemaMetadataUpdater.Update(this); ; M( o% F- R! ]9 @ } % t/ l5 J2 f( x if (settings.IsAutoQuoteEnabled) 7 `1 A3 |8 |$ [6 q1 i { . B5 ~. M3 Q; c* ~0 j6 U" x SchemaMetadataUpdater.QuoteTableAndColumns(cfg);# F8 e8 _3 T- I$ `5 J6 ~
}$ q" R' g$ ?- O
}$ D F1 b' H6 l
catch (NotSupportedException) ( o2 N& H7 G* [' R$ n' f3 d {* ~$ {" p9 `! V% z3 P* `
// Ignore if the Dialect does not provide DataBaseSchema : W* _ o. q$ Z6 |) T/ f" B } 2 ~, ]4 |/ k- L' @0 f+ R 7 n0 u4 B7 H$ X# @: k #region Caches 1 V/ R# K' B1 P0 X8 V% [1 O settings.CacheProvider.Start(properties);0 ^/ Z u+ s8 ?- N4 K! `$ }6 T
#endregion . R2 |' }* |+ c % S% h8 T$ f, E& W7 H9 a #region Generators # P R. P+ ]& y3 Q8 X; s identifierGenerators = new Dictionary<string, IIdentifierGenerator>(); & j9 T! \+ f. I+ a' d! O& l" L foreach (PersistentClass model in cfg.ClassMappings)7 G) o3 b# c( X
{ 8 D/ _. U6 r, w- k @+ b, c) x7 v if (!model.IsInherited) / I8 w) [" {& x9 N6 q) x: q {( {2 B) t) [* ]" x
IIdentifierGenerator generator =7 c$ s D6 l9 q
model.Identifier.CreateIdentifierGenerator(settings.Dialect, settings.DefaultCatalogName,) K* C$ b& b# ~7 J% [' i6 f
settings.DefaultSchemaName, (RootClass) model);- n& T! U) b0 T- u- Q% J; s
# w; G1 l! r% B5 V
identifierGenerators[model.EntityName] = generator;( k* W0 `+ X- t( y4 T- E/ O
}% g$ o# D! l/ C& R6 x* |. T1 r
} 0 }% N3 D9 v# [$ K, P% r1 r #endregion + L$ |2 o' N0 O5 D, y8 `) `( Z! l6 j+ }5 S8 g$ c7 p2 f( L8 n
#region Persisters* N2 g7 ~, n* H" O* \
: p" ^4 Q, U+ }1 e. s' ?5 F
Dictionary<string, ICacheConcurrencyStrategy> caches = new Dictionary<string, ICacheConcurrencyStrategy>();# n% ?* j( ] Z/ B# ^
entityPersisters = new Dictionary<string, IEntityPersister>(); 0 U3 V4 y9 h! v+ b1 { implementorToEntityName = new Dictionary<System.Type, string>();% f/ R' i# a% A; d
) _3 Z, }0 ~7 L, t Dictionary<string, IClassMetadata> classMeta = new Dictionary<string, IClassMetadata>();% G% `! {. ~) B; i
+ Z) y% H/ ~4 l6 a5 Y1 \/ u1 p1 W
foreach (PersistentClass model in cfg.ClassMappings) 9 N( y9 [- ~1 `1 _9 m {- ^+ z/ w3 j! U) y
model.PrepareTemporaryTables(mapping, settings.Dialect);9 i/ [% R; e6 ?2 s& D. s
string cacheRegion = model.RootClazz.CacheRegionName; ) K5 t0 s+ y* }5 [ ICacheConcurrencyStrategy cache; * Y5 Q* W* m/ m, o( \ if (!caches.TryGetValue(cacheRegion, out cache))% o0 q! u) J8 x. r. ^9 C
{! V1 B8 D4 m, d. m v9 L {9 s- |
cache = 5 W/ J6 \/ |( t/ N CacheFactory.CreateCache(model.CacheConcurrencyStrategy, cacheRegion, model.IsMutable, settings, properties);7 h' v( _6 L/ I3 W: }
if (cache != null) 4 ^- [# V8 ]0 L/ W2 Q* V9 A/ J# k { 3 M) o1 j& ]% i1 i" h# C& B caches.Add(cacheRegion, cache);( u' i) \9 }& G! m
allCacheRegions.Add(cache.RegionName, cache.Cache);+ q* [: c# d& \+ L
}7 z* W9 z* Y1 s7 h; ?( t* z) j& i. w
}% R6 X) X1 x2 M% B+ k. o# x/ C
IEntityPersister cp = PersisterFactory.CreateClassPersister(model, cache, this, mapping);* s) [0 z1 |. s8 G* V# ^
entityPersisters[model.EntityName] = cp; ( o, L6 z. b, m- J classMeta[model.EntityName] = cp.ClassMetadata; ; P/ ^+ U$ Z2 I: L2 l' n, g; X8 k+ s5 E$ K n$ d6 o
if (model.HasPocoRepresentation) ) Q7 O% `# k: K s! S {% n( C; i$ a) V( Y5 K* G
implementorToEntityName[model.MappedClass] = model.EntityName;& m# d, E* Q* h( K5 U
} + y: J# Q+ G, V3 d1 [% a7 M; x( v } ( v- Z" w6 B* z" B- T$ K classMetadata = new UnmodifiableDictionary<string, IClassMetadata>(classMeta); 5 X' h# |: w( X C/ ?% ]9 l % U- Z. J$ `+ ]) `6 N Dictionary<string, ISet<string>> tmpEntityToCollectionRoleMap = new Dictionary<string, ISet<string>>(); + \6 n' \1 `( c$ E collectionPersisters = new Dictionary<string, ICollectionPersister>();! p9 q7 a5 ?2 t* z% K; c6 p' T
foreach (Mapping.Collection model in cfg.CollectionMappings) : i6 ~3 z; ^1 k% K7 R8 Y7 K1 F { " d. R& J% e9 e5 y! c6 X5 F ICacheConcurrencyStrategy cache =2 r8 ~, y- M8 Y: Y% |& l
CacheFactory.CreateCache(model.CacheConcurrencyStrategy, model.CacheRegionName, model.Owner.IsMutable, settings, 8 A* A- \4 t/ P! C k+ Y properties);* t' l" |$ M& s1 A+ O
if (cache != null)0 s% E( U! ?5 B* [5 @. R4 ?1 o
{ " j# L! O1 Q% N- W( x. } allCacheRegions[cache.RegionName] = cache.Cache; 4 K7 [+ @% r4 t7 D8 W% u( o }& w6 |8 w- C! e- [- B2 p6 p
ICollectionPersister persister = PersisterFactory.CreateCollectionPersister(cfg, model, cache, this); ! K7 y8 e) j: @5 i# C) }1 d: Y collectionPersisters[model.Role] = persister; $ P# D$ _/ f+ d: [3 f" d: u IType indexType = persister.IndexType;" X9 W& R0 O# z0 {
if (indexType != null && indexType.IsAssociationType && !indexType.IsAnyType) 6 N8 K2 C/ U; J# \ {- m- w7 V: |7 c
string entityName = ((IAssociationType) indexType).GetAssociatedEntityName(this);+ A+ {, D- h p4 P! Q- h; q- E8 |
ISet<string> roles;& r4 R4 j3 s( `- ]+ o
if (!tmpEntityToCollectionRoleMap.TryGetValue(entityName, out roles)) $ R. u6 [- Y3 v$ a/ F, n2 ^8 Z { 4 S0 q O+ Y' `; h. x roles = new HashedSet<string>(); - B+ V) o" e* L3 `6 z, [0 | tmpEntityToCollectionRoleMap[entityName] = roles;4 i; v. }% w6 N& i) q
}9 I6 v* j/ \8 i' Y7 x
roles.Add(persister.Role);. T1 }9 c* F/ m
} 9 w7 O, o0 ~( N1 k. \' E Y/ _5 G( l IType elementType = persister.ElementType;% O- M u% j# v: `& w
if (elementType.IsAssociationType && !elementType.IsAnyType)" z4 I2 ` E4 B( y' K
{ 6 Q' I! L( U6 o Z' l0 j2 } string entityName = ((IAssociationType) elementType).GetAssociatedEntityName(this);6 e+ y* q! _3 F5 M8 g. ^- r3 t P
ISet<string> roles; + b8 v, B, F# K0 y if (!tmpEntityToCollectionRoleMap.TryGetValue(entityName, out roles)) ) `( v# v' r; V6 s" U% ] {! c- U8 Q" K5 H
roles = new HashedSet<string>(); : [# |: _5 G5 I4 r, ?& {/ m2 L tmpEntityToCollectionRoleMap[entityName] = roles;8 I" s0 t. L* e1 |5 {; z0 n; \ d
} # M/ F! p; J- n% R( z# X roles.Add(persister.Role); " {8 g2 x, ?! Z }8 V0 g! P, b& |2 i& `
} , v# r) X' }( |' ]- e2 w9 V Dictionary<string, ICollectionMetadata> tmpcollectionMetadata = new Dictionary<string, ICollectionMetadata>(collectionPersisters.Count); 3 B! b3 `9 Q2 G- E8 ]5 R foreach (KeyValuePair<string, ICollectionPersister> collectionPersister in collectionPersisters)# M7 r5 ^4 L, u5 ~4 V5 B' b
{: H: w+ V- _0 E* {+ c9 d& x# A/ Z
tmpcollectionMetadata.Add(collectionPersister.Key, collectionPersister.Value.CollectionMetadata); 9 o3 i& ]6 N, N6 |+ ^5 ? }& S7 t' ]9 }6 E8 b8 X
collectionMetadata = new UnmodifiableDictionary<string, ICollectionMetadata>(tmpcollectionMetadata);5 y/ u7 `& Y) x
collectionRolesByEntityParticipant = new UnmodifiableDictionary<string, ISet<string>>(tmpEntityToCollectionRoleMap);% `0 d. r# @- J2 I* R$ A; E
#endregion . d2 ?# e* ^1 l0 X5 |" ~8 f, Q, P& h, j, C
#region Named Queries 9 `& K+ s. J, ~, q4 p( H6 F namedQueries = new Dictionary<string, NamedQueryDefinition>(cfg.NamedQueries);, \! b' {) B/ W; `9 a
namedSqlQueries = new Dictionary<string, NamedSQLQueryDefinition>(cfg.NamedSQLQueries);6 ~8 [5 f. y+ H! ^1 f- ]) @, A; i! m
sqlResultSetMappings = new Dictionary<string, ResultSetMappingDefinition>(cfg.SqlResultSetMappings); , R8 }6 g' Q1 E1 y) a) B #endregion ' Z1 B$ B6 T$ |6 ~& ~8 _; r W7 x6 d. C1 w
imports = new Dictionary<string, string>(cfg.Imports);( g2 L) g5 H9 u! G1 g2 b. R! y- R
- s7 N3 I1 S3 ~/ ^) t #region after *all* persisters and named queries are registered% _% f& j/ r6 n) A
foreach (IEntityPersister persister in entityPersisters.Values)! | M1 {. _8 m3 Y5 b
{6 l5 l3 y8 P0 A
persister.PostInstantiate(); ( @% D( A/ T& N9 B } 4 x+ ?0 d( t4 p" K( s foreach (ICollectionPersister persister in collectionPersisters.Values)9 l, C5 o" r) X2 M$ e+ l6 s/ G
{9 v* [& `" K4 B" e5 N
persister.PostInstantiate(); 7 _5 G$ I4 t- A* | f }$ I9 w6 c2 y1 I8 G+ h6 s2 f6 K/ V
#endregion) ^& P! o9 ^2 K n
+ M( {. s- x" r
#region Serialization info1 `8 o4 A) Z+ g7 c- I( c7 |7 `
; Y% A$ ?3 U9 @8 b3 m; t: b
name = settings.SessionFactoryName;8 S- z6 N1 a1 B% g9 ^
try; R" W2 m5 B6 q# j, B
{ + w" F% ?8 g, c$ w+ F0 y uuid = (string) UuidGenerator.Generate(null, null); ' j8 _! l) A( |( ? }! a$ w8 }! T" R' i
catch (Exception)2 m4 p' i( o6 v7 `# C) n k5 H2 R
{ ( I4 Q; |. m: U9 g6 t& ^% u6 y throw new AssertionFailure("Could not generate UUID");$ X9 U% J) [; E& [' g9 J1 e) U( V' D4 R
}2 ~, L$ T* B U- L: L; V k
& K, H* I& v1 }- g0 ^2 ` SessionFactoryObjectFactory.AddInstance(uuid, name, this, properties); % n; U* {# ~( o% y6 d5 w" ]/ |% m. C
#endregion7 n- u% w$ J5 L# f8 f
/ W2 E1 X6 C4 B3 m. m8 F5 C
log.Debug("Instantiated session factory");" `/ a) Q8 D P3 o1 b
4 ^0 z' [9 y8 I5 S #region Schema management 6 }4 C q$ D3 O if (settings.IsAutoCreateSchema)3 j' s& d, q- O8 x" f: z" Q: S
{- T% c X+ |- P5 P
new SchemaExport(cfg).Create(false, true); % `3 Z8 T3 C4 ]$ s: u } * K# `) y; V" {8 N5 A 5 I9 D( P U/ B2 k if ( settings.IsAutoUpdateSchema ) E) j- k: o% w; K ^& @' i8 G4 J {, E1 W: G$ j! n$ s8 e5 Q
new SchemaUpdate(cfg).Execute(false, true); 9 _6 d" B2 k: p! J6 E/ Q E! Z, R } o C4 }8 x. e9 g' t( Z) W% j: j
if (settings.IsAutoValidateSchema) - J. W# f% R N { " U/ I9 t8 s- _5 g4 ? new SchemaValidator(cfg, settings).Validate(); 4 x0 ?0 |( [# r! e. \% j } {: F$ ]7 v# a7 ]
if (settings.IsAutoDropSchema)3 `1 w7 F+ B6 ]1 `0 d6 {
{ + d9 q3 m9 Y/ t# \) x9 D% c schemaExport = new SchemaExport(cfg);2 F( L3 q x1 e7 u/ x8 J7 D
} ! O) Y( d1 Q; D #endregion( e& Y, @, i2 i& d7 R
N/ E. F" a7 s( \9 P: U' x% v+ I
#region Obtaining TransactionManager 3 {3 j, |+ t: S4 e& B // not ported yet " Z \9 A1 S, `. P4 K + T) U! A3 X( n#endregion9 [& q; N; ~* K; [% k" I, S
5 R" b, z* p: j g
currentSessionContext = BuildCurrentSessionContext();; B" F7 v- a5 `: G0 _, A' S
2 @ Z/ C8 C: R' q9 | Q! ~
if (settings.IsQueryCacheEnabled) 0 F) t" I4 s" Z3 g& `7 W { 2 D% e$ N9 j" ^1 t8 b9 N updateTimestampsCache = new UpdateTimestampsCache(settings, properties);- @2 z/ J+ Z8 S% z
queryCache = settings.QueryCacheFactory.GetQueryCache(null, updateTimestampsCache, settings, properties); & u) z" v# L" q3 }: F- ~; ]( _ L queryCaches = new ThreadSafeDictionary<string, IQueryCache>(new Dictionary<string, IQueryCache>()); + S! U8 o2 |, m, }" r }: I- t# g3 F, a3 u; W/ z( H
else0 U; U* Q$ O+ H1 Z% l7 D7 N
{ 2 C9 Y3 f- _' U( Y1 J updateTimestampsCache = null; 8 P: P/ {- E4 E. @5 H! h( { queryCache = null;" |* ~8 B& s9 |3 Q+ X* X! T# Q' _
queryCaches = null; ) G1 F7 {- K4 U" u# S/ p1 ` }6 H3 x( j1 u# t) o4 E
5 r* Z0 ^6 x' M6 @6 [# j9 h #region Checking for named queries - S' F* g) m4 B( C8 ~6 B if (settings.IsNamedQueryStartupCheckingEnabled)4 h% S1 q M0 x6 R' t/ ]* W
{ G6 K, }$ w9 D h5 _# i IDictionary<string, HibernateException> errors = CheckNamedQueries();& I& j$ Q3 b' I) P; `
if (errors.Count > 0) 6 t% G! X8 J2 o { : p8 _- [) q; M8 r2 S StringBuilder failingQueries = new StringBuilder("Errors in named queries: ");: j( j$ s, T D
foreach (KeyValuePair<string, HibernateException> pair in errors)" u" o3 Q9 O2 [9 }/ I( b# k3 o/ E
{ 8 r+ ~" Q9 I) L* ^9 C+ l& w failingQueries.Append('{').Append(pair.Key).Append('}');7 ?2 f' J, }7 e, b- |; L* P5 P
log.Error("Error in named query: " + pair.Key, pair.Value);% v6 V* _8 p- z! t
}1 R1 Y9 C g0 g6 D
throw new HibernateException(failingQueries.ToString());$ I. O J* W. X/ k C4 s
}/ G0 I& p* B5 x8 z& m8 b
}( L5 U8 p) o9 d6 ^
#endregion# ]+ o& [ K' ]( s! S d( u
, {9 r8 C% t7 i! o2 @0 U Statistics.IsStatisticsEnabled = settings.IsStatisticsEnabled; 2 H( Y: V: {& `( _: H2 i 7 t4 H6 w6 B1 C# z // EntityNotFoundDelegate: E- }$ x" b V7 y7 C
IEntityNotFoundDelegate enfd = cfg.EntityNotFoundDelegate;. w. z$ c, }3 i
if (enfd == null) ) I6 A; k3 A3 w/ T1 C% Y/ b6 f {$ t7 q7 u7 q6 K! G3 M: b4 }
enfd = new DefaultEntityNotFoundDelegate(); * C3 G# L3 t r5 E }9 ~" F9 O* u* Y: I
entityNotFoundDelegate = enfd; ( o! [% }/ \- s1 Z8 G' X}/ l" E/ A$ ~8 `, z( R3 B