PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/T4RIA/T4RIA/T4RIA.tt

#
Unknown | 617 lines | 542 code | 75 blank | 0 comment | 0 complexity | f4048ee32736594208e4bc16a8e8cb1f MD5 | raw file
  1. <#
  2. /*
  3. T4RIA Version 1.1.0
  4. Find latest version and documentation at http://riaservicescontrib.codeplex.com/wikipage?title=T4RIA
  5. T4RIA is part of the WCF RIA Contrib project (http://riaservicescontrib.codeplex.com/)
  6. Maintained by Petr Hosek, petr.hosek@baud.cz (http://www.baud.cz/blog)
  7. Developed using T4 Toolbox (http://t4toolbox.codeplex.com/)
  8. Please use in accordance to the Microsoft Public License (http://www.microsoft.com/opensource/licenses.mspx)
  9. */
  10. #>
  11. <#@ template language="C#" hostspecific="True" debug="True" #>
  12. <#@ include file="T4Toolbox.tt" #>
  13. <#@ assembly name="Microsoft.CSharp" #>
  14. <#@ assembly name="System.Core" #>
  15. <#@ assembly name="System.Data" #>
  16. <#@ assembly name="System.Xml" #>
  17. <#@ assembly name="System.Xml.Linq" #>
  18. <#@ import namespace="System.Collections" #>
  19. <#@ import namespace="System.Collections.Generic" #>
  20. <#@ import namespace="System.Dynamic" #>
  21. <#@ import namespace="System.IO" #>
  22. <#@ import namespace="System.Linq" #>
  23. <#@ import namespace="System.Xml" #>
  24. <#@ import namespace="System.Xml.Linq" #>
  25. <#
  26. // Generate WCF RIA services for entity model
  27. var serviceGenerator = new ServiceGenerator();
  28. serviceGenerator.Run();
  29. // Generate entity metadata for validation
  30. var metadataGenerator = new MetadataGenerator();
  31. metadataGenerator.Run();
  32. // Generate entity resource for localization
  33. var resourceGenerator = new ResourceGenerator();
  34. resourceGenerator.Run();
  35. #>
  36. <#@ include file="T4RIA.settings.t4" #>
  37. <#+
  38. public class AutoServiceTemplate : CSharpTemplate
  39. {
  40. public dynamic Entity { get; set; }
  41. public override string TransformText()
  42. {
  43. #>
  44. namespace <#= Namespace #>.Services
  45. {
  46. using System;
  47. using System.Collections.Generic;
  48. using System.ComponentModel;
  49. using System.ComponentModel.DataAnnotations;
  50. using System.Data;
  51. using System.Linq;
  52. using System.ServiceModel.DomainServices.EntityFramework;
  53. using System.ServiceModel.DomainServices.Hosting;
  54. using System.ServiceModel.DomainServices.Server;
  55. using <#= Namespace #>.Models;
  56. public partial class <#= ServiceName #> : LinqToEntitiesDomainService<<#= ContainerName #>>
  57. {
  58. public IQueryable<<#= Entity.Name #>> Get<#= Entity.Name #>()
  59. {
  60. return this.ObjectContext.<#= Entity.Mapping.StoreEntitySet #>;
  61. }
  62. public void Insert<#= Entity.Name #>(<#= Entity.Name #> <#= FieldName(Entity.Name) #>)
  63. {
  64. if (<#= FieldName(Entity.Name) #>.EntityState != EntityState.Detached)
  65. {
  66. this.ObjectContext.ObjectStateManager.ChangeObjectState(<#= FieldName(Entity.Name) #>, EntityState.Added);
  67. }
  68. else
  69. {
  70. this.ObjectContext.<#= Entity.Mapping.StoreEntitySet #>.AddObject(<#= FieldName(Entity.Name) #>);
  71. }
  72. }
  73. public void Update<#= Entity.Name #>(<#= Entity.Name #> current<#= Entity.Name #>)
  74. {
  75. this.ObjectContext.<#= Entity.Mapping.StoreEntitySet #>.AttachAsModified(current<#= Entity.Name #>, this.ChangeSet.GetOriginal(current<#= Entity.Name #>));
  76. }
  77. public void Delete<#= Entity.Name #>(<#= Entity.Name #> <#= FieldName(Entity.Name) #>)
  78. {
  79. if (<#= FieldName(Entity.Name) #>.EntityState == EntityState.Detached)
  80. {
  81. this.ObjectContext.<#= Entity.Mapping.StoreEntitySet #>.Attach(<#= FieldName(Entity.Name) #>);
  82. }
  83. this.ObjectContext.<#= Entity.Mapping.StoreEntitySet #>.DeleteObject(<#= FieldName(Entity.Name) #>);
  84. }
  85. }
  86. }
  87. <#+
  88. return this.GenerationEnvironment.ToString();
  89. }
  90. }
  91. public class CustomServiceTemplate : CSharpTemplate
  92. {
  93. public override string TransformText()
  94. {
  95. #>
  96. namespace <#= Namespace #>.Services
  97. {
  98. using System;
  99. using System.Collections.Generic;
  100. using System.ComponentModel;
  101. using System.ComponentModel.DataAnnotations;
  102. using System.Data;
  103. using System.Linq;
  104. using System.ServiceModel.DomainServices.EntityFramework;
  105. using System.ServiceModel.DomainServices.Hosting;
  106. using System.ServiceModel.DomainServices.Server;
  107. using GeneratedMetadataProvider;
  108. using <#= Namespace #>.Models;
  109. [EnableClientAccess]
  110. [GeneratedMetadataProvider]
  111. public partial class <#= ServiceName #> : LinqToEntitiesDomainService<<#= ContainerName #>>
  112. {
  113. }
  114. }
  115. <#+
  116. return this.GenerationEnvironment.ToString();
  117. }
  118. }
  119. public class ResourceTemplate : Template
  120. {
  121. public IEnumerable<dynamic> Entities { get; set; }
  122. public IEnumerable<string> Properties { get; set; }
  123. public String LabelFormat { get; set; }
  124. public override string TransformText()
  125. {
  126. #>
  127. <?xml version="1.0" encoding="utf-8"?>
  128. <root>
  129. <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  130. <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
  131. <xsd:element name="root" msdata:IsDataSet="true">
  132. <xsd:complexType>
  133. <xsd:choice maxOccurs="unbounded">
  134. <xsd:element name="metadata">
  135. <xsd:complexType>
  136. <xsd:sequence>
  137. <xsd:element name="value" type="xsd:string" minOccurs="0" />
  138. </xsd:sequence>
  139. <xsd:attribute name="name" use="required" type="xsd:string" />
  140. <xsd:attribute name="type" type="xsd:string" />
  141. <xsd:attribute name="mimetype" type="xsd:string" />
  142. <xsd:attribute ref="xml:space" />
  143. </xsd:complexType>
  144. </xsd:element>
  145. <xsd:element name="assembly">
  146. <xsd:complexType>
  147. <xsd:attribute name="alias" type="xsd:string" />
  148. <xsd:attribute name="name" type="xsd:string" />
  149. </xsd:complexType>
  150. </xsd:element>
  151. <xsd:element name="data">
  152. <xsd:complexType>
  153. <xsd:sequence>
  154. <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
  155. <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
  156. </xsd:sequence>
  157. <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
  158. <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
  159. <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
  160. <xsd:attribute ref="xml:space" />
  161. </xsd:complexType>
  162. </xsd:element>
  163. <xsd:element name="resheader">
  164. <xsd:complexType>
  165. <xsd:sequence>
  166. <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
  167. </xsd:sequence>
  168. <xsd:attribute name="name" type="xsd:string" use="required" />
  169. </xsd:complexType>
  170. </xsd:element>
  171. </xsd:choice>
  172. </xsd:complexType>
  173. </xsd:element>
  174. </xsd:schema>
  175. <resheader name="resmimetype">
  176. <value>text/microsoft-resx</value>
  177. </resheader>
  178. <resheader name="version">
  179. <value>2.0</value>
  180. </resheader>
  181. <resheader name="reader">
  182. <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  183. </resheader>
  184. <resheader name="writer">
  185. <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  186. </resheader>
  187. <#+
  188. foreach (String property in Properties)
  189. {
  190. var entities = Entities.Where(e => (e.Property as IList<dynamic>).Any(p => p.Name == property)).Select(e => e.Name);
  191. string label = string.Format(LabelFormat, property);
  192. string comment = string.Join(",", entities.ToArray());
  193. #>
  194. <data name="<#= label #>" xml:space="preserve">
  195. <value><#= property #></value>
  196. <comment><#= comment #></comment>
  197. </data>
  198. <#+
  199. }
  200. #>
  201. </root>
  202. <#+
  203. return this.GenerationEnvironment.ToString();
  204. }
  205. protected override void Validate()
  206. {
  207. if (string.IsNullOrEmpty(LabelFormat))
  208. {
  209. throw new TransformationException("LabelFormat property must be assigned");
  210. }
  211. }
  212. }
  213. public class AutoMetadataTemplate : CSharpTemplate
  214. {
  215. public XElement Entity { get; set; }
  216. public IEnumerable<XElement> Associations { get; set; }
  217. public override string TransformText()
  218. {
  219. #>
  220. #pragma warning disable 649 // disable compiler warnings about unassigned fields
  221. namespace <#= Namespace #>.Models
  222. {
  223. using System;
  224. using System.Collections.Generic;
  225. using System.ComponentModel;
  226. using System.ComponentModel.DataAnnotations;
  227. using System.Data.Objects.DataClasses;
  228. using System.Linq;
  229. using System.ServiceModel.DomainServices.Hosting;
  230. using System.ServiceModel.DomainServices.Server;
  231. using GeneratedMetadataProvider;
  232. using Resources;
  233. [GeneratedMetadataTypeAttribute(typeof(<#= Entity.Attribute("Name").Value #>.<#= Entity.Attribute("Name").Value #>Metadata_Generated))]
  234. public partial class <#= Entity.Attribute("Name").Value #>
  235. {
  236. internal sealed class <#= Entity.Attribute("Name").Value #>Metadata_Generated
  237. {
  238. private <#= Entity.Attribute("Name").Value #>Metadata_Generated()
  239. {
  240. }
  241. <#+ this.WriteFields(); #>
  242. <#+ this.WriteAssociations(); #>
  243. }
  244. }
  245. }
  246. #pragma warning restore 649 // re-enable compiler warnings about unassigned fields
  247. <#+
  248. return this.GenerationEnvironment.ToString();
  249. }
  250. protected void WriteFields()
  251. {
  252. this.PushIndent("\t\t\t");
  253. var keys = Entity.Element(EntityGenerator.edmns + "Key").Elements(EntityGenerator.edmns + "PropertyRef").Select(e => e.Attribute("Name").Value);
  254. foreach (XElement property in Entity.Elements(EntityGenerator.edmns + "Property"))
  255. {
  256. string name = property.Attribute("Name").Value;
  257. if (keys.Contains(name))
  258. {
  259. WriteLine("[Key]");
  260. }
  261. if (ReadonlyProperties.Contains(name))
  262. {
  263. WriteLine("[ReadOnly(true)]");
  264. }
  265. if (TimestampProperties.Contains(name))
  266. {
  267. WriteLine("[Timestamp]");
  268. }
  269. if (!NonlocalizableProperties.Contains(name))
  270. {
  271. WriteLine("[Display(Name = \"{0}Label\", ResourceType = typeof({1}Resources))]", name, ServiceName);
  272. }
  273. string type = property.Attribute("Type").Value;
  274. switch (type)
  275. {
  276. case "Time":
  277. type = "TimeSpan";
  278. break;
  279. }
  280. XAttribute maxLength = property.Attribute("MaxLength");
  281. if (type == "String" && maxLength != null && maxLength.Value != "Max")
  282. {
  283. WriteLine("[StringLength({0}, ErrorMessageResourceName = \"ValidationError{1}Length\", ErrorMessageResourceType = typeof(ValidationError{2}Resources))]", maxLength.Value, name, ServiceName);
  284. }
  285. XAttribute nullable = property.Attribute("Nullable");
  286. if (type != "String" && nullable == null)
  287. {
  288. WriteLine("public Nullable<{0}> {1};", type, name);
  289. }
  290. else
  291. {
  292. WriteLine("public {0} {1};", type, name);
  293. }
  294. WriteLine(string.Empty);
  295. }
  296. this.PopIndent();
  297. }
  298. protected void WriteAssociations()
  299. {
  300. this.PushIndent("\t\t\t");
  301. foreach (XElement property in Entity.Elements(EntityGenerator.edmns + "NavigationProperty"))
  302. {
  303. string name = property.Attribute("Name").Value;
  304. string relationship = property.Attribute("Relationship").Value;
  305. string fromRole = property.Attribute("FromRole").Value;
  306. string toRole = property.Attribute("ToRole").Value;
  307. var association = Associations.Single(a => a.Attribute("Name").Value == relationship.Split('.').Last());
  308. var referentialConstraint = association.Element(EntityGenerator.edmns + "ReferentialConstraint");
  309. if (referentialConstraint == null)
  310. {
  311. continue;
  312. }
  313. var associationName = association.Attribute("Name").Value;
  314. var principal = referentialConstraint.Element(EntityGenerator.edmns + "Principal");
  315. var principalPropertyRef = principal.Element(EntityGenerator.edmns + "PropertyRef").Attribute("Name").Value;
  316. var dependent = referentialConstraint.Element(EntityGenerator.edmns + "Dependent");
  317. var dependentPropertyRef = dependent.Element(EntityGenerator.edmns + "PropertyRef").Attribute("Name").Value;
  318. var thisKey = referentialConstraint.Elements().Single(e => e.Attribute("Role").Value == fromRole).Element(EntityGenerator.edmns + "PropertyRef").Attribute("Name").Value;
  319. var otherKey = referentialConstraint.Elements().Single(e => e.Attribute("Role").Value == toRole).Element(EntityGenerator.edmns + "PropertyRef").Attribute("Name").Value;
  320. XElement role = association.Elements(EntityGenerator.edmns + "End").Single(e => e.Attribute("Role").Value == toRole);
  321. var type = role.Attribute("Type").Value.Split('.').Last();
  322. var multiplicity = role.Attribute("Multiplicity").Value;
  323. if (multiplicity == "*")
  324. {
  325. type = string.Format("EntityCollection<{0}>", type);
  326. }
  327. WriteLine("[Include]");
  328. if (fromRole == principal.Attribute("Role").Value && toRole == dependent.Attribute("Role").Value)
  329. {
  330. WriteLine("[Association(\"{0}\", \"{1}\", \"{2}\")]", associationName, principalPropertyRef, dependentPropertyRef);
  331. }
  332. else
  333. {
  334. WriteLine("[Association(\"{0}\", \"{1}\", \"{2}\", IsForeignKey = true)]", associationName, dependentPropertyRef, principalPropertyRef);
  335. }
  336. WriteLine("public {0} {1};", type, name);
  337. WriteLine(string.Empty);
  338. }
  339. this.PopIndent();
  340. }
  341. }
  342. public class CustomMetadataTemplate : CSharpTemplate
  343. {
  344. public string EntityName { get; set; }
  345. public override string TransformText()
  346. {
  347. #>
  348. #pragma warning disable 649 // disable compiler warnings about unassigned fields
  349. namespace <#= Namespace #>.Models
  350. {
  351. using System;
  352. using System.Collections.Generic;
  353. using System.ComponentModel;
  354. using System.ComponentModel.DataAnnotations;
  355. using System.Data.Objects.DataClasses;
  356. using System.Linq;
  357. using System.ServiceModel.DomainServices.Hosting;
  358. using System.ServiceModel.DomainServices.Server;
  359. [MetadataTypeAttribute(typeof(<#= EntityName #>.<#= EntityName #>Metadata))]
  360. public partial class <#= EntityName #>
  361. {
  362. internal sealed class <#= EntityName #>Metadata
  363. {
  364. private <#= EntityName #>Metadata()
  365. {
  366. }
  367. }
  368. }
  369. }
  370. #pragma warning restore 649 // re-enable compiler warnings about unassigned fields
  371. <#+
  372. return this.GenerationEnvironment.ToString();
  373. }
  374. }
  375. public class ServiceGenerator : EntityGenerator
  376. {
  377. private AutoServiceTemplate templateAuto = new AutoServiceTemplate();
  378. private CustomServiceTemplate templateCustom = new CustomServiceTemplate();
  379. protected override void RunCore()
  380. {
  381. templateAuto.Output.References.Add("System.ServiceModel.DomainServices.EntityFramework");
  382. templateAuto.Output.References.Add("System.ServiceModel.DomainServices.Hosting");
  383. templateAuto.Output.References.Add("System.ServiceModel.DomainServices.Server");
  384. templateCustom.Output.PreserveExistingFile = PreserveExistingFile;
  385. templateCustom.Output.References.Add("System.ServiceModel.DomainServices.EntityFramework");
  386. templateCustom.Output.References.Add("System.ServiceModel.DomainServices.Hosting");
  387. templateCustom.Output.References.Add("System.ServiceModel.DomainServices.Server");
  388. templateCustom.RenderToFile(string.Format(@"{0}\{1}.cs", ServicesFolder, ServiceName));
  389. foreach (dynamic entity in EntityTypes)
  390. {
  391. templateAuto.Entity = entity;
  392. templateAuto.RenderToFile(string.Format(@"{0}\{1}.{2}.g.cs", ServicesFolder, ServiceName, entity.Name));
  393. }
  394. }
  395. protected override void Validate()
  396. {
  397. base.Validate();
  398. if (string.IsNullOrEmpty(Namespace))
  399. {
  400. throw new TransformationException("Namespace property must be assigned");
  401. }
  402. if (string.IsNullOrEmpty(ServiceName))
  403. {
  404. throw new TransformationException("ServiceName property must be assigned");
  405. }
  406. if (string.IsNullOrEmpty(ContainerName))
  407. {
  408. throw new TransformationException("ContainerName property must be assigned");
  409. }
  410. }
  411. }
  412. public class ResourceGenerator : EntityGenerator
  413. {
  414. private ResourceTemplate template = new ResourceTemplate();
  415. protected override void RunCore()
  416. {
  417. template.Output.PreserveExistingFile = PreserveExistingFile;
  418. template.Output.BuildAction = BuildAction.EmbeddedResource;
  419. template.Output.CustomTool = "PublicResXFileCodeGenerator";
  420. template.Entities = EntityTypes;
  421. template.Properties = Schema.Elements(edmns + "EntityType")
  422. .SelectMany(e => e.Elements(EntityGenerator.edmns + "Property"))
  423. .Select(e => e.Attribute("Name").Value)
  424. .Where(e => !NonlocalizableProperties.Contains(e))
  425. .OrderBy(e => e).Distinct();
  426. template.LabelFormat = "{0}Label";
  427. template.RenderToFile(string.Format(@"{0}\{1}Resources.resx", ResourcesFolder, ServiceName));
  428. template.Properties = Schema.Elements(edmns + "EntityType")
  429. .Elements(EntityGenerator.edmns + "Property")
  430. .Where(e => e.Attribute("Type").Value == "String" && e.Attribute("MaxLength") != null && e.Attribute("MaxLength").Value != "Max")
  431. .Select(e => e.Attribute("Name").Value)
  432. .OrderBy(e => e).Distinct();
  433. template.LabelFormat = "ValidationError{0}Length";
  434. template.RenderToFile(string.Format(@"{0}\ValidationError{1}Resources.resx", ResourcesFolder, ServiceName));
  435. }
  436. protected override void Validate()
  437. {
  438. base.Validate();
  439. if (string.IsNullOrEmpty(ServiceName))
  440. {
  441. throw new TransformationException("ServiceName property must be assigned");
  442. }
  443. }
  444. }
  445. public class MetadataGenerator : EntityGenerator
  446. {
  447. private AutoMetadataTemplate templateAuto = new AutoMetadataTemplate();
  448. private CustomMetadataTemplate templateCustom = new CustomMetadataTemplate();
  449. protected override void RunCore()
  450. {
  451. templateAuto.Output.References.Add("System.ComponentModel.DataAnnotations");
  452. templateAuto.Output.References.Add("System.ServiceModel.DomainServices.Hosting");
  453. templateAuto.Output.References.Add("System.ServiceModel.DomainServices.Server");
  454. templateCustom.Output.PreserveExistingFile = PreserveExistingFile;
  455. templateCustom.Output.References.Add("System.ComponentModel.DataAnnotations");
  456. templateCustom.Output.References.Add("System.ServiceModel.DomainServices.Hosting");
  457. templateCustom.Output.References.Add("System.ServiceModel.DomainServices.Server");
  458. templateAuto.Associations = Schema.Elements(edmns + "Association");
  459. foreach (XElement type in Schema.Elements(edmns + "EntityType"))
  460. {
  461. templateAuto.Entity = type;
  462. templateAuto.RenderToFile(string.Format(@"{0}\{1}.{2}.metadata.g.cs", ModelsFolder, ServiceName, type.Attribute("Name").Value));
  463. templateCustom.EntityName = type.Attribute("Name").Value;
  464. templateCustom.RenderToFile(string.Format(@"{0}\{1}.{2}.metadata.cs", ModelsFolder, ServiceName, type.Attribute("Name").Value));
  465. }
  466. }
  467. protected override void Validate()
  468. {
  469. base.Validate();
  470. if (string.IsNullOrEmpty(Namespace))
  471. {
  472. throw new TransformationException("Namespace property must be assigned");
  473. }
  474. if (string.IsNullOrEmpty(ServiceName))
  475. {
  476. throw new TransformationException("ServiceName property must be assigned");
  477. }
  478. }
  479. }
  480. public abstract class EntityGenerator : Generator
  481. {
  482. public static readonly XNamespace csns = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
  483. public static readonly XNamespace edmns = "http://schemas.microsoft.com/ado/2008/09/edm";
  484. public static readonly XNamespace edmxns = "http://schemas.microsoft.com/ado/2008/10/edmx";
  485. public abstract class EntityContext
  486. {
  487. public string Namespace { get; set; }
  488. public string ServiceName { get; set; }
  489. }
  490. internal XElement Schema { get; private set; }
  491. internal XElement Mapping { get; private set; }
  492. internal IEnumerable<dynamic> EntityTypes { get; private set; }
  493. internal IEnumerable<dynamic> Associations { get; private set; }
  494. public void LoadModel()
  495. {
  496. var directoryName = Path.GetDirectoryName(TransformationContext.Host.TemplateFile);
  497. var runtime = XDocument.Load(string.Format(@"{0}\{1}\{2}.edmx", directoryName, ModelsFolder, ModelName))
  498. .Element(edmxns + "Edmx")
  499. .Element(edmxns + "Runtime");
  500. Schema = runtime.Element(edmxns + "ConceptualModels").Element(edmns + "Schema");
  501. Mapping = runtime.Element(edmxns + "Mappings").Element(csns + "Mapping").Element(csns + "EntityContainerMapping");
  502. EntityTypes = Schema.Elements(edmns + "EntityType").Select(e => ToObject(e)).ToList();
  503. Associations = Schema.Elements(edmns + "Association").Select(e => ToObject(e)).ToList();
  504. var mappings = Mapping.Elements(csns + "EntitySetMapping").Select(e => e.Element(csns + "EntityTypeMapping"));
  505. foreach (dynamic type in EntityTypes)
  506. {
  507. var mapping = mappings.SingleOrDefault((XElement e) => e.Attribute("TypeName").Value.Split('.').Last() == type.Name);
  508. if (mapping != null)
  509. {
  510. type.Mapping = ToObject(mapping.Element(csns + "MappingFragment"));
  511. }
  512. }
  513. }
  514. protected override void Validate()
  515. {
  516. if (string.IsNullOrEmpty(ModelName))
  517. {
  518. throw new TransformationException("ModelName property must be assigned");
  519. }
  520. this.LoadModel();
  521. }
  522. public static dynamic ToObject(XElement element)
  523. {
  524. dynamic expando = new ExpandoObject();
  525. IDictionary<string, object> dictionary = expando as IDictionary<string, object>;
  526. foreach (var attribute in element.Attributes())
  527. {
  528. dictionary.Add(attribute.Name.LocalName, attribute.Value);
  529. }
  530. var node = element.Nodes().FirstOrDefault();
  531. if (node is XText)
  532. {
  533. dictionary.Add("Text", node.ToString());
  534. }
  535. foreach (var group in element.Elements().GroupBy(e => e.Name.LocalName))
  536. {
  537. dictionary.Add(group.Key, group.Select(e => ToObject(e)).ToList());
  538. }
  539. return expando;
  540. }
  541. }
  542. #>