PageRenderTime 53ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/CompositeC1/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/SqlDataProvider_Stores.cs

#
C# | 1063 lines | 722 code | 251 blank | 90 comment | 107 complexity | 1d1994755bf11cac63cedd50b983023a MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. * The contents of this web application are subject to the Mozilla Public License Version
  3. * 1.1 (the "License"); you may not use this web application except in compliance with
  4. * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
  5. *
  6. * Software distributed under the License is distributed on an "AS IS" basis,
  7. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. * for the specific language governing rights and limitations under the License.
  9. *
  10. * The Original Code is owned by and the Initial Developer of the Original Code is
  11. * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
  12. *
  13. * Section 11 of the License is EXPRESSLY amended to include a provision stating
  14. * that any dispute, including but not limited to disputes related to the enforcement
  15. * of the License, to which Composite A/S as owner of the Original Code, as Initial
  16. * Developer or in any other role, becomes a part to shall be governed by Danish law
  17. * and be initiated before the Copenhagen City Court ("K�benhavns Byret")
  18. */
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Configuration;
  22. using System.Data.Linq;
  23. using System.IO;
  24. using System.Linq;
  25. using System.Reflection;
  26. using Composite.Core;
  27. using Composite.Core.Collections.Generic;
  28. using Composite.Core.Configuration;
  29. using Composite.Core.Extensions;
  30. using Composite.Core.Instrumentation;
  31. using Composite.Core.Linq;
  32. using Composite.Core.Sql;
  33. using Composite.Core.Types;
  34. using Composite.Data;
  35. using Composite.Data.DynamicTypes;
  36. using Composite.Data.Foundation;
  37. using Composite.Data.Foundation.CodeGeneration;
  38. using Composite.Plugins.Data.DataProviders.MSSqlServerDataProvider.CodeGeneration;
  39. using Composite.Plugins.Data.DataProviders.MSSqlServerDataProvider.Foundation;
  40. using Composite.Plugins.Data.DataProviders.MSSqlServerDataProvider.Sql;
  41. using System.Text;
  42. namespace Composite.Plugins.Data.DataProviders.MSSqlServerDataProvider
  43. {
  44. internal partial class SqlDataProvider
  45. {
  46. private static readonly string LogTitle = typeof(SqlDataProvider).Name;
  47. private readonly List<SqlDataTypeStoreTable> _createdSqlDataTypeStoreTables = new List<SqlDataTypeStoreTable>();
  48. private Assembly _compositeGeneratedAssembly;
  49. private static readonly Hashtable<Type, bool> _typeLoadResults = new Hashtable<Type, bool>();
  50. public void CreateStores(IReadOnlyCollection<DataTypeDescriptor> dataTypeDescriptors)
  51. {
  52. var types = DataTypeTypesManager.GetDataTypes(dataTypeDescriptors);
  53. foreach (var dataTypeDescriptor in dataTypeDescriptors)
  54. {
  55. if (InterfaceConfigurationManipulator.ConfigurationExists(_dataProviderContext.ProviderName, dataTypeDescriptor))
  56. {
  57. throw new InvalidOperationException(string.Format("SqlDataProvider configuration already contains a interface named '{0}'. Remove it from the configuration and restart the application.", dataTypeDescriptor.TypeManagerTypeName));
  58. }
  59. }
  60. // Creating Sql tables and adding to the configuration
  61. var configElements = new Dictionary<DataTypeDescriptor, InterfaceConfigurationElement>();
  62. foreach (var dataTypeDescriptor in dataTypeDescriptors)
  63. {
  64. Type type = types[dataTypeDescriptor.DataTypeId];
  65. Action<string> existingTablesValidator = tableName =>
  66. {
  67. var errors = new StringBuilder();
  68. var interfaceType = type;
  69. if (!ValidateTable(interfaceType, tableName, errors))
  70. {
  71. throw new InvalidOperationException("Table '{0}' already exist but isn't valid: {1}".FormatWith(tableName, errors.ToString()));
  72. }
  73. };
  74. SqlStoreManipulator.CreateStoresForType(dataTypeDescriptor, existingTablesValidator);
  75. InterfaceConfigurationElement element = InterfaceConfigurationManipulator.AddNew(_dataProviderContext.ProviderName, dataTypeDescriptor);
  76. _interfaceConfigurationElements.Add(element);
  77. configElements.Add(dataTypeDescriptor, element);
  78. }
  79. // Generating necessary classes and performing validation
  80. Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>> allSqlDataTypeStoreDataScopes = BuildAllExistingDataTypeStoreDataScopes();
  81. bool dataContextRecompilationNeeded = false;
  82. var toCompileList = new List<HelperClassesGenerationInfo>();
  83. var generatedClassesInfo = new Dictionary<DataTypeDescriptor, InterfaceGeneratedClassesInfo>();
  84. Type dataContextClass = _sqlDataTypeStoresContainer.DataContextClass;
  85. foreach (var dataTypeDescriptor in dataTypeDescriptors)
  86. {
  87. var element = configElements[dataTypeDescriptor];
  88. // InitializeStoreResult initializeStoreResult = InitializeStore(element, allSqlDataTypeStoreDataScopes);
  89. HelperClassesGenerationInfo toCompile = null;
  90. var classesInfo = InitializeStoreTypes(element, allSqlDataTypeStoreDataScopes, dataContextClass, null, false, ref dataContextRecompilationNeeded, ref toCompile);
  91. if (classesInfo != null && toCompile != null)
  92. {
  93. toCompileList.Add(toCompile);
  94. generatedClassesInfo[dataTypeDescriptor] = classesInfo;
  95. }
  96. }
  97. // Compiling missing classes
  98. if (toCompileList.Any())
  99. {
  100. var codeGenerationBuilder = new CodeGenerationBuilder(_dataProviderContext.ProviderName + ":CreateStores");
  101. foreach (var toCompile in toCompileList)
  102. {
  103. toCompile.GenerateCodeAction(codeGenerationBuilder);
  104. }
  105. var generatedHelperClasses = CodeGenerationManager.CompileRuntimeTempTypes(codeGenerationBuilder, false).ToArray();
  106. foreach (var toCompile in toCompileList)
  107. {
  108. toCompile.PopulateFieldsAction(generatedHelperClasses);
  109. }
  110. }
  111. // Emitting a new DataContext class
  112. if (dataContextRecompilationNeeded)
  113. {
  114. var newDataTypeIds = new HashSet<Guid>(dataTypeDescriptors.Select(d => d.DataTypeId));
  115. _createdSqlDataTypeStoreTables.RemoveAll(f => newDataTypeIds.Contains(f.DataTypeId));
  116. var fields = _createdSqlDataTypeStoreTables.Select(s => new Tuple<string, Type>(s.DataContextFieldName, s.DataContextFieldType)).ToList();
  117. foreach (var classesInfo in generatedClassesInfo.Values)
  118. {
  119. fields.AddRange(classesInfo.Fields.Select(f => new Tuple<string, Type>(f.Value.FieldName, f.Value.FieldType)));
  120. }
  121. dataContextClass = DataContextAssembler.EmitDataContextClass(fields);
  122. UpdateCreatedSqlDataTypeStoreTables(dataContextClass);
  123. }
  124. _sqlDataTypeStoresContainer.DataContextClass = dataContextClass;
  125. // Registering the new type/tables
  126. foreach (var dataTypeDescriptor in dataTypeDescriptors)
  127. {
  128. InterfaceGeneratedClassesInfo classesInfo;
  129. if (!generatedClassesInfo.TryGetValue(dataTypeDescriptor, out classesInfo))
  130. {
  131. throw new InvalidOperationException("No generated classes for data type '{0}' found".FormatWith(dataTypeDescriptor.Name));
  132. }
  133. InitializeStoreResult initInfo = EmbedDataContextInfo(classesInfo, dataContextClass);
  134. AddDataTypeStore(initInfo, false);
  135. }
  136. }
  137. public void AlterStore(UpdateDataTypeDescriptor updateDataTypeDescriptor, bool forceCompile)
  138. {
  139. var dataTypeChangeDescriptor = updateDataTypeDescriptor.CreateDataTypeChangeDescriptor();
  140. using (TimerProfilerFacade.CreateTimerProfiler())
  141. {
  142. SqlStoreManipulator.AlterStoresForType(updateDataTypeDescriptor);
  143. bool localizationChanged = dataTypeChangeDescriptor.AlteredType.Localizeable !=
  144. dataTypeChangeDescriptor.OriginalType.Localizeable;
  145. var oldElement = _interfaceConfigurationElements.Single(f => f.DataTypeId == updateDataTypeDescriptor.OldDataTypeDescriptor.DataTypeId);
  146. var newElement = InterfaceConfigurationManipulator.Change(_dataProviderContext.ProviderName, dataTypeChangeDescriptor, localizationChanged);
  147. if (newElement != null)
  148. {
  149. _interfaceConfigurationElements.Remove(oldElement);
  150. _interfaceConfigurationElements.Add(newElement);
  151. }
  152. if (forceCompile)
  153. {
  154. Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>> allSqlDataTypeStoreDataScopes = BuildAllExistingDataTypeStoreDataScopes();
  155. InitializeStoreResult initializeStoreResult = InitializeStore(newElement ?? oldElement, allSqlDataTypeStoreDataScopes, true);
  156. if (!updateDataTypeDescriptor.NewDataTypeDescriptor.IsCodeGenerated)
  157. {
  158. var interfaceType = updateDataTypeDescriptor.NewDataTypeDescriptor.GetInterfaceType();
  159. if (!DataTypeValidationRegistry.IsValidForProvider(interfaceType, _dataProviderContext.ProviderName))
  160. {
  161. // Revalidating alternated static data type
  162. _sqlDataTypeStoresContainer.RemoveKnownInterface(interfaceType);
  163. DataTypeValidationRegistry.ClearValidationError(interfaceType, _dataProviderContext.ProviderName);
  164. AddDataTypeStore(initializeStoreResult);
  165. }
  166. }
  167. }
  168. }
  169. }
  170. public void DropStore(DataTypeDescriptor dataTypeDescriptor)
  171. {
  172. using (TimerProfilerFacade.CreateTimerProfiler())
  173. {
  174. SqlStoreManipulator.DropStoresForType(_dataProviderContext.ProviderName, dataTypeDescriptor);
  175. InterfaceConfigurationManipulator.Remove(_dataProviderContext.ProviderName, dataTypeDescriptor);
  176. InterfaceConfigurationElement oldElement = _interfaceConfigurationElements.FirstOrDefault(f => f.DataTypeId == dataTypeDescriptor.DataTypeId);
  177. if (oldElement != null)
  178. {
  179. _interfaceConfigurationElements.Remove(oldElement);
  180. }
  181. Guid dataTypeId = dataTypeDescriptor.DataTypeId;
  182. int storesRemoved = _createdSqlDataTypeStoreTables.RemoveAll(item => item.DataTypeId == dataTypeId);
  183. if (storesRemoved > 0)
  184. {
  185. Type interfaceType = dataTypeDescriptor.GetInterfaceType();
  186. _sqlDataTypeStoresContainer.ForgetInterface(interfaceType);
  187. }
  188. }
  189. }
  190. private void InitializeExistingStores()
  191. {
  192. _compositeGeneratedAssembly = _compositeGeneratedAssembly ?? AssemblyFacade.GetGeneratedAssemblyFromBin();
  193. _sqlDataTypeStoresContainer = new SqlDataTypeStoresContainer(_dataProviderContext.ProviderName, _connectionString, _sqlLoggingContext);
  194. Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>> allSqlDataTypeStoreDataScopes = BuildAllExistingDataTypeStoreDataScopes();
  195. var initializedStores = new List<InterfaceGeneratedClassesInfo>();
  196. bool dataContextRecompilationNeeded = false;
  197. Type dataContextClass = TryLoadDataContext(ref dataContextRecompilationNeeded);
  198. var dataTypes = LoadDataTypes(_interfaceConfigurationElements);
  199. var compilationData = new List<HelperClassesGenerationInfo>();
  200. foreach (InterfaceConfigurationElement element in _interfaceConfigurationElements)
  201. {
  202. HelperClassesGenerationInfo toCompile = null;
  203. var generatedClassesInfo = InitializeStoreTypes(element, allSqlDataTypeStoreDataScopes,
  204. dataContextClass, dataTypes, false, ref dataContextRecompilationNeeded, ref toCompile);
  205. if (generatedClassesInfo == null) continue;
  206. if (toCompile != null)
  207. {
  208. compilationData.Add(toCompile);
  209. }
  210. initializedStores.Add(generatedClassesInfo);
  211. }
  212. if (compilationData.Any())
  213. {
  214. var codeGenerationBuilder = new CodeGenerationBuilder(_dataProviderContext.ProviderName + " : compiling missing classes");
  215. foreach (var toCompile in compilationData)
  216. {
  217. toCompile.GenerateCodeAction(codeGenerationBuilder);
  218. }
  219. // Precompiling DataWrapper classes as well to improve loading time
  220. foreach (var interfaceType in dataTypes.Values)
  221. {
  222. if (DataWrapperTypeManager.TryGetWrapperType(interfaceType.FullName) == null)
  223. {
  224. DataWrapperCodeGenerator.AddDataWrapperClassCode(codeGenerationBuilder, interfaceType);
  225. }
  226. }
  227. var types = CodeGenerationManager.CompileRuntimeTempTypes(codeGenerationBuilder, false).ToArray();
  228. foreach (var toCompile in compilationData)
  229. {
  230. toCompile.PopulateFieldsAction(types);
  231. }
  232. }
  233. if (dataContextRecompilationNeeded)
  234. {
  235. dataContextClass = DataContextAssembler.EmitDataContextClass(
  236. initializedStores
  237. .Where(s => s.Fields != null)
  238. .SelectMany(s => s.Fields.Values).Evaluate());
  239. }
  240. _sqlDataTypeStoresContainer.DataContextClass = dataContextClass;
  241. foreach (var typeInfo in initializedStores)
  242. {
  243. var store = EmbedDataContextInfo(typeInfo, dataContextClass);
  244. AddDataTypeStore(store, true, true);
  245. }
  246. }
  247. /// <summary>
  248. /// Loads all the data types referenced in the provider's configuration file.
  249. /// </summary>
  250. private static Dictionary<Guid, Type> LoadDataTypes(IEnumerable<InterfaceConfigurationElement> configurationElements)
  251. {
  252. var dataTypeDescriptors = new List<DataTypeDescriptor>();
  253. foreach (InterfaceConfigurationElement element in configurationElements)
  254. {
  255. Guid dataTypeId = element.DataTypeId;
  256. var dataTypeDescriptor = DataMetaDataFacade.GetDataTypeDescriptor(dataTypeId, true);
  257. if (dataTypeDescriptor == null)
  258. {
  259. throw NewConfigurationException(element, "Failed to get a DataTypeDescriptor by id '{0}'".FormatWith(dataTypeId));
  260. }
  261. dataTypeDescriptors.Add(dataTypeDescriptor);
  262. }
  263. return DataTypeTypesManager.GetDataTypes(dataTypeDescriptors);
  264. }
  265. private static Exception NewConfigurationException(ConfigurationElement element, string message)
  266. {
  267. return new ConfigurationErrorsException(message, element.ElementInformation.Source, element.ElementInformation.LineNumber);
  268. }
  269. private InitializeStoreResult InitializeStore(InterfaceConfigurationElement element,
  270. Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>> allSqlDataTypeStoreDataScopes,
  271. bool forceCompile = false)
  272. {
  273. bool dataContextRecompilationNeeded = false;
  274. Type dataContextClass = _sqlDataTypeStoresContainer.DataContextClass;
  275. var initInfo = InitializeStoreTypes(element, allSqlDataTypeStoreDataScopes, dataContextClass, forceCompile, ref dataContextRecompilationNeeded);
  276. if (initInfo.InterfaceType == null)
  277. {
  278. return new InitializeStoreResult();
  279. }
  280. if (dataContextRecompilationNeeded)
  281. {
  282. _createdSqlDataTypeStoreTables.RemoveAll(f => f.DataTypeId == initInfo.DataTypeDescriptor.DataTypeId);
  283. var existingFields = _createdSqlDataTypeStoreTables.Select(
  284. s => new Tuple<string, Type>(s.DataContextFieldName, s.DataContextFieldType));
  285. var newFields = initInfo.Fields.Select(f => new Tuple<string, Type>(f.Value.FieldName, f.Value.FieldType));
  286. dataContextClass = DataContextAssembler.EmitDataContextClass(existingFields.Concat(newFields).Evaluate());
  287. UpdateCreatedSqlDataTypeStoreTables(dataContextClass);
  288. }
  289. _sqlDataTypeStoresContainer.DataContextClass = dataContextClass;
  290. return EmbedDataContextInfo(initInfo, dataContextClass);
  291. }
  292. private Type TryLoadDataContext(ref bool forceCompile)
  293. {
  294. string dataContextClassFullName = NamesCreator.MakeDataContextClassFullName(_dataProviderContext.ProviderName);
  295. Type dataContextClass = TryGetGeneratedType(dataContextClassFullName);
  296. // Trying to instantiate a data context object
  297. if (dataContextClass != null && !TryLoadDataContextClass(dataContextClass))
  298. {
  299. forceCompile = true;
  300. return null;
  301. }
  302. return dataContextClass;
  303. }
  304. private InitializeStoreResult EmbedDataContextInfo(InterfaceGeneratedClassesInfo initInfo, Type dataContextType)
  305. {
  306. var result = new InitializeStoreResult();
  307. if (initInfo.InterfaceType == null)
  308. {
  309. return result;
  310. }
  311. result.InterfaceType = initInfo.InterfaceType;
  312. var sqlDataTypeStoreTables = new Dictionary<SqlDataTypeStoreTableKey, SqlDataTypeStoreTable>();
  313. foreach (SqlDataTypeStoreDataScope storeDataScope in initInfo.DataScopes)
  314. {
  315. var key = new SqlDataTypeStoreTableKey(storeDataScope.DataScopeName, storeDataScope.CultureName);
  316. result.TableNames.Add(key, storeDataScope.TableName);
  317. Verify.IsNotNull(initInfo.Fields, "Fields collection is null");
  318. StoreTypeInfo fieldInfo;
  319. if (!initInfo.Fields.TryGetValue(key, out fieldInfo))
  320. {
  321. continue;
  322. }
  323. Verify.IsNotNull(fieldInfo, "Field info is missing");
  324. FieldInfo dataContextFieldInfo = dataContextType != null
  325. ? dataContextType.GetField(fieldInfo.FieldName)
  326. : fieldInfo.DataContextField;
  327. Type sqlDataProvdierHelperType = fieldInfo.SqlHelperClass;
  328. var sqlDataProviderHelper = (ISqlDataProviderHelper)Activator.CreateInstance(sqlDataProvdierHelperType);
  329. var sqlDataTypeStoreTable = new SqlDataTypeStoreTable(
  330. initInfo.DataTypeDescriptor.DataTypeId,
  331. dataContextFieldInfo,
  332. sqlDataProviderHelper,
  333. fieldInfo.FieldName,
  334. fieldInfo.FieldType);
  335. _createdSqlDataTypeStoreTables.Add(sqlDataTypeStoreTable);
  336. sqlDataTypeStoreTables.Add(key, sqlDataTypeStoreTable);
  337. }
  338. var sqlDataTypeStore = new SqlDataTypeStore(result.InterfaceType,
  339. sqlDataTypeStoreTables,
  340. initInfo.DataTypeDescriptor.IsCodeGenerated,
  341. _sqlDataTypeStoresContainer);
  342. result.SqlDataTypeStore = sqlDataTypeStore;
  343. return result;
  344. }
  345. private InterfaceGeneratedClassesInfo InitializeStoreTypes(InterfaceConfigurationElement element,
  346. Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>> allSqlDataTypeStoreDataScopes,
  347. Type dataContextClass,
  348. bool forceCompile,
  349. ref bool dataContextRecompilationNeeded)
  350. {
  351. HelperClassesGenerationInfo toCompile = null;
  352. var result = InitializeStoreTypes(element, allSqlDataTypeStoreDataScopes, dataContextClass, null, forceCompile, ref dataContextRecompilationNeeded, ref toCompile);
  353. if (result != null && toCompile != null)
  354. {
  355. var codeGenerationBuilder = new CodeGenerationBuilder(_dataProviderContext.ProviderName + ":" + result.InterfaceType.FullName);
  356. toCompile.GenerateCodeAction(codeGenerationBuilder);
  357. var types = CodeGenerationManager.CompileRuntimeTempTypes(codeGenerationBuilder, false).ToArray();
  358. toCompile.PopulateFieldsAction(types);
  359. }
  360. return result;
  361. }
  362. private InterfaceGeneratedClassesInfo InitializeStoreTypes(InterfaceConfigurationElement element,
  363. Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>> allSqlDataTypeStoreDataScopes,
  364. Type dataContextClass,
  365. Dictionary<Guid, Type> dataTypes,
  366. bool forceCompile,
  367. ref bool dataContextRecompilationNeeded,
  368. ref HelperClassesGenerationInfo helperClassesGenerationInfo)
  369. {
  370. var result = new InterfaceGeneratedClassesInfo();
  371. var dataScopes = new List<SqlDataTypeStoreDataScope>();
  372. foreach (StorageInformation storageInformation in element.Stores)
  373. {
  374. var sqlDataTypeStoreDataScope = new SqlDataTypeStoreDataScope
  375. {
  376. DataScopeName = storageInformation.DataScope,
  377. CultureName = storageInformation.CultureName,
  378. TableName = storageInformation.TableName
  379. };
  380. dataScopes.Add(sqlDataTypeStoreDataScope);
  381. }
  382. result.DataScopes = dataScopes;
  383. Guid dataTypeId = element.DataTypeId;
  384. var dataTypeDescriptor = DataMetaDataFacade.GetDataTypeDescriptor(dataTypeId, true);
  385. if (dataTypeDescriptor == null)
  386. {
  387. throw NewConfigurationException(element, "Failed to get a DataTypeDescriptor by id '{0}'".FormatWith(dataTypeId));
  388. }
  389. result.DataTypeDescriptor = dataTypeDescriptor;
  390. Type interfaceType = null;
  391. try
  392. {
  393. interfaceType = dataTypes != null ? dataTypes[dataTypeId] : DataTypeTypesManager.GetDataType(dataTypeDescriptor);
  394. if (interfaceType == null)
  395. {
  396. Log.LogError(LogTitle, "The data interface type '{0}' does not exists and is not code generated. It will not be unusable", dataTypeDescriptor.TypeManagerTypeName);
  397. return result;
  398. }
  399. result.InterfaceType = interfaceType;
  400. string validationMessage;
  401. bool isValid = DataTypeValidationRegistry.Validate(interfaceType, dataTypeDescriptor, out validationMessage);
  402. if (!isValid)
  403. {
  404. Log.LogCritical(LogTitle, validationMessage);
  405. throw new InvalidOperationException(validationMessage);
  406. }
  407. Dictionary<SqlDataTypeStoreTableKey, StoreTypeInfo> fields;
  408. helperClassesGenerationInfo = EnsureNeededTypes(dataTypeDescriptor,
  409. dataScopes, allSqlDataTypeStoreDataScopes, dataContextClass,
  410. out fields, ref dataContextRecompilationNeeded, forceCompile);
  411. result.Fields = fields;
  412. return result;
  413. }
  414. catch (Exception ex)
  415. {
  416. if (interfaceType != null)
  417. {
  418. DataProviderRegistry.RegisterDataTypeInitializationError(interfaceType, ex);
  419. DataProviderRegistry.AddKnownDataType(interfaceType, _dataProviderContext.ProviderName);
  420. Log.LogError(LogTitle, "Failed initialization for the datatype {0}", dataTypeDescriptor.TypeManagerTypeName);
  421. }
  422. Log.LogError(LogTitle, ex);
  423. result.Fields = new Dictionary<SqlDataTypeStoreTableKey, StoreTypeInfo>();
  424. return result;
  425. }
  426. }
  427. private class InitializeStoreResult
  428. {
  429. public InitializeStoreResult()
  430. {
  431. TableNames = new Dictionary<SqlDataTypeStoreTableKey, string>();
  432. }
  433. public Type InterfaceType { get; set; }
  434. public SqlDataTypeStore SqlDataTypeStore { get; set; }
  435. public Dictionary<SqlDataTypeStoreTableKey, string> TableNames { get; set; }
  436. }
  437. private class InterfaceGeneratedClassesInfo
  438. {
  439. public Type InterfaceType { get; set; }
  440. public List<SqlDataTypeStoreDataScope> DataScopes { get; set; }
  441. public DataTypeDescriptor DataTypeDescriptor { get; set; }
  442. public Dictionary<SqlDataTypeStoreTableKey, StoreTypeInfo> Fields { get; set; }
  443. }
  444. private void AddDataTypeStore(InitializeStoreResult initializeStoreResult, bool doValidate = true, bool isInitialization = false)
  445. {
  446. if (initializeStoreResult.InterfaceType == null)
  447. {
  448. return;
  449. }
  450. bool isValid = initializeStoreResult.SqlDataTypeStore != null;
  451. if (isValid && doValidate)
  452. {
  453. isValid = ValidateTables(initializeStoreResult, isInitialization);
  454. }
  455. if (!isValid)
  456. {
  457. _sqlDataTypeStoresContainer.AddKnownInterface(initializeStoreResult.InterfaceType);
  458. return;
  459. }
  460. _sqlDataTypeStoresContainer.AddSupportedDataTypeStore(initializeStoreResult.InterfaceType, initializeStoreResult.SqlDataTypeStore);
  461. DataProviderRegistry.AddNewDataType(initializeStoreResult.InterfaceType, _dataProviderContext.ProviderName);
  462. }
  463. private bool ValidateTables(InitializeStoreResult initializeStoreResult, bool isInitialization)
  464. {
  465. var errors = new StringBuilder();
  466. bool isValid = true;
  467. var interfaceType = initializeStoreResult.InterfaceType;
  468. foreach (string tableName in initializeStoreResult.TableNames.Values)
  469. {
  470. bool isTableValid = ValidateTable(interfaceType, tableName, errors);
  471. if (!isTableValid) isValid = false;
  472. }
  473. if (!isValid)
  474. {
  475. DataTypeValidationRegistry.AddValidationError(initializeStoreResult.InterfaceType, _dataProviderContext.ProviderName, errors.ToString());
  476. if (isInitialization
  477. && GlobalSettingsFacade.EnableDataTypesAutoUpdate
  478. && interfaceType.IsAutoUpdateble()
  479. && !interfaceType.IsGenerated())
  480. {
  481. Log.LogInformation(LogTitle, "Data schema for the data interface '{0}' on the SqlDataProvider '{1}' is not matching and will be updated.",
  482. initializeStoreResult.InterfaceType, _dataProviderContext.ProviderName);
  483. Log.LogInformation(LogTitle, errors.ToString());
  484. }
  485. else
  486. {
  487. Log.LogCritical(LogTitle, "The data interface '{0}' will not work for the SqlDataProvider '{1}'",
  488. initializeStoreResult.InterfaceType, _dataProviderContext.ProviderName);
  489. Log.LogCritical(LogTitle, errors.ToString());
  490. }
  491. }
  492. return isValid;
  493. }
  494. private bool ValidateTable(Type interfaceType, string tableName, StringBuilder errors)
  495. {
  496. ISqlTableInformation sqlTableInformation = SqlTableInformationStore.GetTableInformation(_connectionString, tableName);
  497. if (sqlTableInformation == null)
  498. {
  499. errors.AppendLine("Table '{0}' does not exist".FormatWith(tableName));
  500. return false;
  501. }
  502. int primaryKeyCount = sqlTableInformation.ColumnInformations.Count(column => column.IsPrimaryKey);
  503. if (primaryKeyCount == 0)
  504. {
  505. errors.AppendLine(string.Format("The table '{0}' is missing a primary key", tableName));
  506. return false;
  507. }
  508. var columns = new List<SqlColumnInformation>(sqlTableInformation.ColumnInformations);
  509. var properties = interfaceType.GetPropertiesRecursively();
  510. foreach (PropertyInfo property in properties)
  511. {
  512. if (property.Name == "DataSourceId") continue;
  513. SqlColumnInformation column = columns.Find(col => col.ColumnName == property.Name);
  514. if (column == null)
  515. {
  516. errors.AppendLine(string.Format("The interface property named '{0}' does not exist in the table '{1}' as a column", property.Name, sqlTableInformation.TableName));
  517. return false;
  518. }
  519. if (!column.IsNullable || column.Type == typeof(string))
  520. {
  521. if (column.Type != property.PropertyType)
  522. {
  523. errors.AppendLine(string.Format("Type mismatch. The interface type '{0}' does not match the database type '{1}'", property.PropertyType, column.Type));
  524. return false;
  525. }
  526. }
  527. }
  528. // Updating schema from C1 4.1, to be removed in future versions.
  529. // if (typeof (ILocalizedControlled).IsAssignableFrom(interfaceType)
  530. // && !properties.Any(p => p.Name == "CultureName")
  531. // && columns.Any(c => c.ColumnName == "CultureName"))
  532. // {
  533. // Log.LogInformation(LogTitle, "Removing obsolete 'CultureName' column from table '{0}'", tableName);
  534. // string selectConstraintName = string.Format(
  535. // @"SELECT df.name 'ConstraintName'
  536. // FROM sys.default_constraints df
  537. // INNER JOIN sys.tables t ON df.parent_object_id = t.object_id
  538. // INNER JOIN sys.columns c ON df.parent_object_id = c.object_id AND df.parent_column_id = c.column_id
  539. // where t.name = '{0}'
  540. // and c.name = 'CultureName'", tableName);
  541. // var dt = ExecuteReader(selectConstraintName);
  542. // List<string> constraints = (from DataRow dr in dt.Rows select dr["ConstraintName"].ToString()).ToList();
  543. // foreach (var constrainName in constraints)
  544. // {
  545. // ExecuteSql("ALTER TABLE [{0}] DROP CONSTRAINT [{1}]".FormatWith(tableName, constrainName));
  546. // }
  547. // string sql = "ALTER TABLE [{0}] DROP COLUMN [CultureName]".FormatWith(tableName);
  548. // ExecuteSql(sql);
  549. // }
  550. return true;
  551. }
  552. //private void ExecuteSql(string sql)
  553. //{
  554. // var conn = SqlConnectionManager.GetConnection(_connectionString);
  555. // Log.LogInformation(LogTitle, sql);
  556. // using (var cmd = new SqlCommand(sql, conn))
  557. // {
  558. // cmd.ExecuteNonQuery();
  559. // }
  560. //}
  561. //private DataTable ExecuteReader(string commandText)
  562. //{
  563. // var conn = SqlConnectionManager.GetConnection(_connectionString);
  564. // using (var cmd = new SqlCommand(commandText, conn))
  565. // {
  566. // using (var dt = new DataTable())
  567. // {
  568. // using (var rdr = cmd.ExecuteReader())
  569. // {
  570. // if (rdr != null) dt.Load(rdr);
  571. // return dt;
  572. // }
  573. // }
  574. // }
  575. //}
  576. internal class StoreTypeInfo
  577. {
  578. public StoreTypeInfo(string fieldName, Type fieldType, Type sqlHelperType)
  579. {
  580. FieldName = fieldName;
  581. FieldType = fieldType;
  582. SqlHelperClass = sqlHelperType;
  583. }
  584. public string FieldName;
  585. public Type FieldType;
  586. public Type SqlHelperClass;
  587. public FieldInfo DataContextField;
  588. }
  589. /// <summary>
  590. /// Checks that tables related to specified data type included in current DataContext class, if not - compiles a new version of DataContext that contains them
  591. /// </summary>
  592. private HelperClassesGenerationInfo EnsureNeededTypes(
  593. DataTypeDescriptor dataTypeDescriptor,
  594. IEnumerable<SqlDataTypeStoreDataScope> sqlDataTypeStoreDataScopes,
  595. Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>> allSqlDataTypeStoreDataScopes,
  596. Type dataContextClassType,
  597. out Dictionary<SqlDataTypeStoreTableKey, StoreTypeInfo> fields,
  598. ref bool dataContextRecompileNeeded, bool forceCompile = false)
  599. {
  600. lock (_lock)
  601. {
  602. // Getting the interface (ensuring that it exists)
  603. Type interfaceType = DataTypeTypesManager.GetDataType(dataTypeDescriptor);
  604. var storeDataScopesToCompile = new List<SqlDataTypeStoreDataScope>();
  605. var storeDataScopesAlreadyCompiled = new List<SqlDataTypeStoreDataScope>();
  606. fields = new Dictionary<SqlDataTypeStoreTableKey, StoreTypeInfo>();
  607. foreach (SqlDataTypeStoreDataScope storeDataScope in sqlDataTypeStoreDataScopes)
  608. {
  609. string dataContextFieldName = NamesCreator.MakeDataContextFieldName(storeDataScope.TableName);
  610. FieldInfo dataContextFieldInfo = null;
  611. if (dataContextClassType != null)
  612. {
  613. dataContextFieldInfo = dataContextClassType.GetFields(BindingFlags.Public | BindingFlags.Instance)
  614. .SingleOrDefault(f => f.Name == dataContextFieldName);
  615. }
  616. string sqlDataProviderHelperClassFullName = NamesCreator.MakeSqlDataProviderHelperClassFullName(dataTypeDescriptor, storeDataScope.DataScopeName, storeDataScope.CultureName, _dataProviderContext.ProviderName);
  617. string entityClassName = NamesCreator.MakeEntityClassFullName(dataTypeDescriptor, storeDataScope.DataScopeName, storeDataScope.CultureName, _dataProviderContext.ProviderName);
  618. Type sqlDataProviderHelperClass = null, entityClass = null;
  619. try
  620. {
  621. sqlDataProviderHelperClass = TryGetGeneratedType(sqlDataProviderHelperClassFullName);
  622. entityClass = TryGetGeneratedType(entityClassName);
  623. forceCompile = forceCompile
  624. || CodeGenerationManager.IsRecompileNeeded(interfaceType, new[] { sqlDataProviderHelperClass, entityClass });
  625. }
  626. catch (TypeLoadException)
  627. {
  628. forceCompile = true;
  629. }
  630. if (!forceCompile)
  631. {
  632. var storeTypeInfo = new StoreTypeInfo(dataContextFieldName, entityClass, sqlDataProviderHelperClass)
  633. {
  634. DataContextField = dataContextFieldInfo
  635. };
  636. fields.Add(new SqlDataTypeStoreTableKey(storeDataScope.DataScopeName, storeDataScope.CultureName), storeTypeInfo);
  637. }
  638. if (dataContextFieldInfo == null)
  639. {
  640. dataContextRecompileNeeded = true;
  641. }
  642. if (forceCompile)
  643. {
  644. storeDataScopesToCompile.Add(storeDataScope);
  645. }
  646. else
  647. {
  648. storeDataScopesAlreadyCompiled.Add(storeDataScope);
  649. }
  650. }
  651. if (storeDataScopesToCompile.Any())
  652. {
  653. dataContextRecompileNeeded = true;
  654. if (!dataTypeDescriptor.IsCodeGenerated)
  655. {
  656. // Building a new descriptor so generated classes take in account field changes
  657. dataTypeDescriptor = DynamicTypeManager.BuildNewDataTypeDescriptor(interfaceType);
  658. }
  659. return CompileMissingClasses(dataTypeDescriptor, allSqlDataTypeStoreDataScopes, fields,
  660. storeDataScopesToCompile, storeDataScopesAlreadyCompiled);
  661. }
  662. }
  663. return null;
  664. }
  665. private bool TryLoadDataContextClass(Type dataContextClassType)
  666. {
  667. if (_typeLoadResults.ContainsKey(dataContextClassType))
  668. {
  669. return _typeLoadResults[dataContextClassType];
  670. }
  671. bool success = true;
  672. var connection = SqlConnectionManager.GetConnection(_connectionString);
  673. try
  674. {
  675. var dataContext = (DataContext)Activator.CreateInstance(dataContextClassType, connection);
  676. dataContext.Dispose();
  677. }
  678. catch (Exception ex)
  679. {
  680. var innerEx = ex;
  681. while (innerEx is TargetInvocationException)
  682. {
  683. innerEx = innerEx.InnerException;
  684. }
  685. if (!(innerEx is TypeLoadException || innerEx is FileNotFoundException))
  686. {
  687. throw;
  688. }
  689. Log.LogWarning(LogTitle, "Failed to load DataContext class, creating a new one.");
  690. Log.LogWarning(LogTitle, innerEx.Message);
  691. success = false;
  692. }
  693. lock (_typeLoadResults)
  694. {
  695. _typeLoadResults[dataContextClassType] = success;
  696. }
  697. return success;
  698. }
  699. private class HelperClassesGenerationInfo
  700. {
  701. public Action<CodeGenerationBuilder> GenerateCodeAction;
  702. public Action<Type[]> PopulateFieldsAction;
  703. }
  704. private HelperClassesGenerationInfo CompileMissingClasses(DataTypeDescriptor dataTypeDescriptor, Dictionary<DataTypeDescriptor,
  705. IEnumerable<SqlDataTypeStoreDataScope>> allSqlDataTypeStoreDataScopes,
  706. Dictionary<SqlDataTypeStoreTableKey, StoreTypeInfo> fields,
  707. List<SqlDataTypeStoreDataScope> storeDataScopesToCompile,
  708. List<SqlDataTypeStoreDataScope> storeDataScopesAlreadyCompiled)
  709. {
  710. return new HelperClassesGenerationInfo
  711. {
  712. GenerateCodeAction = codeGenerationBuilder =>
  713. {
  714. var sqlDataProviderCodeBuilder = new SqlDataProviderCodeBuilder(_dataProviderContext.ProviderName, codeGenerationBuilder);
  715. sqlDataProviderCodeBuilder.AddDataType(dataTypeDescriptor, storeDataScopesToCompile);
  716. sqlDataProviderCodeBuilder.AddExistingDataType(dataTypeDescriptor, storeDataScopesAlreadyCompiled);
  717. },
  718. PopulateFieldsAction = types =>
  719. {
  720. foreach (SqlDataTypeStoreDataScope storeDataScope in storeDataScopesToCompile)
  721. {
  722. string dataContextFieldName = NamesCreator.MakeDataContextFieldName(storeDataScope.TableName);
  723. string helperClassFullName = NamesCreator.MakeSqlDataProviderHelperClassFullName(
  724. dataTypeDescriptor, storeDataScope.DataScopeName, storeDataScope.CultureName, _dataProviderContext.ProviderName);
  725. Type helperClass = types.Single(f => f.FullName == helperClassFullName);
  726. string entityClassFullName = NamesCreator.MakeEntityClassFullName(
  727. dataTypeDescriptor, storeDataScope.DataScopeName, storeDataScope.CultureName, _dataProviderContext.ProviderName);
  728. Type entityClass = types.Single(f => f.FullName == entityClassFullName);
  729. var storeTableKey = new SqlDataTypeStoreTableKey(storeDataScope.DataScopeName, storeDataScope.CultureName);
  730. fields[storeTableKey] = new StoreTypeInfo(dataContextFieldName, entityClass, helperClass);
  731. }
  732. foreach (SqlDataTypeStoreDataScope storeDataScope in storeDataScopesAlreadyCompiled)
  733. {
  734. string dataContextFieldName = NamesCreator.MakeDataContextFieldName(storeDataScope.TableName);
  735. string helperClassFullName = NamesCreator.MakeSqlDataProviderHelperClassFullName(
  736. dataTypeDescriptor, storeDataScope.DataScopeName, storeDataScope.CultureName, _dataProviderContext.ProviderName);
  737. Type helperClass = TryGetGeneratedType(helperClassFullName);
  738. string entityClassFullName = NamesCreator.MakeEntityClassFullName(
  739. dataTypeDescriptor, storeDataScope.DataScopeName, storeDataScope.CultureName, _dataProviderContext.ProviderName);
  740. Type entityClass = TryGetGeneratedType(entityClassFullName);
  741. var storeTableKey = new SqlDataTypeStoreTableKey(storeDataScope.DataScopeName, storeDataScope.CultureName);
  742. fields[storeTableKey] = new StoreTypeInfo(dataContextFieldName, entityClass, helperClass);
  743. }
  744. }
  745. };
  746. }
  747. private Type TryGetGeneratedType(string typeName)
  748. {
  749. Type compiledType = CodeGenerationManager.GetCompiledType(typeName);
  750. if (compiledType != null) return compiledType;
  751. if (_compositeGeneratedAssembly != null)
  752. {
  753. Type result = _compositeGeneratedAssembly.GetType(typeName, false);
  754. if (result != null) return result;
  755. }
  756. return TypeManager.TryGetType(typeName);
  757. }
  758. /// <summary>
  759. /// This method updates the DataContextQueryableFieldInfo property on all existing store tables.
  760. /// </summary>
  761. /// <remarks>
  762. /// This is needed due to the fact that all data stores share the same
  763. /// DataContext class and the DataContext class is code generated everytime
  764. /// a new store is code generated.
  765. /// </remarks>
  766. /// <param name="newDataContextClassType"></param>
  767. private void UpdateCreatedSqlDataTypeStoreTables(Type newDataContextClassType)
  768. {
  769. foreach (SqlDataTypeStoreTable dataTypeStoreTable in _createdSqlDataTypeStoreTables)
  770. {
  771. Verify.IsNotNull(dataTypeStoreTable.DataContextQueryableFieldInfo, "Missing field info");
  772. string fieldName = dataTypeStoreTable.DataContextQueryableFieldInfo.Name;
  773. FieldInfo newFieldInfo = newDataContextClassType.GetFields(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault(f => f.Name == fieldName);
  774. if (newFieldInfo != null)
  775. {
  776. dataTypeStoreTable.DataContextQueryableFieldInfo = newFieldInfo;
  777. }
  778. else
  779. {
  780. Log.LogWarning(LogTitle, "DataContext missing field newly created field '{0}'", fieldName);
  781. }
  782. }
  783. }
  784. private Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>> BuildAllExistingDataTypeStoreDataScopes()
  785. {
  786. var allSqlDataTypeStoreDataScopes = new Dictionary<DataTypeDescriptor, IEnumerable<SqlDataTypeStoreDataScope>>();
  787. foreach (InterfaceConfigurationElement element in _interfaceConfigurationElements)
  788. {
  789. Guid dataTypeId = element.DataTypeId;
  790. var dataTypeDescriptor = DataMetaDataFacade.GetDataTypeDescriptor(dataTypeId, true);
  791. if (dataTypeDescriptor == null)
  792. {
  793. Log.LogWarning(LogTitle, "Failed to get data type descriptor by id '{0}'".FormatWith(dataTypeId));
  794. continue;
  795. }
  796. var sqlDataTypeStoreDataScopes = new List<SqlDataTypeStoreDataScope>();
  797. foreach (StorageInformation storageInformation in element.Stores)
  798. {
  799. var sqlDataTypeStoreDataScope = new SqlDataTypeStoreDataScope
  800. {
  801. DataScopeName = storageInformation.DataScope,
  802. CultureName = storageInformation.CultureName,
  803. TableName = storageInformation.TableName
  804. };
  805. sqlDataTypeStoreDataScopes.Add(sqlDataTypeStoreDataScope);
  806. }
  807. allSqlDataTypeStoreDataScopes.Add(dataTypeDescriptor, sqlDataTypeStoreDataScopes);
  808. }
  809. return allSqlDataTypeStoreDataScopes;
  810. }
  811. }
  812. }