Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / ObjectStateEntryOriginalDbUpdatableDataRecord.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ObjectStateEntryOriginalDbUpdatableDataRecord.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System;
10 using System.ComponentModel;
11 using System.Data;
12 using System.Data.Common;
13 using System.Data.Metadata.Edm;
14 using System.Diagnostics;
15 using System.Reflection;
16
17 namespace System.Data.Objects
18 {
19     // Internal version of writeable original values record is used by all internal operations that need to set original values, such as PreserveChanges queries
20     // This version should never be returned to the user, because it doesn't enforce any necessary restrictions.
21     // See ObjectStateEntryOriginalDbUpdatableDataRecord_Public for user scenarios.
22     internal class ObjectStateEntryOriginalDbUpdatableDataRecord_Internal : OriginalValueRecord
23     {
24         internal ObjectStateEntryOriginalDbUpdatableDataRecord_Internal(EntityEntry cacheEntry, StateManagerTypeMetadata metadata, object userObject)
25             : base(cacheEntry, metadata, userObject)
26         {
27             EntityUtil.CheckArgumentNull(cacheEntry, "cacheEntry");
28             EntityUtil.CheckArgumentNull(userObject, "userObject");
29             EntityUtil.CheckArgumentNull(metadata, "metadata");
30             Debug.Assert(!cacheEntry.IsKeyEntry, "Cannot create an ObjectStateEntryOriginalDbUpdatableDataRecord_Internal for a key entry");
31             switch (cacheEntry.State)
32             {
33                 case EntityState.Unchanged:
34                 case EntityState.Modified:
35                 case EntityState.Deleted:
36                     break;
37                 default:
38                     Debug.Assert(false, "An OriginalValueRecord cannot be created for an object in an added or detached state.");
39                     break;
40             }
41         }
42         protected override object GetRecordValue(int ordinal)
43         {
44             Debug.Assert(!_cacheEntry.IsRelationship, "should not be relationship");
45             return (_cacheEntry as EntityEntry).GetOriginalEntityValue(_metadata, ordinal, _userObject, ObjectStateValueRecord.OriginalUpdatableInternal);
46         }
47         protected override void SetRecordValue(int ordinal, object value)
48         {
49             Debug.Assert(!_cacheEntry.IsRelationship, "should not be relationship");
50             (_cacheEntry as EntityEntry).SetOriginalEntityValue(_metadata, ordinal, _userObject, value);
51         }
52     }
53
54     // Public version of writable original values record that is to be returned to the user for setting original values directly.
55     // Although this class is actually internal, it is the version that implements the writeable original values functionality returned through the public surface.
56     // This version must maintain information about the index of the top-level entity property that corresponds to this record, because the record
57     // may represent a complex type somewhere in an entity hierarchy and this is the only way we know which entity property it is associated with.
58     // This version also does minimal necessary validation on the values that the user is trying to set.
59     internal sealed class ObjectStateEntryOriginalDbUpdatableDataRecord_Public : ObjectStateEntryOriginalDbUpdatableDataRecord_Internal
60     {
61         // Will be EntityEntry.s_EntityRoot for entities and for complex types will be the index of the top-level entity property related to this complex type
62         int _parentEntityPropertyIndex;
63
64         internal ObjectStateEntryOriginalDbUpdatableDataRecord_Public(EntityEntry cacheEntry, StateManagerTypeMetadata metadata, object userObject, int parentEntityPropertyIndex)
65             : base(cacheEntry, metadata, userObject)
66         {
67             _parentEntityPropertyIndex = parentEntityPropertyIndex;
68         }
69
70         protected override object GetRecordValue(int ordinal)
71         {
72             Debug.Assert(!_cacheEntry.IsRelationship, "should not be relationship");
73             return (_cacheEntry as EntityEntry).GetOriginalEntityValue(_metadata, ordinal, _userObject, ObjectStateValueRecord.OriginalUpdatablePublic, GetPropertyIndex(ordinal));
74         }
75
76         protected override void SetRecordValue(int ordinal, object value)
77         {
78             StateManagerMemberMetadata member = _metadata.Member(ordinal);
79
80             // We do not allow setting complex properties through writeable original values.
81             // Instead individual scalar properties can be set on a data record that represents the complex type.
82             if (member.IsComplex)
83             {
84                 throw EntityUtil.SetOriginalComplexProperties(member.CLayerName);
85             }
86
87             // Null values are represented in data records as DBNull.Value, so translate appropriately
88             object fieldValue = value ?? DBNull.Value;
89
90             EntityEntry entry = _cacheEntry as EntityEntry;
91             EntityState oldState = entry.State;
92
93             // Only update the original values if the new value is different from the value currently set on the entity
94             if (entry.HasRecordValueChanged(this, ordinal, fieldValue))
95             {
96                 // Since the original value is going to be set, validate that is doesn't violate any restrictions
97
98                 // Throw if trying to change the original value of the primary key
99                 if (member.IsPartOfKey)
100                 {
101                     throw EntityUtil.SetOriginalPrimaryKey(member.CLayerName);
102                 }
103
104                 // Verify non-nullable EDM members are not being set to null
105                 // Need to continue allowing CLR reference types to be set to null for backwards compatibility
106                 Type memberClrType = member.ClrType;
107                 if ((object)DBNull.Value == fieldValue &&
108                     memberClrType.IsValueType &&
109                     !member.CdmMetadata.Nullable)
110                 {
111                     // Throw if the underlying CLR type of this property is not nullable, and it is being set to null
112                     throw EntityUtil.NullOriginalValueForNonNullableProperty(member.CLayerName, member.ClrMetadata.Name, member.ClrMetadata.DeclaringType.FullName);
113                 }
114
115                 base.SetRecordValue(ordinal, value);
116
117                 // Update the state of the ObjectStateEntry if it has been marked as Modified
118                 if (oldState == EntityState.Unchanged && entry.State == EntityState.Modified)
119                 {
120                     entry.ObjectStateManager.ChangeState(entry, oldState, EntityState.Modified);
121                 }
122
123                 // Set the individual property to modified
124                 entry.SetModifiedPropertyInternal(GetPropertyIndex(ordinal));
125             }
126         }
127
128         // For entities the property index is the specified ordinal, but otherwise it's the top-level entity property index that we have saved
129         private int GetPropertyIndex(int ordinal)
130         {
131             return _parentEntityPropertyIndex == EntityEntry.s_EntityRoot ? ordinal : _parentEntityPropertyIndex;
132         }
133     }
134 }