ChangeTracker.cs 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Text;
  8. namespace System.Data.Linq {
  9. using System.Data.Linq.Mapping;
  10. using System.Data.Linq.Provider;
  11. internal abstract class ChangeTracker {
  12. /// <summary>
  13. /// Starts tracking an object as 'unchanged'
  14. /// </summary>
  15. /// <param name="obj"></param>
  16. /// <returns></returns>
  17. internal abstract TrackedObject Track(object obj);
  18. /// <summary>
  19. /// Starts tracking an object as 'unchanged', and optionally
  20. /// 'weakly' tracks all other referenced objects recursively.
  21. /// </summary>
  22. /// <param name="obj"></param>
  23. /// <param name="recurse">True if all untracked objects in the graph
  24. /// should be tracked recursively.</param>
  25. /// <returns></returns>
  26. internal abstract TrackedObject Track(object obj, bool recurse);
  27. /// <summary>
  28. /// Fast-tracks an object that is already in identity cache
  29. /// </summary>
  30. /// <param name="obj"></param>
  31. internal abstract void FastTrack(object obj);
  32. internal abstract bool IsTracked(object obj);
  33. internal abstract TrackedObject GetTrackedObject(object obj);
  34. internal abstract void StopTracking(object obj);
  35. internal abstract void AcceptChanges();
  36. internal abstract IEnumerable<TrackedObject> GetInterestingObjects();
  37. internal static ChangeTracker CreateChangeTracker(CommonDataServices dataServices, bool asReadOnly) {
  38. if (asReadOnly) {
  39. return new ReadOnlyChangeTracker();
  40. }
  41. else {
  42. return new StandardChangeTracker(dataServices);
  43. }
  44. }
  45. class StandardChangeTracker : ChangeTracker {
  46. Dictionary<object, StandardTrackedObject> items;
  47. PropertyChangingEventHandler onPropertyChanging;
  48. CommonDataServices services;
  49. internal StandardChangeTracker(CommonDataServices services) {
  50. this.services = services;
  51. this.items = new Dictionary<object, StandardTrackedObject>();
  52. this.onPropertyChanging = new PropertyChangingEventHandler(this.OnPropertyChanging);
  53. }
  54. /// <summary>
  55. /// Given a type root and a discriminator, return the type that would be instantiated.
  56. /// </summary>
  57. private static MetaType TypeFromDiscriminator(MetaType root, object discriminator) {
  58. foreach (MetaType type in root.InheritanceTypes) {
  59. if (IsSameDiscriminator(discriminator, type.InheritanceCode))
  60. return type;
  61. }
  62. return root.InheritanceDefault;
  63. }
  64. private static bool IsSameDiscriminator(object discriminator1, object discriminator2) {
  65. if (discriminator1 == discriminator2) {
  66. return true;
  67. }
  68. if (discriminator1 == null || discriminator2 == null) {
  69. return false;
  70. }
  71. return discriminator1.Equals(discriminator2);
  72. }
  73. internal override TrackedObject Track(object obj) {
  74. return Track(obj, false);
  75. }
  76. internal override TrackedObject Track(object obj, bool recurse) {
  77. MetaType type = this.services.Model.GetMetaType(obj.GetType());
  78. Dictionary<object, object> visited = new Dictionary<object, object>();
  79. return Track(type, obj, visited, recurse, 1);
  80. }
  81. private TrackedObject Track(MetaType mt, object obj, Dictionary<object, object> visited, bool recurse, int level) {
  82. StandardTrackedObject tracked = (StandardTrackedObject)this.GetTrackedObject(obj);
  83. if (tracked != null || visited.ContainsKey(obj)) {
  84. return tracked;
  85. }
  86. // The root object tracked is tracked normally - all other objects
  87. // in the reference graph are weakly tracked.
  88. bool weaklyTrack = level > 1;
  89. tracked = new StandardTrackedObject(this, mt, obj, obj, weaklyTrack);
  90. if (tracked.HasDeferredLoaders) {
  91. throw Error.CannotAttachAddNonNewEntities();
  92. }
  93. this.items.Add(obj, tracked);
  94. this.Attach(obj);
  95. visited.Add(obj, obj);
  96. if (recurse) {
  97. // track parents (objects we are dependent on)
  98. foreach (RelatedItem parent in this.services.GetParents(mt, obj)) {
  99. this.Track(parent.Type, parent.Item, visited, recurse, level + 1);
  100. }
  101. // track children (objects that are dependent on us)
  102. foreach (RelatedItem child in this.services.GetChildren(mt, obj)) {
  103. this.Track(child.Type, child.Item, visited, recurse, level + 1);
  104. }
  105. }
  106. return tracked;
  107. }
  108. internal override void FastTrack(object obj) {
  109. // assumes object is already in identity cache
  110. this.Attach(obj);
  111. }
  112. internal override void StopTracking(object obj) {
  113. this.Detach(obj);
  114. this.items.Remove(obj);
  115. }
  116. internal override bool IsTracked(object obj) {
  117. return this.items.ContainsKey(obj) || this.IsFastTracked(obj);
  118. }
  119. private bool IsFastTracked(object obj) {
  120. MetaType type = this.services.Model.GetTable(obj.GetType()).RowType;
  121. return this.services.IsCachedObject(type, obj);
  122. }
  123. internal override TrackedObject GetTrackedObject(object obj) {
  124. StandardTrackedObject ti;
  125. if (!this.items.TryGetValue(obj, out ti)) {
  126. if (this.IsFastTracked(obj)) {
  127. return this.PromoteFastTrackedObject(obj);
  128. }
  129. }
  130. return ti;
  131. }
  132. private StandardTrackedObject PromoteFastTrackedObject(object obj) {
  133. Type type = obj.GetType();
  134. MetaType metaType = this.services.Model.GetTable(type).RowType.GetInheritanceType(type);
  135. return this.PromoteFastTrackedObject(metaType, obj);
  136. }
  137. private StandardTrackedObject PromoteFastTrackedObject(MetaType type, object obj) {
  138. StandardTrackedObject ti = new StandardTrackedObject(this, type, obj, obj);
  139. this.items.Add(obj, ti);
  140. return ti;
  141. }
  142. private void Attach(object obj) {
  143. INotifyPropertyChanging notifier = obj as INotifyPropertyChanging;
  144. if (notifier != null) {
  145. notifier.PropertyChanging += this.onPropertyChanging;
  146. }
  147. else {
  148. // if has no notifier, consider it modified already
  149. this.OnPropertyChanging(obj, null);
  150. }
  151. }
  152. private void Detach(object obj) {
  153. INotifyPropertyChanging notifier = obj as INotifyPropertyChanging;
  154. if (notifier != null) {
  155. notifier.PropertyChanging -= this.onPropertyChanging;
  156. }
  157. }
  158. private void OnPropertyChanging(object sender, PropertyChangingEventArgs args) {
  159. StandardTrackedObject ti;
  160. if (this.items.TryGetValue(sender, out ti)) {
  161. ti.StartTracking();
  162. }
  163. else if (this.IsFastTracked(sender)) {
  164. ti = this.PromoteFastTrackedObject(sender);
  165. ti.StartTracking();
  166. }
  167. }
  168. internal override void AcceptChanges() {
  169. List<StandardTrackedObject> list = new List<StandardTrackedObject>((IEnumerable<StandardTrackedObject>)this.items.Values);
  170. foreach (TrackedObject item in list) {
  171. item.AcceptChanges();
  172. }
  173. }
  174. internal override IEnumerable<TrackedObject> GetInterestingObjects() {
  175. foreach (StandardTrackedObject ti in this.items.Values) {
  176. if (ti.IsInteresting) {
  177. yield return ti;
  178. }
  179. }
  180. }
  181. class StandardTrackedObject : TrackedObject {
  182. private StandardChangeTracker tracker;
  183. private MetaType type;
  184. private object current;
  185. private object original;
  186. private State state;
  187. private BitArray dirtyMemberCache;
  188. private bool haveInitializedDeferredLoaders;
  189. private bool isWeaklyTracked;
  190. enum State {
  191. New,
  192. Deleted,
  193. PossiblyModified,
  194. Modified,
  195. Removed,
  196. Dead
  197. }
  198. public override string ToString() {
  199. return type.Name + ":" + GetState();
  200. }
  201. private string GetState() {
  202. switch (this.state) {
  203. case State.New:
  204. case State.Deleted:
  205. case State.Dead:
  206. case State.Removed:
  207. return this.state.ToString();
  208. default:
  209. if (this.IsModified) {
  210. return "Modified";
  211. }
  212. else {
  213. return "Unmodified";
  214. }
  215. }
  216. }
  217. internal StandardTrackedObject(StandardChangeTracker tracker, MetaType type, object current, object original) {
  218. if (current == null) {
  219. throw Error.ArgumentNull("current");
  220. }
  221. this.tracker = tracker;
  222. this.type = type.GetInheritanceType(current.GetType());
  223. this.current = current;
  224. this.original = original;
  225. this.state = State.PossiblyModified;
  226. dirtyMemberCache = new BitArray(this.type.DataMembers.Count);
  227. }
  228. internal StandardTrackedObject(StandardChangeTracker tracker, MetaType type, object current, object original, bool isWeaklyTracked)
  229. : this(tracker, type, current, original) {
  230. this.isWeaklyTracked = isWeaklyTracked;
  231. }
  232. internal override bool IsWeaklyTracked {
  233. get { return isWeaklyTracked; }
  234. }
  235. internal override MetaType Type {
  236. get { return this.type; }
  237. }
  238. internal override object Current {
  239. get { return this.current; }
  240. }
  241. internal override object Original {
  242. get { return this.original; }
  243. }
  244. internal override bool IsNew {
  245. get { return this.state == State.New; }
  246. }
  247. internal override bool IsDeleted {
  248. get { return this.state == State.Deleted; }
  249. }
  250. internal override bool IsRemoved {
  251. get { return this.state == State.Removed; }
  252. }
  253. internal override bool IsDead {
  254. get { return this.state == State.Dead; }
  255. }
  256. internal override bool IsModified {
  257. get { return this.state == State.Modified || (this.state == State.PossiblyModified && this.current != this.original && this.HasChangedValues()); }
  258. }
  259. internal override bool IsUnmodified {
  260. get { return this.state == State.PossiblyModified && (this.current == this.original || !this.HasChangedValues()); }
  261. }
  262. internal override bool IsPossiblyModified {
  263. get { return this.state == State.Modified || this.state == State.PossiblyModified; }
  264. }
  265. internal override bool CanInferDelete() {
  266. // A delete can be inferred iff there is a non-nullable singleton association that has
  267. // been set to null, and the association has DeleteOnNull = true.
  268. if (this.state == State.Modified || this.state == State.PossiblyModified) {
  269. foreach (MetaAssociation assoc in Type.Associations) {
  270. if (assoc.DeleteOnNull && assoc.IsForeignKey && !assoc.IsNullable && !assoc.IsMany &&
  271. assoc.ThisMember.StorageAccessor.HasAssignedValue(Current) &&
  272. assoc.ThisMember.StorageAccessor.GetBoxedValue(Current) == null) {
  273. return true;
  274. }
  275. }
  276. }
  277. return false;
  278. }
  279. internal override bool IsInteresting {
  280. get {
  281. return this.state == State.New ||
  282. this.state == State.Deleted ||
  283. this.state == State.Modified ||
  284. (this.state == State.PossiblyModified && this.current != this.original) ||
  285. CanInferDelete();
  286. }
  287. }
  288. internal override void ConvertToNew() {
  289. // must be new or unmodified or removed to convert to new
  290. System.Diagnostics.Debug.Assert(this.IsNew || this.IsRemoved || this.IsUnmodified);
  291. this.original = null;
  292. this.state = State.New;
  293. }
  294. internal override void ConvertToPossiblyModified() {
  295. System.Diagnostics.Debug.Assert(this.IsPossiblyModified || this.IsDeleted);
  296. this.state = State.PossiblyModified;
  297. this.isWeaklyTracked = false;
  298. }
  299. internal override void ConvertToModified() {
  300. System.Diagnostics.Debug.Assert(this.IsPossiblyModified);
  301. System.Diagnostics.Debug.Assert(this.type.VersionMember != null || !this.type.HasUpdateCheck);
  302. this.state = State.Modified;
  303. this.isWeaklyTracked = false;
  304. }
  305. internal override void ConvertToPossiblyModified(object originalState) {
  306. // must be modified or unmodified to convert to modified
  307. System.Diagnostics.Debug.Assert(this.IsNew || this.IsPossiblyModified);
  308. System.Diagnostics.Debug.Assert(originalState != null);
  309. System.Diagnostics.Debug.Assert(originalState.GetType() == this.type.Type);
  310. this.state = State.PossiblyModified;
  311. this.original = this.CreateDataCopy(originalState);
  312. this.isWeaklyTracked = false;
  313. }
  314. internal override void ConvertToDeleted() {
  315. // must be modified or unmodified to be deleted
  316. System.Diagnostics.Debug.Assert(this.IsDeleted || this.IsPossiblyModified);
  317. this.state = State.Deleted;
  318. this.isWeaklyTracked = false;
  319. }
  320. internal override void ConvertToDead() {
  321. System.Diagnostics.Debug.Assert(this.IsDead || this.IsDeleted);
  322. this.state = State.Dead;
  323. this.isWeaklyTracked = false;
  324. }
  325. internal override void ConvertToRemoved() {
  326. System.Diagnostics.Debug.Assert(this.IsRemoved || this.IsNew);
  327. this.state = State.Removed;
  328. this.isWeaklyTracked = false;
  329. }
  330. internal override void ConvertToUnmodified() {
  331. System.Diagnostics.Debug.Assert(this.IsNew || this.IsPossiblyModified);
  332. // reset to unmodified
  333. this.state = State.PossiblyModified;
  334. if (this.current is INotifyPropertyChanging) {
  335. this.original = this.current;
  336. }
  337. else {
  338. this.original = this.CreateDataCopy(this.current);
  339. }
  340. this.ResetDirtyMemberTracking();
  341. this.isWeaklyTracked = false;
  342. }
  343. internal override void AcceptChanges() {
  344. if (IsWeaklyTracked) {
  345. InitializeDeferredLoaders();
  346. isWeaklyTracked = false;
  347. }
  348. if (this.IsDeleted) {
  349. this.ConvertToDead();
  350. }
  351. else if (this.IsNew) {
  352. this.InitializeDeferredLoaders();
  353. this.ConvertToUnmodified();
  354. }
  355. else if (this.IsPossiblyModified) {
  356. this.ConvertToUnmodified();
  357. }
  358. }
  359. private void AssignMember(object instance, MetaDataMember mm, object value) {
  360. // In the unnotified case, directly use the storage accessor
  361. // for everything because there are not events to be fired.
  362. if (!(this.current is INotifyPropertyChanging)) {
  363. mm.StorageAccessor.SetBoxedValue(ref instance, value);
  364. }
  365. else {
  366. // Go through the member accessor to fire events.
  367. mm.MemberAccessor.SetBoxedValue(ref instance, value);
  368. }
  369. }
  370. /// <summary>
  371. /// Certain state is saved during change tracking to enable modifications
  372. /// to be detected taking refresh operations into account. When changes
  373. /// are reverted or accepted, this state must be reset.
  374. /// </summary>
  375. private void ResetDirtyMemberTracking() {
  376. this.dirtyMemberCache.SetAll(false);
  377. }
  378. /// <summary>
  379. /// Refresh internal tracking state using the original value and mode
  380. /// specified.
  381. /// </summary>
  382. internal override void Refresh(RefreshMode mode, object freshInstance) {
  383. this.SynchDependentData();
  384. // This must be done prior to updating original values
  385. this.UpdateDirtyMemberCache();
  386. // Apply the refresh strategy to each data member
  387. Type instanceType = freshInstance.GetType();
  388. foreach (MetaDataMember mm in type.PersistentDataMembers) {
  389. var memberMode = mm.IsDbGenerated ? RefreshMode.OverwriteCurrentValues : mode;
  390. if (memberMode != RefreshMode.KeepCurrentValues) {
  391. if (!mm.IsAssociation && (this.Type.Type == instanceType || mm.DeclaringType.Type.IsAssignableFrom(instanceType))) {
  392. object freshValue = mm.StorageAccessor.GetBoxedValue(freshInstance);
  393. this.RefreshMember(mm, memberMode, freshValue);
  394. }
  395. }
  396. }
  397. // Make the new data the current original value
  398. this.original = this.CreateDataCopy(freshInstance);
  399. if (mode == RefreshMode.OverwriteCurrentValues) {
  400. this.ResetDirtyMemberTracking();
  401. }
  402. }
  403. /// <summary>
  404. /// Using the last saved comparison baseline, figure out which members have
  405. /// changed since the last refresh, and save that information. This must be
  406. /// done BEFORE any merge operations modify the current values.
  407. /// </summary>
  408. private void UpdateDirtyMemberCache() {
  409. // iterate over all members, and if they differ from
  410. // last read values, mark as dirty
  411. foreach (MetaDataMember mm in type.PersistentDataMembers) {
  412. if (mm.IsAssociation && mm.Association.IsMany) {
  413. continue;
  414. }
  415. if (!this.dirtyMemberCache.Get(mm.Ordinal) && this.HasChangedValue(mm)) {
  416. this.dirtyMemberCache.Set(mm.Ordinal, true);
  417. }
  418. }
  419. }
  420. internal override void RefreshMember(MetaDataMember mm, RefreshMode mode, object freshValue) {
  421. System.Diagnostics.Debug.Assert(!mm.IsAssociation);
  422. if (mode == RefreshMode.KeepCurrentValues) {
  423. return;
  424. }
  425. bool hasUserChange = this.HasChangedValue(mm);
  426. // we don't want to overwrite any modified values, unless
  427. // the mode is original wins
  428. if (hasUserChange && mode != RefreshMode.OverwriteCurrentValues)
  429. return;
  430. object currentValue = mm.StorageAccessor.GetBoxedValue(this.current);
  431. if (!object.Equals(freshValue, currentValue)) {
  432. mm.StorageAccessor.SetBoxedValue(ref this.current, freshValue);
  433. // update all singleton associations that are affected by a change to this member
  434. foreach (MetaDataMember am in this.GetAssociationsForKey(mm)) {
  435. if (!am.Association.IsMany) {
  436. IEnumerable ds = this.tracker.services.GetDeferredSourceFactory(am).CreateDeferredSource(this.current);
  437. if (am.StorageAccessor.HasValue(this.current)) {
  438. this.AssignMember(this.current, am, ds.Cast<Object>().SingleOrDefault());
  439. }
  440. }
  441. }
  442. }
  443. }
  444. private IEnumerable<MetaDataMember> GetAssociationsForKey(MetaDataMember key) {
  445. foreach (MetaDataMember mm in this.type.PersistentDataMembers) {
  446. if (mm.IsAssociation && mm.Association.ThisKey.Contains(key)) {
  447. yield return mm;
  448. }
  449. }
  450. }
  451. internal override object CreateDataCopy(object instance) {
  452. System.Diagnostics.Debug.Assert(instance != null);
  453. Type instanceType = instance.GetType();
  454. System.Diagnostics.Debug.Assert(instance.GetType() == this.type.Type);
  455. object copy = Activator.CreateInstance(this.Type.Type);
  456. MetaType rootMetaType = this.tracker.services.Model.GetTable(instanceType).RowType.InheritanceRoot;
  457. foreach (MetaDataMember mm in rootMetaType.GetInheritanceType(instanceType).PersistentDataMembers) {
  458. if (this.Type.Type != instanceType && !mm.DeclaringType.Type.IsAssignableFrom(instanceType)) {
  459. continue;
  460. }
  461. if (mm.IsDeferred) {
  462. // do not copy associations
  463. if (!mm.IsAssociation) {
  464. if (mm.StorageAccessor.HasValue(instance)) {
  465. object value = mm.DeferredValueAccessor.GetBoxedValue(instance);
  466. mm.DeferredValueAccessor.SetBoxedValue(ref copy, value);
  467. }
  468. else {
  469. IEnumerable ds = this.tracker.services.GetDeferredSourceFactory(mm).CreateDeferredSource(copy);
  470. mm.DeferredSourceAccessor.SetBoxedValue(ref copy, ds);
  471. }
  472. }
  473. }
  474. else {
  475. // otherwise assign the value as-is to the backup instance
  476. object value = mm.StorageAccessor.GetBoxedValue(instance);
  477. // assumes member values are immutable or will communicate changes to entity
  478. // note: byte[] and char[] don't do this.
  479. mm.StorageAccessor.SetBoxedValue(ref copy, value);
  480. }
  481. }
  482. return copy;
  483. }
  484. internal void StartTracking() {
  485. if (this.original == this.current) {
  486. this.original = this.CreateDataCopy(this.current);
  487. }
  488. }
  489. // Return value indicates whether or not any data was actually [....]'d
  490. internal override bool SynchDependentData() {
  491. bool valueWasSet = false;
  492. // set foreign key fields
  493. foreach (MetaAssociation assoc in this.Type.Associations) {
  494. MetaDataMember mm = assoc.ThisMember;
  495. if (assoc.IsForeignKey) {
  496. bool hasAssigned = mm.StorageAccessor.HasAssignedValue(this.current);
  497. bool hasLoaded = mm.StorageAccessor.HasLoadedValue(this.current);
  498. if (hasAssigned || hasLoaded) {
  499. object parent = mm.StorageAccessor.GetBoxedValue(this.current);
  500. if (parent != null) {
  501. // copy parent's current primary key into this instance's foreign key fields
  502. for (int i = 0, n = assoc.ThisKey.Count; i < n; i++) {
  503. MetaDataMember accThis = assoc.ThisKey[i];
  504. MetaDataMember accParent = assoc.OtherKey[i];
  505. object parentValue = accParent.StorageAccessor.GetBoxedValue(parent);
  506. accThis.StorageAccessor.SetBoxedValue(ref this.current, parentValue);
  507. valueWasSet = true;
  508. }
  509. }
  510. else if (assoc.IsNullable) {
  511. if (mm.IsDeferred || (this.original != null && mm.MemberAccessor.GetBoxedValue(this.original) != null)) {
  512. // no known parent? set to null
  513. for (int i = 0, n = assoc.ThisKey.Count; i < n; i++) {
  514. MetaDataMember accThis = assoc.ThisKey[i];
  515. if (accThis.CanBeNull) {
  516. if (this.original != null && this.HasChangedValue(accThis)) {
  517. if (accThis.StorageAccessor.GetBoxedValue(this.current) != null) {
  518. throw Error.InconsistentAssociationAndKeyChange(accThis.Member.Name, mm.Member.Name);
  519. }
  520. }
  521. else {
  522. accThis.StorageAccessor.SetBoxedValue(ref this.current, null);
  523. valueWasSet = true;
  524. }
  525. }
  526. }
  527. }
  528. }
  529. else if (!hasLoaded) {
  530. //Else the parent association has been set to null; but the ID is not nullable so
  531. //the value can not be set
  532. StringBuilder keys = new StringBuilder();
  533. foreach (MetaDataMember key in assoc.ThisKey) {
  534. if (keys.Length > 0) {
  535. keys.Append(", ");
  536. }
  537. keys.AppendFormat("{0}.{1}", this.Type.Name.ToString(), key.Name);
  538. }
  539. throw Error.CouldNotRemoveRelationshipBecauseOneSideCannotBeNull(assoc.OtherType.Name, this.Type.Name, keys);
  540. }
  541. }
  542. }
  543. }
  544. /// Explicitly set any inheritance discriminator for item.
  545. if (this.type.HasInheritance) {
  546. if (this.original != null) {
  547. object currentDiscriminator = type.Discriminator.MemberAccessor.GetBoxedValue(this.current);
  548. MetaType currentTypeFromDiscriminator = TypeFromDiscriminator(this.type, currentDiscriminator);
  549. object dbDiscriminator = type.Discriminator.MemberAccessor.GetBoxedValue(this.original);
  550. MetaType dbTypeFromDiscriminator = TypeFromDiscriminator(this.type, dbDiscriminator);
  551. // Would the discriminator change also change the type? If so, its not allowed.
  552. if (currentTypeFromDiscriminator != dbTypeFromDiscriminator) {
  553. throw Error.CannotChangeInheritanceType(dbDiscriminator,
  554. currentDiscriminator, original.GetType().Name, currentTypeFromDiscriminator);
  555. }
  556. }
  557. else {
  558. // No db value means this is an 'Add'. Set the discriminator.
  559. MetaType currentType = type.GetInheritanceType(this.current.GetType());
  560. if (currentType.HasInheritanceCode) {
  561. object code = currentType.InheritanceCode;
  562. this.type.Discriminator.MemberAccessor.SetBoxedValue(ref current, code);
  563. valueWasSet = true;
  564. }
  565. }
  566. }
  567. return valueWasSet;
  568. }
  569. internal override bool HasChangedValue(MetaDataMember mm) {
  570. if (this.current == this.original) {
  571. return false;
  572. }
  573. if (mm.IsAssociation && mm.Association.IsMany) {
  574. return mm.StorageAccessor.HasAssignedValue(this.original);
  575. }
  576. if (mm.StorageAccessor.HasValue(this.current)) {
  577. if (this.original != null && mm.StorageAccessor.HasValue(this.original)) {
  578. // If the member has ever been in a modified state
  579. // in the past, it is considered modified
  580. if (dirtyMemberCache.Get(mm.Ordinal)) {
  581. return true;
  582. }
  583. object baseline = mm.MemberAccessor.GetBoxedValue(this.original);
  584. object currentValue = mm.MemberAccessor.GetBoxedValue(this.current);
  585. if (!object.Equals(currentValue, baseline)) {
  586. return true;
  587. }
  588. return false;
  589. }
  590. else if (mm.IsDeferred && mm.StorageAccessor.HasAssignedValue(this.current)) {
  591. return true;
  592. }
  593. }
  594. return false;
  595. }
  596. internal override bool HasChangedValues() {
  597. if (this.current == this.original) {
  598. return false;
  599. }
  600. if (this.IsNew) {
  601. return true;
  602. }
  603. foreach (MetaDataMember mm in this.type.PersistentDataMembers) {
  604. if (!mm.IsAssociation && this.HasChangedValue(mm)) {
  605. return true;
  606. }
  607. }
  608. return false;
  609. }
  610. internal override IEnumerable<ModifiedMemberInfo> GetModifiedMembers() {
  611. foreach (MetaDataMember mm in this.type.PersistentDataMembers) {
  612. if (this.IsModifiedMember(mm)) {
  613. object currentValue = mm.MemberAccessor.GetBoxedValue(this.current);
  614. if (this.original != null && mm.StorageAccessor.HasValue(this.original)) {
  615. object originalValue = mm.MemberAccessor.GetBoxedValue(this.original);
  616. yield return new ModifiedMemberInfo(mm.Member, currentValue, originalValue);
  617. }
  618. else if (this.original == null || (mm.IsDeferred && !mm.StorageAccessor.HasLoadedValue(this.current))) {
  619. yield return new ModifiedMemberInfo(mm.Member, currentValue, null);
  620. }
  621. }
  622. }
  623. }
  624. private bool IsModifiedMember(MetaDataMember member) {
  625. return !member.IsAssociation &&
  626. !member.IsPrimaryKey &&
  627. !member.IsVersion &&
  628. !member.IsDbGenerated &&
  629. member.StorageAccessor.HasAssignedValue(this.current) &&
  630. (this.state == State.Modified ||
  631. (this.state == State.PossiblyModified && this.HasChangedValue(member)));
  632. }
  633. internal override bool HasDeferredLoaders {
  634. get {
  635. foreach (MetaAssociation assoc in this.Type.Associations) {
  636. if (HasDeferredLoader(assoc.ThisMember)) {
  637. return true;
  638. }
  639. }
  640. IEnumerable<MetaDataMember> deferredMembers = this.Type.PersistentDataMembers.Where(p => p.IsDeferred && !p.IsAssociation);
  641. foreach (MetaDataMember deferredMember in deferredMembers) {
  642. if (HasDeferredLoader(deferredMember)) {
  643. return true;
  644. }
  645. }
  646. return false;
  647. }
  648. }
  649. private bool HasDeferredLoader(MetaDataMember deferredMember) {
  650. if (!deferredMember.IsDeferred) {
  651. return false;
  652. }
  653. MetaAccessor acc = deferredMember.StorageAccessor;
  654. if (acc.HasAssignedValue(this.current) || acc.HasLoadedValue(this.current)) {
  655. return false;
  656. }
  657. MetaAccessor dsacc = deferredMember.DeferredSourceAccessor;
  658. IEnumerable loader = (IEnumerable)dsacc.GetBoxedValue(this.current);
  659. return loader != null;
  660. }
  661. /// <summary>
  662. /// Called to initialize deferred loaders for New or Attached entities.
  663. /// </summary>
  664. internal override void InitializeDeferredLoaders() {
  665. if (this.tracker.services.Context.DeferredLoadingEnabled) {
  666. foreach (MetaAssociation assoc in this.Type.Associations) {
  667. // don't set loader on association that is dependent on unrealized generated values
  668. if (!this.IsPendingGeneration(assoc.ThisKey)) {
  669. InitializeDeferredLoader(assoc.ThisMember);
  670. }
  671. }
  672. IEnumerable<MetaDataMember> deferredMembers = this.Type.PersistentDataMembers.Where(p => p.IsDeferred && !p.IsAssociation);
  673. foreach (MetaDataMember deferredMember in deferredMembers) {
  674. // don't set loader on member that is dependent on unrealized generated values
  675. if (!this.IsPendingGeneration(Type.IdentityMembers)) {
  676. InitializeDeferredLoader(deferredMember);
  677. }
  678. }
  679. haveInitializedDeferredLoaders = true;
  680. }
  681. }
  682. private void InitializeDeferredLoader(MetaDataMember deferredMember) {
  683. MetaAccessor acc = deferredMember.StorageAccessor;
  684. if (!acc.HasAssignedValue(this.current) && !acc.HasLoadedValue(this.current)) {
  685. MetaAccessor dsacc = deferredMember.DeferredSourceAccessor;
  686. IEnumerable loader = (IEnumerable)dsacc.GetBoxedValue(this.current);
  687. // don't reset loader on any deferred member that already has one
  688. if (loader == null) {
  689. IDeferredSourceFactory factory = this.tracker.services.GetDeferredSourceFactory(deferredMember);
  690. loader = factory.CreateDeferredSource(this.current);
  691. dsacc.SetBoxedValue(ref this.current, loader);
  692. }
  693. else if (loader != null && !haveInitializedDeferredLoaders) {
  694. // If loader is present but wasn't generated by us, then
  695. // an attempt to Attach or Add an entity from another context
  696. // has been made, which is not supported.
  697. throw Error.CannotAttachAddNonNewEntities();
  698. }
  699. }
  700. }
  701. internal override bool IsPendingGeneration(IEnumerable<MetaDataMember> key) {
  702. if (this.IsNew) {
  703. foreach (MetaDataMember member in key) {
  704. if (IsMemberPendingGeneration(member)) {
  705. return true;
  706. }
  707. }
  708. }
  709. return false;
  710. }
  711. internal override bool IsMemberPendingGeneration(MetaDataMember keyMember) {
  712. if (this.IsNew && keyMember.IsDbGenerated) {
  713. return true;
  714. }
  715. // look for any FK association that has this key member (should only be one)
  716. foreach (MetaAssociation assoc in type.Associations) {
  717. if (assoc.IsForeignKey) {
  718. int index = assoc.ThisKey.IndexOf(keyMember);
  719. if (index > -1) {
  720. // we must have a reference to this other object to know if its side of
  721. // the association is generated or not
  722. object otherItem = null;
  723. if (assoc.ThisMember.IsDeferred) {
  724. otherItem = assoc.ThisMember.DeferredValueAccessor.GetBoxedValue(this.current);
  725. }
  726. else {
  727. otherItem = assoc.ThisMember.StorageAccessor.GetBoxedValue(this.current);
  728. }
  729. if (otherItem != null) {
  730. if (assoc.IsMany) {
  731. // Can't be pending generation for a value that would have to be the same
  732. // across many rows.
  733. continue;
  734. }
  735. else {
  736. StandardTrackedObject trackedOther = (StandardTrackedObject)this.tracker.GetTrackedObject(otherItem);
  737. if (trackedOther != null) {
  738. MetaDataMember otherMember = assoc.OtherKey[index];
  739. return trackedOther.IsMemberPendingGeneration(otherMember);
  740. }
  741. }
  742. }
  743. }
  744. }
  745. }
  746. return false;
  747. }
  748. }
  749. }
  750. /// <summary>
  751. /// This is the implementation used when change tracking is disabled.
  752. /// </summary>
  753. class ReadOnlyChangeTracker : ChangeTracker {
  754. internal override TrackedObject Track(object obj) { return null; }
  755. internal override TrackedObject Track(object obj, bool recurse) { return null; }
  756. internal override void FastTrack(object obj) { }
  757. internal override bool IsTracked(object obj) { return false; }
  758. internal override TrackedObject GetTrackedObject(object obj) { return null; }
  759. internal override void StopTracking(object obj) { }
  760. internal override void AcceptChanges() { }
  761. internal override IEnumerable<TrackedObject> GetInterestingObjects() { return new TrackedObject[0]; }
  762. }
  763. }
  764. internal abstract class TrackedObject {
  765. internal abstract MetaType Type { get; }
  766. /// <summary>
  767. /// The current client value.
  768. /// </summary>
  769. internal abstract object Current { get; }
  770. /// <summary>
  771. /// The last read database value. This is updated whenever the
  772. /// item is refreshed.
  773. /// </summary>
  774. internal abstract object Original { get; }
  775. internal abstract bool IsInteresting { get; } // new, deleted or possibly changed
  776. internal abstract bool IsNew { get; }
  777. internal abstract bool IsDeleted { get; }
  778. internal abstract bool IsModified { get; }
  779. internal abstract bool IsUnmodified { get; }
  780. internal abstract bool IsPossiblyModified { get; }
  781. internal abstract bool IsRemoved { get; }
  782. internal abstract bool IsDead { get; }
  783. /// <summary>
  784. /// True if the object is being tracked (perhaps during a recursive
  785. /// attach operation) but can be transitioned to other states.
  786. /// </summary>
  787. internal abstract bool IsWeaklyTracked { get; }
  788. internal abstract bool HasDeferredLoaders { get; }
  789. internal abstract bool HasChangedValues();
  790. internal abstract IEnumerable<ModifiedMemberInfo> GetModifiedMembers();
  791. internal abstract bool HasChangedValue(MetaDataMember mm);
  792. internal abstract bool CanInferDelete();
  793. internal abstract void AcceptChanges();
  794. internal abstract void ConvertToNew();
  795. internal abstract void ConvertToPossiblyModified();
  796. internal abstract void ConvertToPossiblyModified(object original);
  797. internal abstract void ConvertToUnmodified();
  798. internal abstract void ConvertToModified();
  799. internal abstract void ConvertToDeleted();
  800. internal abstract void ConvertToRemoved();
  801. internal abstract void ConvertToDead();
  802. /// <summary>
  803. /// Refresh the item by making the value passed in the current
  804. /// Database value, and refreshing the current values using the
  805. /// mode specified.
  806. /// </summary>
  807. internal abstract void Refresh(RefreshMode mode, object freshInstance);
  808. /// <summary>
  809. /// Does the refresh operation for a single member. This method does not
  810. /// update the baseline 'original' value. You must call
  811. /// Refresh(RefreshMode.KeepCurrentValues, freshInstance) to finish the refresh
  812. /// after refreshing individual members.
  813. /// </summary>
  814. /// <param name="member"></param>
  815. /// <param name="mode"></param>
  816. /// <param name="freshValue"></param>
  817. internal abstract void RefreshMember(MetaDataMember member, RefreshMode mode, object freshValue);
  818. /// <summary>
  819. /// Create a data-member only copy of the instance (no associations)
  820. /// </summary>
  821. /// <returns></returns>
  822. internal abstract object CreateDataCopy(object instance);
  823. internal abstract bool SynchDependentData();
  824. internal abstract bool IsPendingGeneration(IEnumerable<MetaDataMember> keyMembers);
  825. internal abstract bool IsMemberPendingGeneration(MetaDataMember keyMember);
  826. internal abstract void InitializeDeferredLoaders();
  827. }
  828. }