1
//------------------------------------------------------------------------------
2 // <copyright file="EntityDataSourceConfigureObjectContext.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
9 // Manages the properties that can be set on the first page of the wizard
10 //------------------------------------------------------------------------------
12 using System
.Collections
.Generic
;
13 using System
.Web
.UI
.Design
.WebControls
.Util
;
14 using System
.Diagnostics
;
15 using System
.Globalization
;
16 using System
.Windows
.Forms
;
18 namespace System
.Web
.UI
.Design
.WebControls
20 // delegate for event handler to process notifications when the DefaultContainerName is changed
21 internal delegate void EntityDataSourceContainerChangedEventHandler(object sender
, EntityDataSourceContainerNameItem newContainerName
);
23 internal class EntityDataSourceConfigureObjectContext
25 #region Private readonly fields
26 private readonly EntityDataSourceConfigureObjectContextPanel _panel
;
27 private readonly EntityDataSourceDesignerHelper _helper
;
30 #region Private writeable fields
31 private EntityDataSourceContainerChangedEventHandler _containerNameChanged
; // used to notify the DataSelection panel that a change has been made
34 #region Private fields for temporary storage of property values
35 private EntityConnectionStringBuilderItem _selectedConnectionStringBuilder
;
36 private bool _connectionStringHasValue
;
37 private List
<EntityConnectionStringBuilderItem
> _namedConnections
;
38 private List
<EntityDataSourceContainerNameItem
> _containerNames
;
39 private EntityDataSourceContainerNameItem _selectedContainerName
;
40 private readonly EntityDataSourceState _entityDataSourceState
;
41 private readonly EntityDataSourceWizardForm _wizardForm
;
45 internal EntityDataSourceConfigureObjectContext(EntityDataSourceConfigureObjectContextPanel panel
, EntityDataSourceWizardForm wizardForm
, EntityDataSourceDesignerHelper helper
, EntityDataSourceState entityDataSourceState
)
49 Cursor
.Current
= Cursors
.WaitCursor
;
53 // Explicitly load metadata here to ensure that we get the latest changes in the project
54 _helper
.ReloadResources();
56 _panel
.Register(this);
58 _wizardForm
= wizardForm
;
59 _entityDataSourceState
= entityDataSourceState
;
63 Cursor
.Current
= Cursors
.Default
;
69 internal event EntityDataSourceContainerChangedEventHandler ContainerNameChanged
73 _containerNameChanged
+= value;
77 _containerNameChanged
-= value;
81 // Fires the event to notify that a container has been chosen from the list
82 private void OnContainerNameChanged(EntityDataSourceContainerNameItem selectedContainerName
)
84 if (_containerNameChanged
!= null)
86 _containerNameChanged(this, selectedContainerName
);
92 #region Methods to manage temporary state and wizard contents
93 // Save current wizard settings back to the EntityDataSourceState
94 internal void SaveState()
96 SaveConnectionString();
100 // Load the initial state of the wizard
101 internal void LoadState()
103 LoadConnectionStrings();
104 LoadContainerNames(_entityDataSourceState
.DefaultContainerName
, true /*initialLoad*/);
107 #region DefaultContainerName
109 /// Populates the DefaultContainerName ComboBox with all of the EntityContainers in the loaded metadata
110 /// If the specified DefaultContainerName property on the data source control is not empty and 'initialLoad' is true,
111 /// it is added to the list and selected in the control
113 /// <param name="containerName">The container name to find</param>
114 /// <param name="initialLoad">if true, this is the initial load so the container name is added to the list if it is not found.</param>
115 private void LoadContainerNames(string containerName
, bool initialLoad
)
117 // Get a list of EntityContainers from the metadata in the connection string
118 _containerNames
= _helper
.GetContainerNames(false /*sortResults*/);
120 // Try to find the specified container in list
121 _selectedContainerName
= FindContainerName(containerName
, initialLoad
/*addIfNotFound*/);
123 // Sort the list now, after we may have added a new entry above
124 _containerNames
.Sort();
126 // Update the controls
127 _panel
.SetContainerNames(_containerNames
);
128 _panel
.SetSelectedContainerName(_selectedContainerName
, initialLoad
/*initialLoad*/);
132 /// Find the current container in the current list of containers
134 /// <param name="containerName">The container name to find</param>
135 /// <param name="addIfNotFound">if true, adds the container name to the list if it is not found.</param>
136 /// <returns></returns>
137 private EntityDataSourceContainerNameItem
FindContainerName(string containerName
, bool addIfNotFound
)
139 Debug
.Assert(_containerNames
!= null, "_containerNames have already been initialized and should not be null");
141 if (!String
.IsNullOrEmpty(containerName
))
143 EntityDataSourceContainerNameItem containerToSelect
= null;
144 foreach (EntityDataSourceContainerNameItem containerNameItem
in _containerNames
)
146 // Ignore case here when searching the list for a matching item, but set the temporary state property to the
147 // correctly-cased version from metadata so that if the user clicks Finish, the correct one will be saved. This
148 // allows some flexibility the designer without preserving an incorrectly-cased value that could cause errors at runtime.
149 if (String
.Equals(containerName
, containerNameItem
.EntityContainerName
, StringComparison
.OrdinalIgnoreCase
))
151 containerToSelect
= containerNameItem
;
155 // didn't find a matching container, so just create a placeholder for one using the specified name and add it to the list
156 if (containerToSelect
== null && addIfNotFound
)
158 containerToSelect
= new EntityDataSourceContainerNameItem(containerName
);
159 _containerNames
.Add(containerToSelect
);
162 Debug
.Assert(addIfNotFound
== false || containerToSelect
!= null, "expected a non-null EntityDataSourceContainerNameItem");
163 return containerToSelect
;
169 // Set the container name in temporary storage, update the connection string, and fire the event so the EntitySet will know there has been a change
170 internal void SelectContainerName(EntityDataSourceContainerNameItem selectedContainer
)
172 _selectedContainerName
= selectedContainer
;
175 OnContainerNameChanged(_selectedContainerName
);
178 private void SaveContainerName()
180 Debug
.Assert(_selectedContainerName
!= null, "wizard data should not be saved if container name is empty");
181 _entityDataSourceState
.DefaultContainerName
= _selectedContainerName
.EntityContainerName
;
185 #region ConnectionString
186 // Populates the NamedConnection ComboBox with all of the EntityClient connections from the web.config.
187 // If the specified connectionString is a named connection (if it contains "name=ConnectionName"), it is added to the list and selected.
188 // If the specified connectionString is not a named connection, the plain connection string option is selected and populated with the specified value.
189 private void LoadConnectionStrings()
191 // Get a list of all named EntityClient connections in the web.config
192 _namedConnections
= _helper
.GetNamedEntityClientConnections(false /*sortResults*/);
194 EntityConnectionStringBuilderItem connStrBuilderItem
= _helper
.GetEntityConnectionStringBuilderItem(_entityDataSourceState
.ConnectionString
);
195 Debug
.Assert(connStrBuilderItem
!= null, "expected GetEntityConnectionStringBuilder to always return non-null");
197 if (connStrBuilderItem
.IsNamedConnection
)
199 // Try to find the specified connection in the list or add it
200 connStrBuilderItem
= FindCurrentNamedConnection(connStrBuilderItem
);
201 Debug
.Assert(connStrBuilderItem
!= null, "expected a non-null connStrBuilderItem for the named connection because it should have added it if it didn't exist");
204 // Sort results now, after we may have added a new item above
205 _namedConnections
.Sort();
207 SelectConnectionStringBuilder(connStrBuilderItem
, false /*resetContainer*/);
209 // Update the controls
210 _panel
.SetNamedConnections(_namedConnections
);
211 _panel
.SetConnectionString(_selectedConnectionStringBuilder
);
214 // Find the current named connection in the list of connections
215 // The returned item may refer to the same connection as the specified item, but it will be the actual reference from the list
216 private EntityConnectionStringBuilderItem
FindCurrentNamedConnection(EntityConnectionStringBuilderItem newBuilderItem
)
218 Debug
.Assert(_namedConnections
!= null, "_namedConnections should have already been initialized and should not be null");
219 Debug
.Assert(newBuilderItem
!= null && newBuilderItem
.IsNamedConnection
, "expected non-null newBuilderItem");
221 foreach (EntityConnectionStringBuilderItem namedConnectionItem
in _namedConnections
)
223 if (((IComparable
<EntityConnectionStringBuilderItem
>)newBuilderItem
).CompareTo(namedConnectionItem
) == 0)
225 // returning the one that was actually in the list, so we can select it in the control
226 return namedConnectionItem
;
230 // didn't find it in the list, so add it
231 _namedConnections
.Add(newBuilderItem
);
232 return newBuilderItem
;
235 internal EntityConnectionStringBuilderItem
GetEntityConnectionStringBuilderItem(string connectionString
)
237 return _helper
.GetEntityConnectionStringBuilderItem(connectionString
);
240 // Set the connection string in temporary storage
241 // Returns true if the metadata was successfully loaded for the specified connections
242 internal bool SelectConnectionStringBuilder(EntityConnectionStringBuilderItem selectedConnection
, bool resetContainer
)
244 _selectedConnectionStringBuilder
= selectedConnection
;
246 bool metadataLoaded
= false;
247 if (selectedConnection
!= null)
249 if (selectedConnection
.EntityConnectionStringBuilder
!= null)
251 metadataLoaded
= _helper
.LoadMetadata(selectedConnection
.EntityConnectionStringBuilder
);
255 // Since we don't have a valid connection string builder, we don't have enough information to load metadata.
256 // Clear any existing metadata so we don't see an old item collection on any subsequent calls that access it.
257 // Don't need to display an error here because that was handled by the caller who created the builder item
258 _helper
.ClearMetadata();
262 // Reset the list of containers if requested and set the ComboBox to have no selection.
263 // In some cases the containers do not need to be reset because the caller wants to delay that until a later event or wants to preserve a specific value
266 string defaultContainerName
= _selectedConnectionStringBuilder
.EntityConnectionStringBuilder
== null ? null : _selectedConnectionStringBuilder
.EntityConnectionStringBuilder
.Name
;
267 LoadContainerNames(defaultContainerName
, false /*initialLoad*/);
270 // Update the controls
273 return metadataLoaded
;
276 // Set a flag indicating that the connection string textbox or named connection dropdown has a value
277 // This value has not be verified at this point, or may not even be complete, so we don't want to validate it yet and turn it into a builder
278 internal void SelectConnectionStringHasValue(bool connectionStringHasValue
)
280 _connectionStringHasValue
= connectionStringHasValue
;
282 // Update the controls
286 private void SaveConnectionString()
288 Debug
.Assert(_selectedConnectionStringBuilder
!= null, "wizard data should not be saved if connection string is empty");
289 _entityDataSourceState
.ConnectionString
= _selectedConnectionStringBuilder
.ConnectionString
;
296 #region Wizard button state management
297 // Update the state of the wizard buttons
298 internal void UpdateWizardState()
300 _wizardForm
.SetCanNext(this.CanEnableNext
);
302 // Finish button should never be enabled at this stage
303 _wizardForm
.SetCanFinish(false);
306 // Next button can only be enabled when the following are true:
307 // (1) DefaultContainerName has a value
308 // (2) Either a named connection is selected or the connection string textbox has a value
309 internal bool CanEnableNext
313 return _selectedContainerName
!= null && _connectionStringHasValue
;