Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Web.Entity / System / Data / WebControls / EntityDataSourceWrapper.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntityDataSourceWrapper.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9 using System;
10 using System.Data;
11 using System.Configuration;
12 using System.ComponentModel;
13 using System.Data.Common;
14 using System.Data.Metadata.Edm;
15 using System.Reflection;
16 using System.Data.Objects;
17 using System.Data.Objects.DataClasses;
18 using System.Collections.Generic;
19 using System.Diagnostics;
20 using System.Linq;
21 using System.Collections.ObjectModel;
22 using System.Text;
23 using System.Web.UI;
24
25 namespace System.Web.UI.WebControls
26 {
27     /// <summary>
28     /// Wraps an entity displayed in the data source in an ICustomTypeDescriptor
29     /// implementation that flattens complex types and exposes references.
30     /// </summary>
31     internal class EntityDataSourceWrapper : ICustomTypeDescriptor
32     {
33         private readonly EntityDataSourceWrapperCollection _collection;
34         private readonly ObjectStateEntry _stateEntry;
35
36         internal EntityDataSourceWrapper(EntityDataSourceWrapperCollection collection, object trackedEntity)
37         {
38             EntityDataSourceUtil.CheckArgumentNull(collection, "collection");
39             EntityDataSourceUtil.CheckArgumentNull(trackedEntity, "trackedEntity");
40
41             this._collection = collection;
42
43             // retrieve state entry
44             if (!this._collection.Context.ObjectStateManager.TryGetObjectStateEntry(trackedEntity, out _stateEntry))
45             {
46                 throw new ArgumentException(Strings.ComponentNotFromProperCollection, "trackedEntity");
47             }
48         }
49
50         /// <summary>
51         /// Gets entity wrapped by this type descriptor.
52         /// </summary>
53         internal object WrappedEntity
54         {
55             get
56             {
57                 return this._stateEntry.Entity;
58             }
59         }
60
61         internal RelationshipManager RelationshipManager
62         {
63             get
64             {
65                 return this._stateEntry.RelationshipManager;
66             }
67         }
68
69         /// <summary>
70         /// Gets collection containing this wrapper.
71         /// </summary>
72         internal EntityDataSourceWrapperCollection Collection
73         {
74             get { return this._collection; }
75         }
76
77         #region ICustomTypeDescriptor Implementation
78         System.ComponentModel.AttributeCollection System.ComponentModel.ICustomTypeDescriptor.GetAttributes() { return new System.ComponentModel.AttributeCollection(); }
79         string ICustomTypeDescriptor.GetClassName() { return null; }
80         string ICustomTypeDescriptor.GetComponentName() { return null; }
81         TypeConverter ICustomTypeDescriptor.GetConverter() { return null; }
82         EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return null; }
83         PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return null; }
84         object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return null; }
85         EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { return null; }
86         EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { return null; }
87
88
89         public PropertyDescriptorCollection GetProperties()
90         {
91             return ((ITypedList)this._collection).GetItemProperties(null);
92         }
93
94         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
95         {
96             return ((ICustomTypeDescriptor)this).GetProperties();
97         }
98
99         object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
100         {
101             return this.WrappedEntity;
102         }
103         #endregion ICustomTypeDescriptor Implementation
104
105         /// <summary>
106         /// Use this method to set the properties on the wrapped entity
107         /// </summary>
108         /// <param name="propertiesFromViewState"></param>
109         /// <param name="wrapper"></param>
110         /// <param name="overwriteSameValue"></param>
111         internal void SetAllProperties(Dictionary<string, object> propertiesFromViewState, bool overwriteSameValue,
112             ref Dictionary<string, Exception> propertySettingExceptionsCaught)
113         {
114             // We aggregate the reference descriptors rather than setting them directly
115             // to account for compound keys (we need all components of the key to create
116             // an EntityKey that can be set on the EntityReference)
117             var referenceList = new List<KeyValuePair<EntityDataSourceReferenceKeyColumn, object>>();
118
119             foreach (EntityDataSourceWrapperPropertyDescriptor descriptor in _collection.AllPropertyDescriptors)
120             {
121                 // figure out which display name to match for this descriptor
122                 string displayName = descriptor.Column.DisplayName;
123
124                 // if we have a controlling column, use its display name instead
125                 if (descriptor.Column.ControllingColumn != null)
126                 {
127                     displayName = descriptor.Column.ControllingColumn.DisplayName;
128                 }
129
130                 object value;
131                 if (propertiesFromViewState.TryGetValue(displayName, out value))
132                 {
133                     // get all changed ReferencePropertyDescriptor from ViewState
134                     EntityDataSourceReferenceKeyColumn referenceColumn = descriptor.Column as EntityDataSourceReferenceKeyColumn;
135
136                     // convert the value as needed
137                     object adjustedValue = EntityDataSourceUtil.ConvertType(value, descriptor.PropertyType, descriptor.DisplayName);
138
139                     if (null != referenceColumn)
140                     {
141                         referenceList.Add(new KeyValuePair<EntityDataSourceReferenceKeyColumn, object>(
142                                 referenceColumn, adjustedValue));
143                         continue;
144                     }
145
146                     if (overwriteSameValue || adjustedValue != descriptor.GetValue(this))
147                     {
148                         if (EntityDataSourceUtil.NullCanBeAssignedTo(descriptor.PropertyType) || null != adjustedValue)
149                         {
150                             try
151                             {
152                                 descriptor.SetValue(this, adjustedValue);
153                             }
154                             catch (Exception e)
155                             {
156                                 // The property descriptor uses reflection to set the property. Therefore, the inner exception contains the actual message.
157                                 Exception exceptionToThrow = e;
158                                 if (e.InnerException != null)
159                                 {
160                                     exceptionToThrow = e.InnerException;
161                                 }
162                                 if (null == propertySettingExceptionsCaught)
163                                 {
164                                     propertySettingExceptionsCaught = new Dictionary<string, Exception>();
165                                 }
166                                 propertySettingExceptionsCaught.Add(descriptor.DisplayName, exceptionToThrow);
167                             }
168                         }
169                     }
170                 }
171             }
172
173             // aggregate setting for EntityKey
174             SetEntityKeyProperties(referenceList, overwriteSameValue);
175         }
176
177         private void SetEntityKeyProperties(
178             List<KeyValuePair<EntityDataSourceReferenceKeyColumn, object>> referenceList, bool overwriteSameValue)
179         {
180             EntityDataSourceUtil.CheckArgumentNull(referenceList, "referenceList");
181
182             var groups = referenceList.GroupBy(r => r.Key.Group);
183
184             foreach (var group in groups)
185             {
186                 Dictionary<string, object> partialKeys = new Dictionary<string, object>();
187
188                 foreach (KeyValuePair<EntityDataSourceReferenceKeyColumn, object> reference in group)
189                 {
190                     // convert the value as needed
191                     EntityDataSourceReferenceKeyColumn column = reference.Key;
192                     object keyValue = reference.Value;
193
194                     if (null == keyValue)
195                     {
196                         partialKeys = null;
197                         break;
198                     }
199
200                     partialKeys.Add(column.KeyMember.Name, keyValue);
201                 }
202
203                 // we only set the entitykey for once, although there might be more than one 
204                 // properties descriptor associated with the same entitykey
205                 group.Key.SetKeyValues(this, partialKeys);
206             }
207         }
208     }
209 }