PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/ToMigrate/Raven.Database/Storage/IndexDefinitionStorage.cs

http://github.com/ayende/ravendb
C# | 680 lines | 566 code | 104 blank | 10 comment | 65 complexity | 5e3c8eddfcb3f020ac398412906f304e MD5 | raw file
Possible License(s): GPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Apache-2.0, BSD-3-Clause, CC-BY-SA-3.0
  1. //-----------------------------------------------------------------------
  2. // <copyright file="IndexDefinitionStorage.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Collections.Generic;
  9. using System.ComponentModel;
  10. using System.Globalization;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Security.Cryptography;
  14. using System.Text;
  15. using System.Threading;
  16. using Lucene.Net.Analysis.Standard;
  17. using Lucene.Net.Documents;
  18. using Mono.CSharp;
  19. using Mono.CSharp.Linq;
  20. using Raven.Abstractions.Data;
  21. using Raven.Abstractions.Logging;
  22. using Raven.Database.Indexing.IndexMerging;
  23. using Raven.Imports.Newtonsoft.Json;
  24. using Raven.Abstractions;
  25. using Raven.Abstractions.Extensions;
  26. using Raven.Abstractions.Indexing;
  27. using Raven.Abstractions.MEF;
  28. using Raven.Database.Config;
  29. using Raven.Database.Extensions;
  30. using Raven.Database.Linq;
  31. using Raven.Database.Plugins;
  32. using Raven.Database.Indexing;
  33. namespace Raven.Database.Storage
  34. {
  35. public class IndexDefinitionStorage
  36. {
  37. private const string IndexDefDir = "IndexDefinitions";
  38. private readonly ReaderWriterLockSlim currentlyIndexingLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
  39. public long currentlyIndexing;
  40. private readonly ConcurrentDictionary<int, AbstractViewGenerator> indexCache =
  41. new ConcurrentDictionary<int, AbstractViewGenerator>();
  42. private readonly ConcurrentDictionary<int, AbstractTransformer> transformCache =
  43. new ConcurrentDictionary<int, AbstractTransformer>();
  44. private readonly ConcurrentDictionary<string, int> indexNameToId =
  45. new ConcurrentDictionary<string, int>(StringComparer.OrdinalIgnoreCase);
  46. private readonly ConcurrentDictionary<string, int> transformNameToId =
  47. new ConcurrentDictionary<string, int>(StringComparer.OrdinalIgnoreCase);
  48. private readonly ConcurrentDictionary<int, TransformerDefinition> transformDefinitions =
  49. new ConcurrentDictionary<int, TransformerDefinition>();
  50. private readonly ConcurrentDictionary<int, IndexDefinition> indexDefinitions =
  51. new ConcurrentDictionary<int, IndexDefinition>();
  52. private readonly ConcurrentDictionary<int, IndexDefinition> newDefinitionsThisSession =
  53. new ConcurrentDictionary<int, IndexDefinition>();
  54. private static readonly ILog logger = LogManager.GetCurrentClassLogger();
  55. private readonly string path;
  56. private readonly RavenConfiguration configuration;
  57. private readonly ITransactionalStorage transactionalStorage;
  58. private readonly OrderedPartCollection<AbstractDynamicCompilationExtension> extensions;
  59. [CLSCompliant(false)]
  60. public IndexDefinitionStorage(
  61. RavenConfiguration configuration,
  62. ITransactionalStorage transactionalStorage,
  63. string path,
  64. OrderedPartCollection<AbstractDynamicCompilationExtension> extensions)
  65. {
  66. this.configuration = configuration;
  67. this.transactionalStorage = transactionalStorage;
  68. this.extensions = extensions; // this is used later in the ctor, so it must appears first
  69. this.path = Path.Combine(path, IndexDefDir);
  70. if (Directory.Exists(this.path) == false && configuration.Core.RunInMemory == false)
  71. Directory.CreateDirectory(this.path);
  72. }
  73. internal Dictionary<int, DocumentDatabase.IndexFailDetails> Initialize()
  74. {
  75. Dictionary<int, DocumentDatabase.IndexFailDetails> reason = null;
  76. if (configuration.Core.RunInMemory == false)
  77. reason = ReadFromDisk();
  78. newDefinitionsThisSession.Clear();
  79. return reason;
  80. }
  81. public bool IsNewThisSession(IndexDefinition definition)
  82. {
  83. return newDefinitionsThisSession.ContainsKey(definition.IndexId);
  84. }
  85. private Dictionary<int, DocumentDatabase.IndexFailDetails> ReadFromDisk()
  86. {
  87. var indexFailReason = new Dictionary<int, DocumentDatabase.IndexFailDetails>();
  88. if (logger.IsDebugEnabled)
  89. logger.Debug("Reading index definitions from disk...");
  90. foreach (var indexDefinition in ReadIndexDefinitionsFromDisk())
  91. {
  92. try
  93. {
  94. if (Contains(indexDefinition.Name)) // checking if there are no older indexes with the same name
  95. RemoveIndexAndCleanup(indexDefinition.Name);
  96. ResolveAnalyzers(indexDefinition);
  97. AddAndCompileIndex(indexDefinition);
  98. AddIndex(indexDefinition.IndexId, indexDefinition);
  99. }
  100. catch (Exception e)
  101. {
  102. var reason = new DocumentDatabase.IndexFailDetails
  103. {
  104. IndexName = indexDefinition.Name,
  105. Reason = string.Format("Index '{0}-({1})' couldn't be compiled", indexDefinition.IndexId, indexDefinition.Name),
  106. Ex = e
  107. };
  108. indexFailReason.Add(indexDefinition.IndexId, reason);
  109. using (LogContext.WithDatabase(configuration.DatabaseName))
  110. {
  111. logger.WarnException(string.Format("Could not compile index '{0} ({1})', skipping bad index", indexDefinition.IndexId, indexDefinition.Name), e);
  112. }
  113. }
  114. }
  115. if (logger.IsDebugEnabled)
  116. {
  117. logger.Debug("Read {0} index definitions", indexDefinitions.Count);
  118. logger.Debug("Reading transformer definitions from disk...");
  119. }
  120. foreach (var transformerDefinition in ReadTransformerDefinitionsFromDisk())
  121. {
  122. try
  123. {
  124. RemoveTransformer(transformerDefinition.Name);
  125. var generator = CompileTransform(transformerDefinition);
  126. transformCache[transformerDefinition.TransfomerId] = generator;
  127. AddTransform(transformerDefinition.TransfomerId, transformerDefinition);
  128. }
  129. catch (Exception e)
  130. {
  131. logger.WarnException(string.Format("Could not compile transformer '{0} ({1})', skipping bad transformer", transformerDefinition.TransfomerId, transformerDefinition.Name), e);
  132. }
  133. }
  134. if (logger.IsDebugEnabled)
  135. logger.Debug("Read {0} transform definitions", transformDefinitions.Count);
  136. return indexFailReason;
  137. }
  138. private IEnumerable<IndexDefinition> ReadIndexDefinitionsFromDisk()
  139. {
  140. var result = new SortedList<int, IndexDefinition>();
  141. foreach (var index in Directory.GetFiles(path, "*.index"))
  142. {
  143. try
  144. {
  145. var indexDefinition = JsonConvert.DeserializeObject<IndexDefinition>(File.ReadAllText(index), Default.Converters);
  146. result.Add(indexDefinition.IndexId, indexDefinition);
  147. }
  148. catch (Exception e)
  149. {
  150. logger.WarnException("Could not compile index " + index + ", skipping bad index", e);
  151. }
  152. }
  153. return result.Values;
  154. }
  155. private IEnumerable<TransformerDefinition> ReadTransformerDefinitionsFromDisk()
  156. {
  157. var result = new SortedList<int, TransformerDefinition>();
  158. foreach (var transformer in Directory.GetFiles(path, "*.transform"))
  159. {
  160. try
  161. {
  162. var transformerDefinition = JsonConvert.DeserializeObject<TransformerDefinition>(File.ReadAllText(transformer), Default.Converters);
  163. result.Add(transformerDefinition.TransfomerId, transformerDefinition);
  164. }
  165. catch (Exception e)
  166. {
  167. logger.WarnException("Could not compile transformer " + transformer + ", skipping bad transformer", e);
  168. }
  169. }
  170. return result.Values;
  171. }
  172. private void RemoveIndexAndCleanup(string name)
  173. {
  174. var index = GetIndexDefinition(name);
  175. if (index == null)
  176. return;
  177. transactionalStorage.Batch(accessor =>
  178. {
  179. accessor.Indexing.PrepareIndexForDeletion(index.IndexId);
  180. accessor.Indexing.DeleteIndex(index.IndexId, CancellationToken.None);
  181. });
  182. RemoveIndex(index.IndexId);
  183. }
  184. public int IndexesCount
  185. {
  186. get { return indexCache.Count; }
  187. }
  188. public int ResultTransformersCount
  189. {
  190. get { return transformCache.Count; }
  191. }
  192. public string[] IndexNames
  193. {
  194. get { return IndexDefinitions.Values.OrderBy(x => x.Name).Select(x => x.Name).ToArray(); }
  195. }
  196. public int[] Indexes
  197. {
  198. get { return indexCache.Keys.OrderBy(name => name).ToArray(); }
  199. }
  200. public string IndexDefinitionsPath
  201. {
  202. get { return path; }
  203. }
  204. public string[] TransformerNames
  205. {
  206. get
  207. {
  208. return transformDefinitions.Values
  209. .Where(x => !x.Temporary)
  210. .OrderBy(x => x.Name)
  211. .Select(x => x.Name)
  212. .ToArray();
  213. }
  214. }
  215. public ConcurrentDictionary<int, IndexDefinition> IndexDefinitions
  216. {
  217. get { return indexDefinitions; }
  218. }
  219. public string CreateAndPersistIndex(IndexDefinition indexDefinition)
  220. {
  221. var transformer = AddAndCompileIndex(indexDefinition);
  222. if (configuration.Core.RunInMemory == false)
  223. {
  224. WriteIndexDefinition(indexDefinition);
  225. }
  226. return transformer.Name;
  227. }
  228. private void WriteIndexDefinition(IndexDefinition indexDefinition)
  229. {
  230. if (configuration.Core.RunInMemory)
  231. return;
  232. var indexName = System.IO.Path.Combine(path, indexDefinition.IndexId + ".index");
  233. var serializedObject = JsonConvert.SerializeObject(indexDefinition, Formatting.Indented, Default.Converters);
  234. File.WriteAllText(indexName, serializedObject);
  235. }
  236. private void WriteTransformerDefinition(TransformerDefinition transformerDefinition)
  237. {
  238. if (configuration.Core.RunInMemory)
  239. return;
  240. string transformerFileName = System.IO.Path.Combine(path, transformerDefinition.TransfomerId + ".transform");
  241. File.WriteAllText(transformerFileName,
  242. JsonConvert.SerializeObject(transformerDefinition, Formatting.Indented, Default.Converters));
  243. }
  244. [CLSCompliant(false)]
  245. public string CreateTransform(TransformerDefinition transformerDefinition, AbstractTransformer transformer)
  246. {
  247. transformCache.AddOrUpdate(transformerDefinition.TransfomerId, transformer, (s, viewGenerator) => transformer);
  248. return transformer.Name;
  249. }
  250. [CLSCompliant(false)]
  251. public void PersistTransform(TransformerDefinition transformerDefinition)
  252. {
  253. if (configuration.Core.RunInMemory == false)
  254. {
  255. WriteTransformerDefinition(transformerDefinition);
  256. }
  257. }
  258. public void UpdateIndexDefinitionWithoutUpdatingCompiledIndex(IndexDefinition definition)
  259. {
  260. IndexDefinitions.AddOrUpdate(definition.IndexId, s =>
  261. {
  262. throw new InvalidOperationException(
  263. "Cannot find index named: " +
  264. definition.IndexId);
  265. }, (s, indexDefinition) => definition);
  266. WriteIndexDefinition(definition);
  267. }
  268. private DynamicViewCompiler AddAndCompileIndex(IndexDefinition indexDefinition)
  269. {
  270. var transformer = new DynamicViewCompiler(indexDefinition.Name, indexDefinition, extensions, path,
  271. configuration);
  272. var generator = transformer.GenerateInstance();
  273. indexCache.AddOrUpdate(indexDefinition.IndexId, generator, (s, viewGenerator) => generator);
  274. logger.Info("New index {0}:\r\n{1}\r\nCompiled to:\r\n{2}", transformer.Name, transformer.CompiledQueryText,
  275. transformer.CompiledQueryText);
  276. return transformer;
  277. }
  278. [CLSCompliant(false)]
  279. public AbstractTransformer CompileTransform(TransformerDefinition transformerDefinition)
  280. {
  281. var transformer = new DynamicTransformerCompiler(transformerDefinition, configuration, extensions,
  282. transformerDefinition.Name, path);
  283. var generator = transformer.GenerateInstance();
  284. logger.Info("New transformer {0}:\r\n{1}\r\nCompiled to:\r\n{2}", transformer.Name,
  285. transformer.CompiledQueryText,
  286. transformer.CompiledQueryText);
  287. return generator;
  288. }
  289. public void RegisterNewIndexInThisSession(string name, IndexDefinition definition)
  290. {
  291. newDefinitionsThisSession.TryAdd(definition.IndexId, definition);
  292. }
  293. public void AddIndex(int id, IndexDefinition definition)
  294. {
  295. IndexDefinitions.AddOrUpdate(id, definition, (s1, def) =>
  296. {
  297. if (def.IsCompiled)
  298. throw new InvalidOperationException("Index " + id + " is a compiled index, and cannot be replaced");
  299. return definition;
  300. });
  301. indexNameToId[definition.Name] = id;
  302. UpdateIndexMappingFile();
  303. }
  304. public void AddTransform(int id, TransformerDefinition definition)
  305. {
  306. transformDefinitions.AddOrUpdate(id, definition, (s1, def) => definition);
  307. transformNameToId[definition.Name] = id;
  308. UpdateTransformerMappingFile();
  309. }
  310. public void RemoveIndex(int id, bool removeByNameMapping = true)
  311. {
  312. AbstractViewGenerator ignoredViewGenerator;
  313. int ignoredId;
  314. if (indexCache.TryRemove(id, out ignoredViewGenerator) && removeByNameMapping)
  315. indexNameToId.TryRemove(ignoredViewGenerator.Name, out ignoredId);
  316. IndexDefinition ignoredIndexDefinition;
  317. IndexDefinitions.TryRemove(id, out ignoredIndexDefinition);
  318. newDefinitionsThisSession.TryRemove(id, out ignoredIndexDefinition);
  319. if (configuration.Core.RunInMemory)
  320. return;
  321. File.Delete(GetIndexSourcePath(id) + ".index");
  322. UpdateIndexMappingFile();
  323. }
  324. private string GetIndexSourcePath(int id)
  325. {
  326. return System.IO.Path.Combine(path, id.ToString());
  327. }
  328. public IndexDefinition GetIndexDefinition(string name)
  329. {
  330. int id = 0;
  331. if (indexNameToId.TryGetValue(name, out id))
  332. {
  333. IndexDefinition indexDefinition = IndexDefinitions[id];
  334. if (indexDefinition.Fields.Count == 0)
  335. {
  336. AbstractViewGenerator abstractViewGenerator = GetViewGenerator(id);
  337. indexDefinition.Fields = abstractViewGenerator.Fields;
  338. }
  339. return indexDefinition;
  340. }
  341. return null;
  342. }
  343. public IndexDefinition GetIndexDefinition(int id)
  344. {
  345. IndexDefinition value;
  346. IndexDefinitions.TryGetValue(id, out value);
  347. return value;
  348. }
  349. public TransformerDefinition GetTransformerDefinition(string name)
  350. {
  351. int id;
  352. if (transformNameToId.TryGetValue(name, out id))
  353. return transformDefinitions[id];
  354. return null;
  355. }
  356. public IEnumerable<TransformerDefinition> GetAllTransformerDefinitions()
  357. {
  358. return transformDefinitions.Select(definition => definition.Value);
  359. }
  360. public IndexMergeResults ProposeIndexMergeSuggestions()
  361. {
  362. var indexMerger = new IndexMerger(IndexDefinitions.ToDictionary(x => x.Key, x => x.Value));
  363. return indexMerger.ProposeIndexMergeSuggestions();
  364. }
  365. public TransformerDefinition GetTransformerDefinition(int id)
  366. {
  367. TransformerDefinition value;
  368. transformDefinitions.TryGetValue(id, out value);
  369. return value;
  370. }
  371. [CLSCompliant(false)]
  372. public AbstractViewGenerator GetViewGenerator(string name)
  373. {
  374. int id = 0;
  375. if (indexNameToId.TryGetValue(name, out id))
  376. return indexCache[id];
  377. return null;
  378. }
  379. [CLSCompliant(false)]
  380. public AbstractViewGenerator GetViewGenerator(int id)
  381. {
  382. AbstractViewGenerator value;
  383. if (indexCache.TryGetValue(id, out value) == false)
  384. return null;
  385. return value;
  386. }
  387. public IndexCreationOptions FindIndexCreationOptions(IndexDefinition newIndexDef)
  388. {
  389. var currentIndexDefinition = GetIndexDefinition(newIndexDef.Name);
  390. if (currentIndexDefinition == null)
  391. {
  392. if (CheckIfIndexIsBeenDeleted(newIndexDef))
  393. {
  394. //index is been deleted ignoring this index
  395. return IndexCreationOptions.Noop;
  396. }
  397. if (newIndexDef.IndexVersion == null)
  398. newIndexDef.IndexVersion = 0;
  399. return IndexCreationOptions.Create;
  400. }
  401. if (newIndexDef.IndexVersion == null)
  402. newIndexDef.IndexVersion = currentIndexDefinition.IndexVersion + 1;
  403. if (currentIndexDefinition.IsTestIndex) // always update test indexes
  404. return IndexCreationOptions.Update;
  405. newIndexDef.IndexId = currentIndexDefinition.IndexId;
  406. var result = currentIndexDefinition.Equals(newIndexDef);
  407. if (result)
  408. return IndexCreationOptions.Noop;
  409. // try to compare to find changes which doesn't require removing compiled index
  410. return currentIndexDefinition.Equals(newIndexDef, ignoreFormatting: true, ignoreMaxIndexOutput: true)
  411. ? IndexCreationOptions.UpdateWithoutUpdatingCompiledIndex : IndexCreationOptions.Update;
  412. }
  413. private bool CheckIfIndexIsBeenDeleted(IndexDefinition definition)
  414. {
  415. return CheckIfIndexVersionIsEqualOrSmaller(definition, Constants.RavenReplicationIndexesTombstones)
  416. || CheckIfIndexVersionIsEqualOrSmaller(definition, "Raven/Indexes/PendingDeletion");
  417. }
  418. private bool CheckIfIndexVersionIsEqualOrSmaller(IndexDefinition definition, string listName)
  419. {
  420. bool res = false;
  421. if (definition.IndexVersion == null) return false;
  422. transactionalStorage.Batch(action =>
  423. {
  424. var li = action.Lists.Read(listName, definition.Name);
  425. if (li == null) return;
  426. int version;
  427. string versionStr = li.Data.Value<string>("IndexVersion");
  428. // The index that we are trying to add is deleted
  429. if (int.TryParse(versionStr, out version))
  430. {
  431. if (version >= definition.IndexVersion.Value)
  432. {
  433. if (version > definition.IndexVersion.Value)
  434. logger.Error("Trying to add an index ({0}) with a version smaller than the deleted version, this should not happen", definition.Name);
  435. res = true;
  436. }
  437. }
  438. else
  439. {
  440. logger.Error("Failed to parse index version of index {0}", definition.Name);
  441. }
  442. });
  443. return res;
  444. }
  445. public bool Contains(string indexName)
  446. {
  447. return indexNameToId.ContainsKey(indexName);
  448. }
  449. public static void ResolveAnalyzers(IndexDefinition indexDefinition)
  450. {
  451. // Stick Lucene.Net's namespace to all analyzer aliases that are missing a namespace
  452. var analyzerNames = (from analyzer in indexDefinition.Analyzers
  453. where analyzer.Value.IndexOf('.') == -1
  454. select analyzer).ToArray();
  455. // Only do this for analyzer that actually exist; we do this here to be able to throw a correct error later on
  456. foreach (
  457. var a in
  458. analyzerNames.Where(
  459. a => typeof(StandardAnalyzer).Assembly.GetType("Lucene.Net.Analysis." + a.Value) != null))
  460. {
  461. indexDefinition.Analyzers[a.Key] = "Lucene.Net.Analysis." + a.Value;
  462. }
  463. }
  464. public IDisposable TryRemoveIndexContext()
  465. {
  466. if (currentlyIndexingLock.TryEnterWriteLock(TimeSpan.FromSeconds(60)) == false)
  467. throw new InvalidOperationException(
  468. "Cannot modify indexes while indexing is in progress (already waited full minute). Try again later");
  469. return new DisposableAction(currentlyIndexingLock.ExitWriteLock);
  470. }
  471. public bool IsCurrentlyIndexing()
  472. {
  473. return Interlocked.Read(ref currentlyIndexing) != 0;
  474. }
  475. [CLSCompliant(false)]
  476. public IDisposable CurrentlyIndexing()
  477. {
  478. currentlyIndexingLock.EnterReadLock();
  479. Interlocked.Increment(ref currentlyIndexing);
  480. return new DisposableAction(() =>
  481. {
  482. currentlyIndexingLock.ExitReadLock();
  483. Interlocked.Decrement(ref currentlyIndexing);
  484. });
  485. }
  486. public bool RemoveTransformer(string name)
  487. {
  488. var transformer = GetTransformerDefinition(name);
  489. if (transformer == null)
  490. return false;
  491. RemoveTransformer(transformer.TransfomerId);
  492. return true;
  493. }
  494. public void RemoveIndex(string name)
  495. {
  496. var index = GetIndexDefinition(name);
  497. if (index == null) return;
  498. RemoveIndex(index.IndexId);
  499. }
  500. public void RemoveTransformer(int id)
  501. {
  502. AbstractTransformer ignoredViewGenerator;
  503. int ignoredId;
  504. if (transformCache.TryRemove(id, out ignoredViewGenerator))
  505. transformNameToId.TryRemove(ignoredViewGenerator.Name, out ignoredId);
  506. TransformerDefinition ignoredIndexDefinition;
  507. transformDefinitions.TryRemove(id, out ignoredIndexDefinition);
  508. if (configuration.Core.RunInMemory)
  509. return;
  510. File.Delete(GetIndexSourcePath(id) + ".transform");
  511. UpdateTransformerMappingFile();
  512. }
  513. [CLSCompliant(false)]
  514. public AbstractTransformer GetTransformer(int id)
  515. {
  516. AbstractTransformer value;
  517. if (transformCache.TryGetValue(id, out value) == false)
  518. return null;
  519. return value;
  520. }
  521. [CLSCompliant(false)]
  522. public AbstractTransformer GetTransformer(string name)
  523. {
  524. int id = 0;
  525. if (transformNameToId.TryGetValue(name, out id))
  526. return transformCache[id];
  527. return null;
  528. }
  529. internal bool ReplaceIndex(string indexName, string indexToSwapName)
  530. {
  531. var index = GetIndexDefinition(indexName);
  532. if (index == null)
  533. return false;
  534. int _;
  535. indexNameToId.TryRemove(index.Name, out _);
  536. index.IsSideBySideIndex = false;
  537. var indexToReplace = GetIndexDefinition(indexToSwapName);
  538. index.Name = indexToReplace != null ? indexToReplace.Name : indexToSwapName;
  539. CreateAndPersistIndex(index);
  540. AddIndex(index.IndexId, index);
  541. return true;
  542. }
  543. private void UpdateIndexMappingFile()
  544. {
  545. if (configuration.Core.RunInMemory)
  546. return;
  547. var sb = new StringBuilder();
  548. foreach (var index in indexNameToId)
  549. {
  550. sb.Append(string.Format("{0} - {1}{2}", index.Value, index.Key, Environment.NewLine));
  551. }
  552. File.WriteAllText(System.IO.Path.Combine(path, "indexes.txt"), sb.ToString());
  553. }
  554. private void UpdateTransformerMappingFile()
  555. {
  556. if (configuration.Core.RunInMemory)
  557. return;
  558. var sb = new StringBuilder();
  559. foreach (var transform in transformNameToId)
  560. {
  561. sb.Append(string.Format("{0} - {1}{2}", transform.Value, transform.Key, Environment.NewLine));
  562. }
  563. File.WriteAllText(System.IO.Path.Combine(path, "transformers.txt"), sb.ToString());
  564. }
  565. public void UpdateTransformerDefinitionWithoutUpdatingCompiledTransformer(TransformerDefinition definition)
  566. {
  567. transformDefinitions.AddOrUpdate(definition.TransfomerId, s =>
  568. {
  569. throw new InvalidOperationException(
  570. "Cannot find transformer named: " +
  571. definition.TransfomerId);
  572. }, (s, transformerDefinition) => definition);
  573. WriteTransformerDefinition(definition);
  574. }
  575. }
  576. }