PageRenderTime 65ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/Signum.Engine/Schema/Schema.cs

https://github.com/mutharasank/framework
C# | 1000 lines | 767 code | 223 blank | 10 comment | 124 complexity | 1cb8fefd53431afb4ca8f63604e3e808 MD5 | raw file
Possible License(s): GPL-3.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Reflection;
  6. using Signum.Entities;
  7. using Signum.Utilities;
  8. using System.Globalization;
  9. using System.Diagnostics;
  10. using Signum.Utilities.ExpressionTrees;
  11. using Signum.Utilities.DataStructures;
  12. using System.Threading;
  13. using System.Linq.Expressions;
  14. using Signum.Entities.Reflection;
  15. using System.Collections;
  16. using Signum.Utilities.Reflection;
  17. using Signum.Engine.Linq;
  18. using Signum.Entities.DynamicQuery;
  19. using Signum.Engine.DynamicQuery;
  20. using Signum.Entities.Basics;
  21. using Signum.Engine.Basics;
  22. namespace Signum.Engine.Maps
  23. {
  24. public class Schema : IImplementationsFinder
  25. {
  26. public CultureInfo ForceCultureInfo { get; set; }
  27. public TimeZoneMode TimeZoneMode { get; set; }
  28. Version version;
  29. public Version Version
  30. {
  31. get
  32. {
  33. if (version == null)
  34. throw new InvalidOperationException("Schema.Version is not set");
  35. return version;
  36. }
  37. set { this.version = value; }
  38. }
  39. string applicationName;
  40. public string ApplicationName
  41. {
  42. get { return applicationName ?? (applicationName = AppDomain.CurrentDomain.FriendlyName); }
  43. set { applicationName = value; }
  44. }
  45. public SchemaSettings Settings { get; private set; }
  46. public SchemaAssets Assets { get; private set; }
  47. Dictionary<Type, Table> tables = new Dictionary<Type, Table>();
  48. public Dictionary<Type, Table> Tables
  49. {
  50. get { return tables; }
  51. }
  52. const string errorType = "TypeDN table not cached. Remember to call Schema.Current.Initialize";
  53. #region Events
  54. public event Func<Type, string> IsAllowedCallback;
  55. public string IsAllowed(Type type)
  56. {
  57. if (IsAllowedCallback != null)
  58. foreach (Func<Type, string> f in IsAllowedCallback.GetInvocationList())
  59. {
  60. string result = f(type);
  61. if (result != null)
  62. return result;
  63. }
  64. return null;
  65. }
  66. internal Dictionary<string, Type> NameToType = new Dictionary<string, Type>();
  67. internal Dictionary<Type, string> TypeToName = new Dictionary<Type, string>();
  68. internal ResetLazy<TypeCaches> typeCachesLazy;
  69. public void AssertAllowed(Type type)
  70. {
  71. string error = IsAllowed(type);
  72. if (error != null)
  73. throw new UnauthorizedAccessException(EngineMessage.UnauthorizedAccessTo0Because1.NiceToString().Formato(type.NiceName(), error));
  74. }
  75. readonly IEntityEvents entityEventsGlobal = new EntityEvents<IdentifiableEntity>();
  76. public EntityEvents<IdentifiableEntity> EntityEventsGlobal
  77. {
  78. get { return (EntityEvents<IdentifiableEntity>)entityEventsGlobal; }
  79. }
  80. Dictionary<Type, IEntityEvents> entityEvents = new Dictionary<Type, IEntityEvents>();
  81. public EntityEvents<T> EntityEvents<T>()
  82. where T : IdentifiableEntity
  83. {
  84. return (EntityEvents<T>)entityEvents.GetOrCreate(typeof(T), () => new EntityEvents<T>());
  85. }
  86. internal void OnPreSaving(IdentifiableEntity entity, ref bool graphModified)
  87. {
  88. AssertAllowed(entity.GetType());
  89. IEntityEvents ee = entityEvents.TryGetC(entity.GetType());
  90. if (ee != null)
  91. ee.OnPreSaving(entity, ref graphModified);
  92. entityEventsGlobal.OnPreSaving(entity, ref graphModified);
  93. }
  94. internal void OnSaving(IdentifiableEntity entity)
  95. {
  96. AssertAllowed(entity.GetType());
  97. IEntityEvents ee = entityEvents.TryGetC(entity.GetType());
  98. if (ee != null)
  99. ee.OnSaving(entity);
  100. entityEventsGlobal.OnSaving(entity);
  101. }
  102. internal void OnRetrieved(IdentifiableEntity entity)
  103. {
  104. AssertAllowed(entity.GetType());
  105. IEntityEvents ee = entityEvents.TryGetC(entity.GetType());
  106. if (ee != null)
  107. ee.OnRetrieved(entity);
  108. entityEventsGlobal.OnRetrieved(entity);
  109. }
  110. internal void OnPreUnsafeDelete<T>(IQueryable<T> entityQuery) where T : IdentifiableEntity
  111. {
  112. AssertAllowed(typeof(T));
  113. EntityEvents<T> ee = (EntityEvents<T>)entityEvents.TryGetC(typeof(T));
  114. if (ee != null)
  115. ee.OnPreUnsafeDelete(entityQuery);
  116. }
  117. internal void OnPreUnsafeMListDelete<T>(IQueryable mlistQuery, IQueryable<T> entityQuery) where T : IdentifiableEntity
  118. {
  119. AssertAllowed(typeof(T));
  120. EntityEvents<T> ee = (EntityEvents<T>)entityEvents.TryGetC(typeof(T));
  121. if (ee != null)
  122. ee.OnPreUnsafeMListDelete(mlistQuery, entityQuery);
  123. }
  124. internal void OnPreUnsafeUpdate(IUpdateable update)
  125. {
  126. var type = update.EntityType;
  127. if (type.IsInstantiationOf(typeof(MListElement<,>)))
  128. type = type.GetGenericArguments().First();
  129. AssertAllowed(type);
  130. var ee = entityEvents.TryGetC(type);
  131. if (ee != null)
  132. ee.OnPreUnsafeUpdate(update);
  133. }
  134. internal void OnPreUnsafeInsert(Type type, IQueryable query, LambdaExpression constructor, IQueryable entityQuery)
  135. {
  136. AssertAllowed(type);
  137. var ee = entityEvents.TryGetC(type);
  138. if (ee != null)
  139. ee.OnPreUnsafeInsert(query, constructor, entityQuery);
  140. }
  141. public ICacheController CacheController(Type type)
  142. {
  143. IEntityEvents ee = entityEvents.TryGetC(type);
  144. if (ee == null)
  145. return null;
  146. return ee.CacheController;
  147. }
  148. internal CacheControllerBase<T> CacheController<T>() where T : IdentifiableEntity
  149. {
  150. EntityEvents<T> ee = (EntityEvents<T>)entityEvents.TryGetC(typeof(T));
  151. if (ee == null)
  152. return null;
  153. return ee.CacheController;
  154. }
  155. internal IQueryable<T> OnFilterQuery<T>(IQueryable<T> query)
  156. where T : IdentifiableEntity
  157. {
  158. AssertAllowed(typeof(T));
  159. EntityEvents<T> ee = (EntityEvents<T>)entityEvents.TryGetC(typeof(T));
  160. if (ee == null)
  161. return query;
  162. return ee.OnFilterQuery(query);
  163. }
  164. internal bool HasQueryFilter(Type type)
  165. {
  166. IEntityEvents ee = entityEvents.TryGetC(type);
  167. if (ee == null)
  168. return false;
  169. return ee.HasQueryFilter;
  170. }
  171. public event Func<Replacements, SqlPreCommand> Synchronizing;
  172. internal SqlPreCommand SynchronizationScript(string databaseName, bool interactive = true)
  173. {
  174. if (Synchronizing == null)
  175. return null;
  176. using (Sync.ChangeBothCultures(ForceCultureInfo))
  177. using (ExecutionMode.Global())
  178. {
  179. Replacements replacements = new Replacements() { Interactive = interactive };
  180. SqlPreCommand command = Synchronizing
  181. .GetInvocationList()
  182. .Cast<Func<Replacements, SqlPreCommand>>()
  183. .Select(e =>
  184. {
  185. try
  186. {
  187. return e(replacements);
  188. }
  189. catch (Exception ex)
  190. {
  191. return new SqlPreCommandSimple("Exception on {0}.{1}: {2}".Formato(e.Method.DeclaringType.Name, e.Method.Name, ex.Message));
  192. }
  193. })
  194. .Combine(Spacing.Triple);
  195. if (command == null)
  196. return null;
  197. var replacementsComment = replacements.Interactive ? null : replacements.Select(r =>
  198. SqlPreCommandConcat.Combine(Spacing.Double, new SqlPreCommandSimple("-- Replacements on {0}".Formato(r.Key)),
  199. r.Value.Select(a => new SqlPreCommandSimple("-- {0} -> {1}".Formato(a.Key, a.Value))).Combine(Spacing.Simple)));
  200. return SqlPreCommand.Combine(Spacing.Double,
  201. new SqlPreCommandSimple(SynchronizerMessage.StartOfSyncScriptGeneratedOn0.NiceToString().Formato(DateTime.Now)),
  202. new SqlPreCommandSimple("use {0}".Formato(databaseName)),
  203. command,
  204. new SqlPreCommandSimple(SynchronizerMessage.EndOfSyncScript.NiceToString()));
  205. }
  206. }
  207. public event Func<SqlPreCommand> Generating;
  208. internal SqlPreCommand GenerationScipt()
  209. {
  210. if (Generating == null)
  211. return null;
  212. using (Sync.ChangeBothCultures(ForceCultureInfo))
  213. using (ExecutionMode.Global())
  214. {
  215. return Generating
  216. .GetInvocationList()
  217. .Cast<Func<SqlPreCommand>>()
  218. .Select(e => e())
  219. .Combine(Spacing.Triple);
  220. }
  221. }
  222. public class InitEventDictionary
  223. {
  224. Dictionary<InitLevel, Action> dict = new Dictionary<InitLevel, Action>();
  225. InitLevel? initLevel;
  226. public Action this[InitLevel level]
  227. {
  228. get { return dict.TryGetC(level); }
  229. set
  230. {
  231. int current = dict.TryGetC(level).Try(d => d.GetInvocationList().Length) ?? 0;
  232. int @new = value.Try(d => d.GetInvocationList().Length) ?? 0;
  233. if (Math.Abs(current - @new) > 1)
  234. throw new InvalidOperationException("add or remove just one event handler each time");
  235. dict[level] = value;
  236. }
  237. }
  238. public void InitializeUntil(InitLevel topLevel)
  239. {
  240. for (InitLevel current = initLevel + 1 ?? InitLevel.Level0SyncEntities; current <= topLevel; current++)
  241. {
  242. InitializeJust(current);
  243. initLevel = current;
  244. }
  245. }
  246. void InitializeJust(InitLevel currentLevel)
  247. {
  248. using (HeavyProfiler.Log("InitializeJuts", () => currentLevel.ToString()))
  249. {
  250. Action h = dict.TryGetC(currentLevel);
  251. if (h == null)
  252. return;
  253. var handlers = h.GetInvocationList().Cast<Action>();
  254. foreach (Action handler in handlers)
  255. {
  256. using (HeavyProfiler.Log("InitAction", () => "{0}.{1}".Formato(handler.Method.DeclaringType.TypeName(), handler.Method.MethodName())))
  257. {
  258. handler();
  259. }
  260. }
  261. }
  262. }
  263. public override string ToString()
  264. {
  265. return dict.OrderBy(a => a.Key)
  266. .ToString(a => "{0} -> \r\n{1}".Formato(
  267. a.Key,
  268. a.Value.GetInvocationList().Select(h => h.Method).ToString(mi => "\t{0}.{1}".Formato(
  269. mi.DeclaringType.TypeName(),
  270. mi.MethodName()),
  271. "\r\n")
  272. ), "\r\n\r\n");
  273. }
  274. }
  275. public InitEventDictionary Initializing = new InitEventDictionary();
  276. public void Initialize()
  277. {
  278. using (ExecutionMode.Global())
  279. Initializing.InitializeUntil(InitLevel.Level4BackgroundProcesses);
  280. }
  281. public void InitializeUntil(InitLevel level)
  282. {
  283. using (ExecutionMode.Global())
  284. Initializing.InitializeUntil(level);
  285. }
  286. #endregion
  287. static Schema()
  288. {
  289. PropertyRoute.SetFindImplementationsCallback(pr => Schema.Current.FindImplementations(pr));
  290. }
  291. internal Schema(SchemaSettings settings)
  292. {
  293. this.Settings = settings;
  294. this.Assets = new SchemaAssets();
  295. Generating += SchemaGenerator.CreateSchemasScript;
  296. Generating += SchemaGenerator.CreateTablesScript;
  297. Generating += SchemaGenerator.InsertEnumValuesScript;
  298. Generating += TypeLogic.Schema_Generating;
  299. Generating += Assets.Schema_Generating;
  300. Synchronizing += SchemaSynchronizer.SnapshotIsolation;
  301. Synchronizing += SchemaSynchronizer.SynchronizeSchemasScript;
  302. Synchronizing += SchemaSynchronizer.SynchronizeTablesScript;
  303. Synchronizing += TypeLogic.Schema_Synchronizing;
  304. Synchronizing += Assets.Schema_Synchronizing;
  305. }
  306. public static Schema Current
  307. {
  308. get { return Connector.Current.Schema; }
  309. }
  310. public Table Table<T>() where T : IdentifiableEntity
  311. {
  312. return Table(typeof(T));
  313. }
  314. public Table Table(Type type)
  315. {
  316. return Tables.GetOrThrow(type, "Table {0} not loaded in schema");
  317. }
  318. internal static Field FindField(IFieldFinder fieldFinder, MemberInfo[] members)
  319. {
  320. IFieldFinder current = fieldFinder;
  321. Field result = null;
  322. foreach (var mi in members)
  323. {
  324. if (current == null)
  325. throw new InvalidOperationException("{0} does not implement {1}".Formato(result, typeof(IFieldFinder).Name));
  326. result = current.GetField(mi);
  327. current = result as IFieldFinder;
  328. }
  329. return result;
  330. }
  331. internal static Field TryFindField(IFieldFinder fieldFinder, MemberInfo[] members)
  332. {
  333. IFieldFinder current = fieldFinder;
  334. Field result = null;
  335. foreach (var mi in members)
  336. {
  337. if (current == null)
  338. return null;
  339. result = current.TryGetField(mi);
  340. if (result == null)
  341. return null;
  342. current = result as IFieldFinder;
  343. }
  344. return result;
  345. }
  346. public Dictionary<PropertyRoute, Implementations> FindAllImplementations(Type root)
  347. {
  348. try
  349. {
  350. if (!Tables.ContainsKey(root))
  351. return null;
  352. var table = Table(root);
  353. return PropertyRoute.GenerateRoutes(root)
  354. .Select(r => r.Type.IsMList() ? r.Add("Item") : r)
  355. .Where(r => r.Type.CleanType().IsIIdentifiable())
  356. .ToDictionary(r => r, r => FindImplementations(r));
  357. }
  358. catch (Exception e)
  359. {
  360. e.Data["rootType"] = root.TypeName();
  361. throw;
  362. }
  363. }
  364. public Implementations FindImplementations(PropertyRoute route)
  365. {
  366. if (route.PropertyRouteType == PropertyRouteType.LiteEntity)
  367. route = route.Parent;
  368. Type type = route.RootType;
  369. if (!Tables.ContainsKey(type))
  370. return Implementations.By(route.Type.CleanType());
  371. Field field = TryFindField(Table(type), route.Members);
  372. //if (field == null)
  373. // return Implementations.ByAll;
  374. FieldReference refField = field as FieldReference;
  375. if (refField != null)
  376. return Implementations.By(refField.FieldType.CleanType());
  377. FieldImplementedBy ibField = field as FieldImplementedBy;
  378. if (ibField != null)
  379. return Implementations.By(ibField.ImplementationColumns.Keys.ToArray());
  380. FieldImplementedByAll ibaField = field as FieldImplementedByAll;
  381. if (ibaField != null)
  382. return Implementations.ByAll;
  383. Implementations? implementations = CalculateExpressionImplementations(route);
  384. if (implementations != null)
  385. return implementations.Value;
  386. var ss = Schema.Current.Settings;
  387. if (route.Follow(r => r.Parent)
  388. .TakeWhile(t => t.PropertyRouteType != PropertyRouteType.Root)
  389. .SelectMany(r => ss.FieldAttributes(r))
  390. .Any(a => a is IgnoreAttribute))
  391. {
  392. var atts = ss.FieldAttributes(route);
  393. return Implementations.TryFromAttributes(route.Type.CleanType(), atts, route) ?? Implementations.By();
  394. }
  395. throw new InvalidOperationException("Impossible to determine implementations for {0}".Formato(route, typeof(IIdentifiable).Name));
  396. }
  397. private Implementations? CalculateExpressionImplementations(PropertyRoute route)
  398. {
  399. if (route.PropertyRouteType != PropertyRouteType.FieldOrProperty)
  400. return null;
  401. var lambda = ExpressionCleaner.GetFieldExpansion(route.Parent.Type, route.PropertyInfo);
  402. if (lambda == null)
  403. return null;
  404. Expression e = MetadataVisitor.JustVisit(lambda, new MetaExpression(route.Parent.Type, new CleanMeta(route.Parent.TryGetImplementations(), new[] { route.Parent })));
  405. MetaExpression me = e as MetaExpression;
  406. if (me == null)
  407. return null;
  408. return me.Meta.Implementations;
  409. }
  410. /// <summary>
  411. /// Uses a lambda navigate in a strongly-typed way, you can acces field using the property and collections using Single().
  412. /// </summary>
  413. public Field Field<T, V>(Expression<Func<T, V>> lambdaToField)
  414. where T : IdentifiableEntity
  415. {
  416. return FindField(Table(typeof(T)), Reflector.GetMemberList(lambdaToField));
  417. }
  418. public override string ToString()
  419. {
  420. return "Schema ( tables: {0} )".Formato(tables.Count);
  421. }
  422. public IEnumerable<ITable> GetDatabaseTables()
  423. {
  424. foreach (var table in Schema.Current.Tables.Values)
  425. {
  426. yield return table;
  427. foreach (var subTable in table.RelationalTables().Cast<ITable>())
  428. yield return subTable;
  429. }
  430. }
  431. public List<DatabaseName> DatabaseNames()
  432. {
  433. return GetDatabaseTables().Select(a => a.Name.Schema.Try(s => s.Database)).Distinct().ToList();
  434. }
  435. public DirectedEdgedGraph<Table, RelationInfo> ToDirectedGraph()
  436. {
  437. return DirectedEdgedGraph<Table, RelationInfo>.Generate(Tables.Values, t => t.DependentTables());
  438. }
  439. public Type GetType(int id)
  440. {
  441. return typeCachesLazy.Value.IdToType[id];
  442. }
  443. }
  444. public class RelationInfo
  445. {
  446. public bool IsLite { get; set; }
  447. public bool IsNullable { get; set; }
  448. public bool IsCollection { get; set; }
  449. public bool IsEnum { get; set; }
  450. }
  451. internal interface IEntityEvents
  452. {
  453. void OnPreSaving(IdentifiableEntity entity, ref bool graphModified);
  454. void OnSaving(IdentifiableEntity entity);
  455. void OnRetrieved(IdentifiableEntity entity);
  456. void OnPreUnsafeUpdate(IUpdateable update);
  457. void OnPreUnsafeInsert(IQueryable query, LambdaExpression constructor, IQueryable entityQuery);
  458. ICacheController CacheController { get; }
  459. bool HasQueryFilter { get; }
  460. }
  461. public interface ICacheController
  462. {
  463. bool Enabled { get; }
  464. void Load();
  465. IEnumerable<int> GetAllIds();
  466. void Complete(IdentifiableEntity entity, IRetriever retriver);
  467. string GetToString(int id);
  468. }
  469. public class InvalidateEventArgs : EventArgs { }
  470. public class InvaludateEventArgs : EventArgs { }
  471. public abstract class CacheControllerBase<T> : ICacheController
  472. where T : IdentifiableEntity
  473. {
  474. public abstract bool Enabled { get; }
  475. public abstract void Load();
  476. public abstract IEnumerable<int> GetAllIds();
  477. void ICacheController.Complete(IdentifiableEntity entity, IRetriever retriver)
  478. {
  479. Complete((T)entity, retriver);
  480. }
  481. public abstract void Complete(T entity, IRetriever retriver);
  482. public abstract string GetToString(int id);
  483. }
  484. public class EntityEvents<T> : IEntityEvents
  485. where T : IdentifiableEntity
  486. {
  487. public event PreSavingEventHandler<T> PreSaving;
  488. public event SavingEventHandler<T> Saving;
  489. public event RetrievedEventHandler<T> Retrieved;
  490. public CacheControllerBase<T> CacheController { get; set; }
  491. public event FilterQueryEventHandler<T> FilterQuery;
  492. public event DeleteHandler<T> PreUnsafeDelete;
  493. public event DeleteMlistHandler<T> PreUnsafeMListDelete;
  494. public event UpdateHandler<T> PreUnsafeUpdate;
  495. public event InsertHandler<T> PreUnsafeInsert;
  496. internal IQueryable<T> OnFilterQuery(IQueryable<T> query)
  497. {
  498. if (FilterQuery != null)
  499. foreach (FilterQueryEventHandler<T> filter in FilterQuery.GetInvocationList())
  500. query = filter(query);
  501. return query;
  502. }
  503. public bool HasQueryFilter
  504. {
  505. get { return FilterQuery != null; }
  506. }
  507. internal void OnPreUnsafeDelete(IQueryable<T> entityQuery)
  508. {
  509. if (PreUnsafeDelete != null)
  510. foreach (DeleteHandler<T> action in PreUnsafeDelete.GetInvocationList().Reverse())
  511. action(entityQuery);
  512. }
  513. internal void OnPreUnsafeMListDelete(IQueryable mlistQuery, IQueryable<T> entityQuery)
  514. {
  515. if (PreUnsafeMListDelete != null)
  516. foreach (DeleteMlistHandler<T> action in PreUnsafeMListDelete.GetInvocationList().Reverse())
  517. action(mlistQuery, entityQuery);
  518. }
  519. void IEntityEvents.OnPreUnsafeUpdate(IUpdateable update)
  520. {
  521. if (PreUnsafeUpdate != null)
  522. {
  523. var query = update.EntityQuery<T>();
  524. foreach (UpdateHandler<T> action in PreUnsafeUpdate.GetInvocationList().Reverse())
  525. action(update, query);
  526. }
  527. }
  528. void IEntityEvents.OnPreUnsafeInsert(IQueryable query, LambdaExpression constructor, IQueryable entityQuery)
  529. {
  530. if (PreUnsafeInsert != null)
  531. foreach (InsertHandler<T> action in PreUnsafeInsert.GetInvocationList().Reverse())
  532. action(query, constructor, (IQueryable<T>)entityQuery);
  533. }
  534. void IEntityEvents.OnPreSaving(IdentifiableEntity entity, ref bool graphModified)
  535. {
  536. if (PreSaving != null)
  537. PreSaving((T)entity, ref graphModified);
  538. }
  539. void IEntityEvents.OnSaving(IdentifiableEntity entity)
  540. {
  541. if (Saving != null)
  542. Saving((T)entity);
  543. }
  544. void IEntityEvents.OnRetrieved(IdentifiableEntity entity)
  545. {
  546. if (Retrieved != null)
  547. Retrieved((T)entity);
  548. }
  549. ICacheController IEntityEvents.CacheController
  550. {
  551. get { return CacheController; }
  552. }
  553. }
  554. public delegate void PreSavingEventHandler<T>(T ident, ref bool graphModified) where T : IdentifiableEntity;
  555. public delegate void RetrievedEventHandler<T>(T ident) where T : IdentifiableEntity;
  556. public delegate void SavingEventHandler<T>(T ident) where T : IdentifiableEntity;
  557. public delegate void SavedEventHandler<T>(T ident, SavedEventArgs args) where T : IdentifiableEntity;
  558. public delegate IQueryable<T> FilterQueryEventHandler<T>(IQueryable<T> query);
  559. public delegate void DeleteHandler<T>(IQueryable<T> entityQuery);
  560. public delegate void DeleteMlistHandler<T>(IQueryable mlistQuery, IQueryable<T> entityQuery);
  561. public delegate void UpdateHandler<T>(IUpdateable update, IQueryable<T> entityQuery);
  562. public delegate void InsertHandler<T>(IQueryable query, LambdaExpression constructor, IQueryable<T> entityQuery);
  563. public class SavedEventArgs
  564. {
  565. public bool IsRoot { get; set; }
  566. public bool WasNew { get; set; }
  567. public bool WasModified { get; set; }
  568. }
  569. public static class TableExtensions
  570. {
  571. internal static string UnScapeSql(this string name)
  572. {
  573. return name.Trim('[', ']');
  574. }
  575. }
  576. public class ServerName : IEquatable<ServerName>
  577. {
  578. public string Name { get; private set; }
  579. /// <summary>
  580. /// Linked Servers: http://msdn.microsoft.com/en-us/library/ms188279.aspx
  581. /// Not fully supported jet
  582. /// </summary>
  583. /// <param name="name"></param>
  584. public ServerName(string name)
  585. {
  586. if (string.IsNullOrEmpty(name))
  587. throw new ArgumentNullException("name");
  588. this.Name = name;
  589. }
  590. public override string ToString()
  591. {
  592. return Name.SqlScape();
  593. }
  594. public bool Equals(ServerName other)
  595. {
  596. return other.Name == Name;
  597. }
  598. public override bool Equals(object obj)
  599. {
  600. var db = obj as ServerName;
  601. return db != null && Equals(db);
  602. }
  603. public override int GetHashCode()
  604. {
  605. return Name.GetHashCode();
  606. }
  607. internal static ServerName Parse(string name)
  608. {
  609. if (string.IsNullOrEmpty(name))
  610. return null;
  611. return new ServerName(name.UnScapeSql());
  612. }
  613. }
  614. public class DatabaseName : IEquatable<DatabaseName>
  615. {
  616. public string Name { get; private set; }
  617. public ServerName Server { get; private set; }
  618. public DatabaseName(ServerName server, string name)
  619. {
  620. if (string.IsNullOrEmpty(name))
  621. throw new ArgumentNullException("name");
  622. this.Name = name;
  623. this.Server = server;
  624. }
  625. public override string ToString()
  626. {
  627. var result = Name.SqlScape();
  628. if (Server == null)
  629. return result;
  630. return Server.ToString() + "." + result;
  631. }
  632. public bool Equals(DatabaseName other)
  633. {
  634. return other.Name == Name &&
  635. object.Equals(Server, other.Server);
  636. }
  637. public override bool Equals(object obj)
  638. {
  639. var db = obj as DatabaseName;
  640. return db != null && Equals(db);
  641. }
  642. public override int GetHashCode()
  643. {
  644. return Name.GetHashCode() ^ (Server == null ? 0 : Server.GetHashCode());
  645. }
  646. internal static DatabaseName Parse(string name)
  647. {
  648. if (string.IsNullOrEmpty(name))
  649. return null;
  650. return new DatabaseName(ServerName.Parse(name.TryBeforeLast('.')), (name.TryAfterLast('.') ?? name).UnScapeSql());
  651. }
  652. }
  653. public class SchemaName : IEquatable<SchemaName>
  654. {
  655. public string Name { get; private set; }
  656. readonly DatabaseName database;
  657. public DatabaseName Database
  658. {
  659. get
  660. {
  661. if (database == null || ObjectName.CurrentOptions.AvoidDatabaseName)
  662. return null;
  663. return database;
  664. }
  665. }
  666. public static readonly SchemaName Default = new SchemaName(null, "dbo");
  667. public bool IsDefault()
  668. {
  669. return Name == "dbo" && Database == null;
  670. }
  671. public SchemaName(DatabaseName database, string name)
  672. {
  673. if (string.IsNullOrEmpty(name))
  674. throw new ArgumentNullException("name");
  675. this.Name = name;
  676. this.database = database;
  677. }
  678. public override string ToString()
  679. {
  680. var result = Name.SqlScape();
  681. if (Database == null)
  682. return result;
  683. return Database.ToString() + "." + result;
  684. }
  685. public bool Equals(SchemaName other)
  686. {
  687. return other.Name == Name &&
  688. object.Equals(Database, other.Database);
  689. }
  690. public override bool Equals(object obj)
  691. {
  692. var sc = obj as SchemaName;
  693. return sc != null && Equals(sc);
  694. }
  695. public override int GetHashCode()
  696. {
  697. return Name.GetHashCode() ^ (Database == null ? 0 : Database.GetHashCode());
  698. }
  699. internal static SchemaName Parse(string name)
  700. {
  701. if (string.IsNullOrEmpty(name))
  702. return SchemaName.Default;
  703. return new SchemaName(DatabaseName.Parse(name.TryBeforeLast('.')), (name.TryAfterLast('.') ?? name).UnScapeSql());
  704. }
  705. }
  706. public class ObjectName : IEquatable<ObjectName>
  707. {
  708. public string Name { get; private set; }
  709. public SchemaName Schema { get; private set; }
  710. public ObjectName(SchemaName schema, string name)
  711. {
  712. if (string.IsNullOrEmpty(name))
  713. throw new ArgumentNullException("name");
  714. if (schema == null)
  715. throw new ArgumentNullException("schema");
  716. this.Name = name;
  717. this.Schema = schema;
  718. }
  719. public override string ToString()
  720. {
  721. if (Schema == null || Schema.IsDefault())
  722. return Name.SqlScape();
  723. return Schema.ToString() + "." + Name.SqlScape();
  724. }
  725. public string ToStringDbo()
  726. {
  727. if (Schema == null)
  728. return Name.SqlScape();
  729. return Schema.ToString() + "." + Name.SqlScape();
  730. }
  731. public bool Equals(ObjectName other)
  732. {
  733. return other.Name == Name &&
  734. object.Equals(Schema, other.Schema);
  735. }
  736. public override bool Equals(object obj)
  737. {
  738. var sc = obj as ObjectName;
  739. return sc != null && Equals(sc);
  740. }
  741. public override int GetHashCode()
  742. {
  743. return Name.GetHashCode() ^ (Schema == null ? 0 : Schema.GetHashCode());
  744. }
  745. internal static ObjectName Parse(string name)
  746. {
  747. if (string.IsNullOrEmpty(name))
  748. throw new ArgumentNullException("name");
  749. return new ObjectName(SchemaName.Parse(name.TryBeforeLast('.')), (name.TryAfterLast('.') ?? name).UnScapeSql());
  750. }
  751. public ObjectName OnDatabase(DatabaseName databaseName)
  752. {
  753. return new ObjectName(new SchemaName(databaseName, Schema.Name), Name);
  754. }
  755. public ObjectName OnSchema(SchemaName schemaName)
  756. {
  757. return new ObjectName(schemaName, Name);
  758. }
  759. static readonly ThreadVariable<ObjectNameOptions> optionsVariable = Statics.ThreadVariable<ObjectNameOptions>("objectNameOptions");
  760. public static IDisposable OverrideOptions(ObjectNameOptions options)
  761. {
  762. var old = optionsVariable.Value;
  763. optionsVariable.Value = options;
  764. return new Disposable(() => optionsVariable.Value = old);
  765. }
  766. public static ObjectNameOptions CurrentOptions
  767. {
  768. get { return optionsVariable.Value; }
  769. }
  770. }
  771. public struct ObjectNameOptions
  772. {
  773. public bool IncludeDboSchema;
  774. public DatabaseName OverrideDatabaseNameOnSystemQueries;
  775. public bool AvoidDatabaseName;
  776. }
  777. }