MappingSource.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using System.Collections.Generic;
  4. using System.Linq.Expressions;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Runtime.Versioning;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Xml;
  11. using System.Xml.Serialization;
  12. using LinqToSqlShared.Mapping;
  13. namespace System.Data.Linq.Mapping {
  14. using System.Data.Linq.Provider;
  15. using System.Diagnostics.CodeAnalysis;
  16. /// <summary>
  17. /// Represents a source for mapping information.
  18. /// </summary>
  19. public abstract class MappingSource {
  20. MetaModel primaryModel;
  21. ReaderWriterLock rwlock;
  22. Dictionary<Type, MetaModel> secondaryModels;
  23. /// <summary>
  24. /// Gets the MetaModel representing a DataContext and all it's
  25. /// accessible tables, functions and entities.
  26. /// </summary>
  27. public MetaModel GetModel(Type dataContextType) {
  28. if (dataContextType == null) {
  29. throw Error.ArgumentNull("dataContextType");
  30. }
  31. MetaModel model = null;
  32. if (this.primaryModel == null) {
  33. model = this.CreateModel(dataContextType);
  34. Interlocked.CompareExchange<MetaModel>(ref this.primaryModel, model, null);
  35. }
  36. // if the primary one matches, use it!
  37. if (this.primaryModel.ContextType == dataContextType) {
  38. return this.primaryModel;
  39. }
  40. // the rest of this only happens if you are using the mapping source for
  41. // more than one context type
  42. // build a map if one is not already defined
  43. if (this.secondaryModels == null) {
  44. Interlocked.CompareExchange<Dictionary<Type, MetaModel>>(ref this.secondaryModels, new Dictionary<Type, MetaModel>(), null);
  45. }
  46. // if we haven't created a read/writer lock, make one now
  47. if (this.rwlock == null) {
  48. Interlocked.CompareExchange<ReaderWriterLock>(ref this.rwlock, new ReaderWriterLock(), null);
  49. }
  50. // lock the map and look inside
  51. MetaModel foundModel;
  52. this.rwlock.AcquireReaderLock(Timeout.Infinite);
  53. try {
  54. if (this.secondaryModels.TryGetValue(dataContextType, out foundModel)) {
  55. return foundModel;
  56. }
  57. }
  58. finally {
  59. this.rwlock.ReleaseReaderLock();
  60. }
  61. // if it wasn't found, lock for write and try again
  62. this.rwlock.AcquireWriterLock(Timeout.Infinite);
  63. try {
  64. if (this.secondaryModels.TryGetValue(dataContextType, out foundModel)) {
  65. return foundModel;
  66. }
  67. if (model == null) {
  68. model = this.CreateModel(dataContextType);
  69. }
  70. this.secondaryModels.Add(dataContextType, model);
  71. }
  72. finally {
  73. this.rwlock.ReleaseWriterLock();
  74. }
  75. return model;
  76. }
  77. /// <summary>
  78. /// Creates a new instance of a MetaModel. This method is called by GetModel().
  79. /// Override this method when defining a new type of MappingSource.
  80. /// </summary>
  81. /// <param name="dataContextType"></param>
  82. /// <returns></returns>
  83. protected abstract MetaModel CreateModel(Type dataContextType);
  84. }
  85. /// <summary>
  86. /// A mapping source that uses attributes on the context to create the mapping model.
  87. /// </summary>
  88. public sealed class AttributeMappingSource : MappingSource {
  89. public AttributeMappingSource() {
  90. }
  91. protected override MetaModel CreateModel(Type dataContextType) {
  92. if (dataContextType == null) {
  93. throw Error.ArgumentNull("dataContextType");
  94. }
  95. return new AttributedMetaModel(this, dataContextType);
  96. }
  97. }
  98. /// <summary>
  99. /// A mapping source that uses an external XML mapping source to create the model.
  100. /// </summary>
  101. public sealed class XmlMappingSource : MappingSource {
  102. DatabaseMapping map;
  103. [ResourceExposure(ResourceScope.Assembly)] // map parameter contains type names.
  104. private XmlMappingSource(DatabaseMapping map) {
  105. this.map = map;
  106. }
  107. [ResourceExposure(ResourceScope.None)] // Exposure is via map instance variable.
  108. [ResourceConsumption(ResourceScope.Assembly | ResourceScope.Machine, ResourceScope.Assembly | ResourceScope.Machine)] // For MappedMetaModel constructor call.
  109. protected override MetaModel CreateModel(Type dataContextType) {
  110. if (dataContextType == null) {
  111. throw Error.ArgumentNull("dataContextType");
  112. }
  113. return new MappedMetaModel(this, dataContextType, this.map);
  114. }
  115. /// <summary>
  116. /// Create a mapping source from xml string.
  117. /// </summary>
  118. /// <param name="dataContextType">The type of DataContext to base the mapping on.</param>
  119. /// <param name="xml">A string containing XML.</param>
  120. /// <returns>The mapping source.</returns>
  121. [ResourceExposure(ResourceScope.Assembly)] // Xml contains type names.
  122. [ResourceConsumption(ResourceScope.Assembly)] // For FromReader method call.
  123. public static XmlMappingSource FromXml(string xml) {
  124. if (xml == null) {
  125. throw Error.ArgumentNull("xml");
  126. }
  127. XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xml));
  128. reader.DtdProcessing = DtdProcessing.Prohibit;
  129. return FromReader(reader);
  130. }
  131. /// <summary>
  132. /// Create a mapping source from xml reader.
  133. /// </summary>
  134. /// <param name="dataContextType">The type of DataContext to base the mapping on.</param>
  135. /// <param name="xml">An xml reader.</param>
  136. /// <returns>The mapping source.</returns>
  137. [ResourceExposure(ResourceScope.Assembly)] // reader parameter contains type names.
  138. [ResourceConsumption(ResourceScope.Assembly)] // XmlMappingSource constructor call.
  139. public static XmlMappingSource FromReader(XmlReader reader) {
  140. if (reader == null) {
  141. throw Error.ArgumentNull("reader");
  142. }
  143. reader.MoveToContent();
  144. DatabaseMapping db = XmlMappingReader.ReadDatabaseMapping(reader);
  145. if (db == null) {
  146. throw Error.DatabaseNodeNotFound(XmlMappingConstant.MappingNamespace);
  147. }
  148. return new XmlMappingSource(db);
  149. }
  150. /// <summary>
  151. /// Create a mapping source from xml in a stream.
  152. /// </summary>
  153. /// <param name="dataContextType">The type of DataContext to base the mapping on.</param>
  154. /// <param name="xml">A stream of xml.</param>
  155. /// <returns>The mapping source.</returns>
  156. [ResourceExposure(ResourceScope.Assembly)] // Stream contains type names.
  157. [ResourceConsumption(ResourceScope.Assembly)] // For FromReader method call.
  158. public static XmlMappingSource FromStream(System.IO.Stream stream) {
  159. if (stream == null) {
  160. throw Error.ArgumentNull("stream");
  161. }
  162. XmlTextReader reader = new XmlTextReader(stream);
  163. reader.DtdProcessing = DtdProcessing.Prohibit;
  164. return FromReader(reader);
  165. }
  166. /// <summary>
  167. /// Create a mapping source from xml loaded from a url.
  168. /// </summary>
  169. /// <param name="dataContextType">The type of DataContext to base the mapping on.</param>
  170. /// <param name="url">The Url pointing to the xml.</param>
  171. /// <returns>The mapping source.</returns>
  172. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification="Unknown reason.")]
  173. [ResourceExposure(ResourceScope.Machine | ResourceScope.Assembly)] // url parameter which may contain type names.
  174. [ResourceConsumption(ResourceScope.Machine | ResourceScope.Assembly)] // XmlTextReader constructor & FromReader method call.
  175. public static XmlMappingSource FromUrl(string url) {
  176. if (url == null) {
  177. throw Error.ArgumentNull("url");
  178. }
  179. XmlTextReader reader = new XmlTextReader(url);
  180. reader.DtdProcessing = DtdProcessing.Prohibit;
  181. try {
  182. return FromReader(reader);
  183. }
  184. finally {
  185. reader.Close();
  186. }
  187. }
  188. }
  189. class XmlMappingReader {
  190. private static string RequiredAttribute(XmlReader reader, string attribute) {
  191. string result = OptionalAttribute(reader, attribute);
  192. if (result == null) {
  193. throw Error.CouldNotFindRequiredAttribute(attribute, reader.ReadOuterXml());
  194. }
  195. return result;
  196. }
  197. private static string OptionalAttribute(XmlReader reader, string attribute) {
  198. return reader.GetAttribute(attribute);
  199. }
  200. private static bool OptionalBoolAttribute(XmlReader reader, string attribute, bool @default) {
  201. string value = OptionalAttribute(reader, attribute);
  202. return (value != null) ? bool.Parse(value) : @default;
  203. }
  204. private static bool? OptionalNullableBoolAttribute(XmlReader reader, string attribute) {
  205. string value = OptionalAttribute(reader, attribute);
  206. return (value != null) ? (bool?)bool.Parse(value) : null;
  207. }
  208. private static void AssertEmptyElement(XmlReader reader) {
  209. if (!reader.IsEmptyElement) {
  210. string nodeName = reader.Name;
  211. reader.Read();
  212. if (reader.NodeType != XmlNodeType.EndElement) {
  213. throw Error.ExpectedEmptyElement(nodeName, reader.NodeType, reader.Name);
  214. }
  215. }
  216. reader.Skip();
  217. }
  218. internal static DatabaseMapping ReadDatabaseMapping(XmlReader reader) {
  219. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  220. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Database) {
  221. return null;
  222. }
  223. ValidateAttributes(reader, new[] {
  224. XmlMappingConstant.Name,
  225. XmlMappingConstant.Provider
  226. });
  227. DatabaseMapping dm = new DatabaseMapping();
  228. dm.DatabaseName = RequiredAttribute(reader, XmlMappingConstant.Name);
  229. dm.Provider = OptionalAttribute(reader, XmlMappingConstant.Provider);
  230. if (!reader.IsEmptyElement) {
  231. reader.ReadStartElement();
  232. reader.MoveToContent();
  233. while (reader.NodeType != XmlNodeType.EndElement) {
  234. if (reader.NodeType == XmlNodeType.Whitespace || !IsInNamespace(reader)) {
  235. reader.Skip();
  236. continue;
  237. }
  238. switch (reader.LocalName) {
  239. case XmlMappingConstant.Table:
  240. dm.Tables.Add(ReadTableMapping(reader));
  241. break;
  242. case XmlMappingConstant.Function:
  243. dm.Functions.Add(ReadFunctionMapping(reader));
  244. break;
  245. default:
  246. throw Error.UnrecognizedElement(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  247. }
  248. reader.MoveToContent();
  249. }
  250. if (reader.LocalName != XmlMappingConstant.Database) {
  251. throw Error.UnexpectedElement(XmlMappingConstant.Database, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  252. }
  253. reader.ReadEndElement();
  254. }
  255. else {
  256. System.Diagnostics.Debug.Assert(false, "DatabaseMapping has no content");
  257. reader.Skip();
  258. }
  259. return dm;
  260. }
  261. internal static bool IsInNamespace(XmlReader reader) {
  262. return reader.LookupNamespace(reader.Prefix) == XmlMappingConstant.MappingNamespace;
  263. }
  264. internal static void ValidateAttributes(XmlReader reader, string[] validAttributes) {
  265. if (reader.HasAttributes) {
  266. List<string> attrList = new List<string>(validAttributes);
  267. const string xmlns = "xmlns";
  268. for (int i = 0; i < reader.AttributeCount; i++) {
  269. reader.MoveToAttribute(i);
  270. // if the node's in the namespace, it is required to be one of the valid ones
  271. if (IsInNamespace(reader) && reader.LocalName != xmlns && !attrList.Contains(reader.LocalName)) {
  272. throw Error.UnrecognizedAttribute(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : ":", reader.LocalName));
  273. }
  274. }
  275. reader.MoveToElement(); // Moves the reader back to the element node.
  276. }
  277. }
  278. internal static FunctionMapping ReadFunctionMapping(XmlReader reader) {
  279. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  280. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Function) {
  281. throw Error.UnexpectedElement(XmlMappingConstant.Function, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  282. }
  283. ValidateAttributes(reader, new[] {
  284. XmlMappingConstant.Name,
  285. XmlMappingConstant.Method,
  286. XmlMappingConstant.IsComposable
  287. });
  288. FunctionMapping fm = new FunctionMapping();
  289. fm.MethodName = RequiredAttribute(reader, XmlMappingConstant.Method);
  290. fm.Name = OptionalAttribute(reader, XmlMappingConstant.Name);
  291. fm.IsComposable = OptionalBoolAttribute(reader, XmlMappingConstant.IsComposable, false);
  292. if (!reader.IsEmptyElement) {
  293. reader.ReadStartElement();
  294. reader.MoveToContent();
  295. while (reader.NodeType != XmlNodeType.EndElement) {
  296. if (reader.NodeType == XmlNodeType.Whitespace || !IsInNamespace(reader)) {
  297. reader.Skip();
  298. continue;
  299. }
  300. switch (reader.LocalName) {
  301. case XmlMappingConstant.Parameter:
  302. fm.Parameters.Add(ReadParameterMapping(reader));
  303. break;
  304. case XmlMappingConstant.ElementType:
  305. fm.Types.Add(ReadElementTypeMapping(null, reader));
  306. break;
  307. case XmlMappingConstant.Return:
  308. fm.FunReturn = ReadReturnMapping(reader);
  309. break;
  310. default:
  311. throw Error.UnrecognizedElement(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  312. }
  313. reader.MoveToContent();
  314. }
  315. reader.ReadEndElement();
  316. }
  317. else {
  318. // no content is okay
  319. reader.Skip();
  320. }
  321. return fm;
  322. }
  323. private static ReturnMapping ReadReturnMapping(XmlReader reader) {
  324. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  325. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Return) {
  326. throw Error.UnexpectedElement(XmlMappingConstant.Return, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  327. }
  328. ValidateAttributes(reader, new[] {
  329. XmlMappingConstant.DbType
  330. });
  331. ReturnMapping rm = new ReturnMapping();
  332. rm.DbType = OptionalAttribute(reader, XmlMappingConstant.DbType);
  333. AssertEmptyElement(reader);
  334. return rm;
  335. }
  336. private static ParameterMapping ReadParameterMapping(XmlReader reader) {
  337. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  338. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Parameter) {
  339. throw Error.UnexpectedElement(XmlMappingConstant.Parameter, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  340. }
  341. ValidateAttributes(reader, new[] {
  342. XmlMappingConstant.Name,
  343. XmlMappingConstant.DbType,
  344. XmlMappingConstant.Parameter,
  345. XmlMappingConstant.Direction
  346. });
  347. ParameterMapping pm = new ParameterMapping();
  348. pm.Name = RequiredAttribute(reader, XmlMappingConstant.Name);
  349. pm.ParameterName = RequiredAttribute(reader, XmlMappingConstant.Parameter);
  350. pm.DbType = OptionalAttribute(reader, XmlMappingConstant.DbType);
  351. pm.XmlDirection = OptionalAttribute(reader, XmlMappingConstant.Direction);
  352. AssertEmptyElement(reader);
  353. return pm;
  354. }
  355. private static TableMapping ReadTableMapping(XmlReader reader) {
  356. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  357. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Table) {
  358. throw Error.UnexpectedElement(XmlMappingConstant.Table, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  359. }
  360. ValidateAttributes(reader, new[] {
  361. XmlMappingConstant.Name,
  362. XmlMappingConstant.Member
  363. });
  364. TableMapping tm = new TableMapping();
  365. tm.TableName = OptionalAttribute(reader, XmlMappingConstant.Name);
  366. tm.Member = OptionalAttribute(reader, XmlMappingConstant.Member);
  367. if (!reader.IsEmptyElement) {
  368. reader.ReadStartElement();
  369. reader.MoveToContent();
  370. while (reader.NodeType != XmlNodeType.EndElement) {
  371. if (reader.NodeType == XmlNodeType.Whitespace || !IsInNamespace(reader)) {
  372. reader.Skip();
  373. continue;
  374. }
  375. switch (reader.LocalName) {
  376. case XmlMappingConstant.Type:
  377. if (tm.RowType != null) {
  378. goto default;
  379. }
  380. tm.RowType = ReadTypeMapping(null, reader);
  381. break;
  382. default:
  383. throw Error.UnrecognizedElement(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  384. }
  385. reader.MoveToContent();
  386. }
  387. if (reader.LocalName != XmlMappingConstant.Table) {
  388. throw Error.UnexpectedElement(XmlMappingConstant.Table, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  389. }
  390. reader.ReadEndElement();
  391. }
  392. else {
  393. System.Diagnostics.Debug.Assert(false, "Table has no content");
  394. reader.Skip();
  395. }
  396. return tm;
  397. }
  398. private static TypeMapping ReadElementTypeMapping(TypeMapping baseType, XmlReader reader) {
  399. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  400. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.ElementType) {
  401. throw Error.UnexpectedElement(XmlMappingConstant.Type, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  402. }
  403. return ReadTypeMappingImpl(baseType, reader);
  404. }
  405. private static TypeMapping ReadTypeMapping(TypeMapping baseType, XmlReader reader) {
  406. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  407. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Type) {
  408. throw Error.UnexpectedElement(XmlMappingConstant.Type, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  409. }
  410. return ReadTypeMappingImpl(baseType, reader);
  411. }
  412. private static TypeMapping ReadTypeMappingImpl(TypeMapping baseType, XmlReader reader) {
  413. ValidateAttributes(reader, new[] {
  414. XmlMappingConstant.Name,
  415. XmlMappingConstant.InheritanceCode,
  416. XmlMappingConstant.IsInheritanceDefault
  417. });
  418. TypeMapping tm = new TypeMapping();
  419. tm.BaseType = baseType;
  420. tm.Name = RequiredAttribute(reader, XmlMappingConstant.Name);
  421. tm.InheritanceCode = OptionalAttribute(reader, XmlMappingConstant.InheritanceCode);
  422. tm.IsInheritanceDefault = OptionalBoolAttribute(reader, XmlMappingConstant.IsInheritanceDefault, false);
  423. if (!reader.IsEmptyElement) {
  424. reader.ReadStartElement();
  425. reader.MoveToContent();
  426. while (reader.NodeType != XmlNodeType.EndElement) {
  427. if (reader.NodeType == XmlNodeType.Whitespace || !IsInNamespace(reader)) {
  428. reader.Skip();
  429. continue;
  430. }
  431. switch (reader.LocalName) {
  432. case XmlMappingConstant.Type:
  433. tm.DerivedTypes.Add(ReadTypeMapping(tm, reader));
  434. break;
  435. case XmlMappingConstant.Association:
  436. tm.Members.Add(ReadAssociationMapping(reader));
  437. break;
  438. case XmlMappingConstant.Column:
  439. tm.Members.Add(ReadColumnMapping(reader));
  440. break;
  441. default:
  442. throw Error.UnrecognizedElement(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  443. }
  444. reader.MoveToContent();
  445. }
  446. reader.ReadEndElement();
  447. }
  448. else {
  449. // no content is okay
  450. reader.Skip();
  451. }
  452. return tm;
  453. }
  454. private static AssociationMapping ReadAssociationMapping(XmlReader reader) {
  455. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  456. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Association) {
  457. throw Error.UnexpectedElement(XmlMappingConstant.Association, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  458. }
  459. ValidateAttributes(reader, new[] {
  460. XmlMappingConstant.Name,
  461. XmlMappingConstant.IsForeignKey,
  462. XmlMappingConstant.IsUnique,
  463. XmlMappingConstant.Member,
  464. XmlMappingConstant.OtherKey,
  465. XmlMappingConstant.Storage,
  466. XmlMappingConstant.ThisKey,
  467. XmlMappingConstant.DeleteRule,
  468. XmlMappingConstant.DeleteOnNull,
  469. });
  470. AssociationMapping am = new AssociationMapping();
  471. am.DbName = OptionalAttribute(reader, XmlMappingConstant.Name);
  472. am.IsForeignKey = OptionalBoolAttribute(reader, XmlMappingConstant.IsForeignKey, false);
  473. am.IsUnique = OptionalBoolAttribute(reader, XmlMappingConstant.IsUnique, false);
  474. am.MemberName = RequiredAttribute(reader, XmlMappingConstant.Member);
  475. am.OtherKey = OptionalAttribute(reader, XmlMappingConstant.OtherKey);
  476. am.StorageMemberName = OptionalAttribute(reader, XmlMappingConstant.Storage);
  477. am.ThisKey = OptionalAttribute(reader, XmlMappingConstant.ThisKey);
  478. am.DeleteRule = OptionalAttribute(reader, XmlMappingConstant.DeleteRule);
  479. am.DeleteOnNull = OptionalBoolAttribute(reader, XmlMappingConstant.DeleteOnNull, false);
  480. AssertEmptyElement(reader);
  481. return am;
  482. }
  483. private static ColumnMapping ReadColumnMapping(XmlReader reader) {
  484. System.Diagnostics.Debug.Assert(reader.NodeType == XmlNodeType.Element);
  485. if (!IsInNamespace(reader) || reader.LocalName != XmlMappingConstant.Column) {
  486. throw Error.UnexpectedElement(XmlMappingConstant.Column, String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}{1}{2}", reader.Prefix, String.IsNullOrEmpty(reader.Prefix) ? "" : "/", reader.LocalName));
  487. }
  488. ValidateAttributes(reader, new[] {
  489. XmlMappingConstant.Name,
  490. XmlMappingConstant.DbType,
  491. XmlMappingConstant.IsDbGenerated,
  492. XmlMappingConstant.IsDiscriminator,
  493. XmlMappingConstant.IsPrimaryKey,
  494. XmlMappingConstant.IsVersion,
  495. XmlMappingConstant.Member,
  496. XmlMappingConstant.Storage,
  497. XmlMappingConstant.Expression,
  498. XmlMappingConstant.CanBeNull,
  499. XmlMappingConstant.UpdateCheck,
  500. XmlMappingConstant.AutoSync
  501. });
  502. ColumnMapping cm = new ColumnMapping();
  503. cm.DbName = OptionalAttribute(reader, XmlMappingConstant.Name);
  504. cm.DbType = OptionalAttribute(reader, XmlMappingConstant.DbType);
  505. cm.IsDbGenerated = OptionalBoolAttribute(reader, XmlMappingConstant.IsDbGenerated, false);
  506. cm.IsDiscriminator = OptionalBoolAttribute(reader, XmlMappingConstant.IsDiscriminator, false);
  507. cm.IsPrimaryKey = OptionalBoolAttribute(reader, XmlMappingConstant.IsPrimaryKey, false);
  508. cm.IsVersion = OptionalBoolAttribute(reader, XmlMappingConstant.IsVersion, false);
  509. cm.MemberName = RequiredAttribute(reader, XmlMappingConstant.Member);
  510. cm.StorageMemberName = OptionalAttribute(reader, XmlMappingConstant.Storage);
  511. cm.Expression = OptionalAttribute(reader, XmlMappingConstant.Expression);
  512. cm.CanBeNull = OptionalNullableBoolAttribute(reader, XmlMappingConstant.CanBeNull);
  513. string updateCheck = OptionalAttribute(reader, XmlMappingConstant.UpdateCheck);
  514. cm.UpdateCheck = (updateCheck == null) ? UpdateCheck.Always : (UpdateCheck)Enum.Parse(typeof(UpdateCheck), updateCheck);
  515. string autoSync = OptionalAttribute(reader, XmlMappingConstant.AutoSync);
  516. cm.AutoSync = (autoSync == null) ? AutoSync.Default : (AutoSync)Enum.Parse(typeof(AutoSync), autoSync);
  517. AssertEmptyElement(reader);
  518. return cm;
  519. }
  520. }
  521. }