Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.Entity.Design / System / Data / WebControls / Design / EntityDataSourceDataSelection.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="EntityDataSourceDataSelection.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //
9 // Manages the properties that can be set on the second page of the wizard
10 //------------------------------------------------------------------------------
11
12 namespace System.Web.UI.Design.WebControls
13 {
14     using System.Collections.Generic;
15     using System.Data.Metadata.Edm;
16     using System.Diagnostics;
17     using System.Globalization;
18     using System.Text;
19
20     internal class EntityDataSourceDataSelection
21     {
22         #region Private static fields
23         // iterator prefix used in building and parsing Select property value
24         private static readonly string s_itKeyword = "it.";
25         // Placeholder item to indicate (None) on the EntityTypeFilter ComboBox
26         private static readonly EntityDataSourceEntityTypeFilterItem s_entityTypeFilterNoneItem =
27                 new EntityDataSourceEntityTypeFilterItem(Strings.Wizard_DataSelectionPanel_NoEntityTypeFilter);
28         #endregion
29
30         #region Private readonly fields
31         private readonly EntityDataSourceDataSelectionPanel _panel;
32         private readonly EntityDataSourceDesignerHelper _helper;
33         #endregion
34
35         #region Private fields for temporary storage of property values
36         private readonly EntityDataSourceState _entityDataSourceState;
37         private List<EntityDataSourceEntitySetNameItem> _entitySetNames;
38         private EntityDataSourceEntitySetNameItem _selectedEntitySetName;
39         private List<EntityDataSourceEntityTypeFilterItem> _entityTypeFilters;
40         private EntityDataSourceEntityTypeFilterItem _selectedEntityTypeFilter;
41
42         #region Select views
43         // The Data Selection wizard panel can display two kinds of views of the Select property:
44         //     (1) Simple Select View: CheckedListBox with a list of available entity type properties
45         //     (2) Advanced Select View: TextBox that allows any statement to be entered (no validation)
46         //
47         // When either view is visible to the user, the fields shown below for that view should be non-null, and the fields
48         // for the other view should be null.
49
50         // Simple Select View
51         // _selectedEntityTypeProperties contains a set of indexes of properties in _entityTypeProperties
52         private List<string> _entityTypeProperties; 
53         private List<int> _selectedEntityTypeProperties;
54
55         // Advanced Select View
56         private string _select;
57         #endregion
58
59         private bool _enableInsert;
60         private bool _enableUpdate;
61         private bool _enableDelete;
62         private readonly EntityDataSourceWizardForm _wizardForm;        
63         #endregion
64
65         #region Constructors
66         internal EntityDataSourceDataSelection(EntityDataSourceDataSelectionPanel panel, EntityDataSourceWizardForm wizard, EntityDataSourceDesignerHelper designerHelper, EntityDataSourceState entityDataSourceState)
67         {
68             _panel = panel;
69             _panel.Register(this);
70             _helper = designerHelper;
71
72             _entityDataSourceState = entityDataSourceState;
73             _wizardForm = wizard;
74         }
75         #endregion
76
77         #region Events
78         // Event handler to process notifications when a DefaultContainerName is selected on the ObjectContext configuration panel
79         internal void ContainerNameChangedHandler(object sender, EntityDataSourceContainerNameItem newContainerName)
80         {
81             // Load the entity sets for this container, don't select anything initially in the list
82             LoadEntitySetNames(newContainerName, null);
83             
84             // Reset the other controls that depend on the value of EntitySet
85             LoadEntityTypeFilters(null, null);
86             LoadSelect(String.Empty);
87         }
88         #endregion        
89
90         #region Methods to manage temporary state and wizard contents
91         // Used when the wizard is launched, to load existing property values from data source control
92         internal void LoadState()
93         {
94             LoadEntitySetNames(_helper.GetEntityContainerItem(_entityDataSourceState.DefaultContainerName), _entityDataSourceState.EntitySetName);
95             LoadEntityTypeFilters(_selectedEntitySetName, _entityDataSourceState.EntityTypeFilter);
96             LoadSelect(_entityDataSourceState.Select);
97             LoadInsertUpdateDelete();
98         }
99
100         // Save current wizard settings back to the EntityDataSourceState
101         internal void SaveState()
102         {
103             SaveEntitySetName();
104             SaveEntityTypeFilter();
105             SaveSelect();
106             SaveInsertUpdateDelete();
107             SaveEnableFlattening();
108         }
109
110         #region EntitySetName
111         // Find the specified entitySetName in the list or add it if it's not there
112         private EntityDataSourceEntitySetNameItem FindEntitySetName(string entitySetName)
113         {
114             if (!String.IsNullOrEmpty(entitySetName))
115             {
116                 EntityDataSourceEntitySetNameItem entitySetToSelect = null;
117                 foreach (EntityDataSourceEntitySetNameItem entitySetNameItem in _entitySetNames)
118                 {
119                     // Ignore case here when searching the list for a matching item, but set the temporary state property to the
120                     // correctly-cased version from metadata so that if the user clicks Finish, the correct one will be saved. This
121                     // allows some flexibility the designer without preserving an incorrectly-cased value that could cause errors at runtime.                    
122                     if (String.Equals(entitySetNameItem.EntitySetName, entitySetName, StringComparison.OrdinalIgnoreCase))
123                     {
124                         entitySetToSelect = entitySetNameItem;
125                     }
126                 }
127
128                 // didn't find a matching entityset, so just create a placeholder for one using the specified name and add it to the list
129                 if (entitySetToSelect == null)
130                 {
131                     entitySetToSelect = new EntityDataSourceEntitySetNameItem(entitySetName);
132                     _entitySetNames.Add(entitySetToSelect);                    
133                 }
134
135                 Debug.Assert(entitySetToSelect != null, "expected a non-null EntityDataSourceEntitySetNameItem");
136                 return entitySetToSelect;
137             }
138
139             return null;
140         }
141
142         // Populates the EntitySetName combobox with all of the discoverable EntitySets for the specified container.
143         // If the specified entitySetName is not empty, it is added to the list and selected as the initial value
144         // containerNameItem may not be backed by a real EntityContainer, in which case there is no way to look up the EntitySet in metadata
145         // devnote: This method should not automatically reset EntityTypeFilter and Select because it can be used to load the initial state
146         //          for the form, in which case we need to preserve any values that are already set on the data source control.
147         private void LoadEntitySetNames(EntityDataSourceContainerNameItem containerNameItem, string entitySetName)
148         {
149             // If this is a container that we found in the project's metadata, get a list of EntitySets for that container
150             if (containerNameItem != null && containerNameItem.EntityContainer != null)
151             {
152                 _entitySetNames = _helper.GetEntitySets(containerNameItem.EntityContainer, false /*sortResults*/);
153
154                 // Try to find the specified entityset in list and add it if it isn't there
155                 _selectedEntitySetName = FindEntitySetName(entitySetName);
156             }
157             else
158             {
159                 // if this is an unknown container, there is no way to find a list of entitysets from metadata
160                 // so just create a new list and placeholder for the specified entityset
161                 _entitySetNames = new List<EntityDataSourceEntitySetNameItem>();
162                 if (!String.IsNullOrEmpty(entitySetName))
163                 {
164                     _selectedEntitySetName = new EntityDataSourceEntitySetNameItem(entitySetName);
165                     _entitySetNames.Add(_selectedEntitySetName);
166                 }
167                 else
168                 {
169                     _selectedEntitySetName = null;
170                 }
171             }
172
173             // Sort the list now, after we may have added one above
174             _entitySetNames.Sort();
175
176             // Update the controls
177             _panel.SetEntitySetNames(_entitySetNames);
178             _panel.SetSelectedEntitySetName(_selectedEntitySetName);
179         }
180
181         // Set EntitySetName in temporary storage
182         internal void SelectEntitySetName(EntityDataSourceEntitySetNameItem selectedEntitySet)
183         {
184             _selectedEntitySetName = selectedEntitySet;
185             // Load the types for the selected EntitySet, don't select one initially
186             LoadEntityTypeFilters(selectedEntitySet, null);
187             // Reinitialize the Select control with a list of properties, don't preserve any existing Select value
188             LoadSelect(String.Empty);
189         }
190
191         private void SaveEntitySetName()
192         {
193             if (_selectedEntitySetName != null)
194             {
195                 _entityDataSourceState.EntitySetName = _selectedEntitySetName.EntitySetName;
196             }
197             else
198             {
199                 _entityDataSourceState.EntitySetName = String.Empty;
200             }
201         }
202         #endregion
203
204         #region EntityTypeFilter
205         // Populate a list with the base type for the EntitySet plus all derived types, and a special entry to indicate no filter
206         // devnote: This method should not automatically reset Select because it can be used to load the initial state
207         //          for the form, in which case we need to preserve any values that are already set on the data source control.
208         private void LoadEntityTypeFilters(EntityDataSourceEntitySetNameItem entitySetItem, string entityTypeFilter)
209         {
210             // If this is an EntitySet that we found in the project's metadata, get the type information
211             if (entitySetItem != null && entitySetItem.EntitySet != null)
212             {
213                 _entityTypeFilters = _helper.GetEntityTypeFilters(entitySetItem.EntitySet.ElementType, false /*sortResults*/);
214                 // add (None) to the beginning of the list
215                 _entityTypeFilters.Insert(0, s_entityTypeFilterNoneItem);
216
217                 // Try to find the specified type in list and add it if it isn't there
218                 _selectedEntityTypeFilter = FindEntityTypeFilter(entityTypeFilter);
219             }
220             else
221             {
222                 // if this is an unknown EntitySet, there is no way to find a list of types from metadata
223                 // so just create a new list and placeholder for the specified type
224                 _entityTypeFilters = new List<EntityDataSourceEntityTypeFilterItem>();
225                 _entityTypeFilters.Add(s_entityTypeFilterNoneItem);
226
227                 if (!String.IsNullOrEmpty(entityTypeFilter))
228                 {
229                     _selectedEntityTypeFilter = new EntityDataSourceEntityTypeFilterItem(entityTypeFilter);
230                     _entityTypeFilters.Add(_selectedEntityTypeFilter);
231                 }
232                 else
233                 {
234                     _selectedEntityTypeFilter = s_entityTypeFilterNoneItem;
235                 }
236             }
237
238             // Sort now after we might have added items above
239             _entityTypeFilters.Sort();
240
241             // Update the controls
242             _panel.SetEntityTypeFilters(_entityTypeFilters);
243             _panel.SetSelectedEntityTypeFilter(_selectedEntityTypeFilter);
244         }
245
246         // Find the specified entityTypeFilter in the list and add it if it's not there
247         private EntityDataSourceEntityTypeFilterItem FindEntityTypeFilter(string entityTypeFilter)
248         {
249             if (!String.IsNullOrEmpty(entityTypeFilter))
250             {
251                 EntityDataSourceEntityTypeFilterItem typeToSelect = null;
252                 foreach (EntityDataSourceEntityTypeFilterItem entityTypeFilterItem in _entityTypeFilters)
253                 {
254                     // Ignore case here when searching the list for a matching item, but set the temporary state property to the
255                     // correctly-cased version from metadata so that if the user clicks Finish, the correct one will be saved. This
256                     // allows some flexibility the designer without preserving an incorrectly-cased value that could cause errors at runtime.                    
257                     if (String.Equals(entityTypeFilterItem.EntityTypeName, entityTypeFilter, StringComparison.OrdinalIgnoreCase))
258                     {
259                         typeToSelect = entityTypeFilterItem;
260                     }
261                 }
262
263                 // didn't find a matching type, so just create a placeholder item and add it to the list
264                 if (typeToSelect == null)
265                 {
266                     typeToSelect = new EntityDataSourceEntityTypeFilterItem(entityTypeFilter);
267                     _entityTypeFilters.Add(typeToSelect);
268
269                 }
270
271                 Debug.Assert(typeToSelect != null, "expected a non-null string for EntityTypeFilter");
272                 return typeToSelect;
273             }
274
275             return s_entityTypeFilterNoneItem;
276         }
277
278         // Set EntityTypeFilter in temporary storage and load the Select property
279         internal void SelectEntityTypeFilter(EntityDataSourceEntityTypeFilterItem selectedEntityTypeFilter)
280         {
281             _selectedEntityTypeFilter = selectedEntityTypeFilter;
282             // Reinitialize the Select control with a list of properties, don't preserve any existing Select value
283             LoadSelect(String.Empty);
284         }
285
286         private void SaveEntityTypeFilter()
287         {
288             // If (None) is selected, it is the same as an empty string on the data source control
289             if (Object.ReferenceEquals(_selectedEntityTypeFilter, s_entityTypeFilterNoneItem))
290             {
291                 _entityDataSourceState.EntityTypeFilter = String.Empty;
292             }
293             else
294             {
295                 _entityDataSourceState.EntityTypeFilter = _selectedEntityTypeFilter.EntityTypeName;
296             }
297         }
298
299         #endregion
300
301         #region Select
302         // Load and parse the Select property
303         private void LoadSelect(string select)
304         {
305             Debug.Assert(_selectedEntityTypeFilter != null, "_selectedEntityTypeFilter should never be null");
306
307             EntityType entityType = GetSelectedEntityType();
308
309             if (entityType != null)
310             {
311                 // this is a real type from metadata, load its properties
312                 _entityTypeProperties = _helper.GetEntityTypeProperties(entityType);
313                 // add the 'Select All (Entity Value)' placeholder at the beginning of the list
314                 _entityTypeProperties.Insert(0, Strings.Wizard_DataSelectionPanel_SelectAllProperties);
315
316                 // parse the current value for the Select property to see if it can be displayed in the simple CheckedListBox view                
317                 if (TryParseSelect(select))
318                 {
319                     _select = null;
320
321                     // Update the controls
322                     _panel.SetEntityTypeProperties(_entityTypeProperties, _selectedEntityTypeProperties);
323                     UpdateInsertUpdateDeleteState();
324                     return;
325                 }
326                 // else we failed to parse the select into entity type properties on the specified type, so just use the advanced select view
327             } // else can't get a list of properties unless we have a known EntityType
328
329
330             // if we don't have a valid entity type or couldn't parse the incoming Select value, just display the advanced TextBox view
331             _entityTypeProperties = null;
332             _selectedEntityTypeProperties = null;
333             _select = select;
334
335             // Update the controls
336             _panel.SetSelect(_select);
337             UpdateInsertUpdateDeleteState();
338         }
339
340         // Build a value for the Select property from the selected values in the CheckedListBox
341         // Value will be in the from "it.Property1, it.Property2, it.Property3"
342         private string BuildSelect()
343         {
344             Debug.Assert(_selectedEntityTypeProperties != null && _selectedEntityTypeProperties.Count > 0, "expected non-null _selectedEntityTypeProperties with at least one value");
345
346             // 'Select All (Entity Value)' is the same thing as an empty string for the property
347             if (_selectedEntityTypeProperties[0] == 0)
348             {
349                 Debug.Assert(_selectedEntityTypeProperties.Count == 1, "'Select All (Entity Value)' should be the only property selected");
350                 return String.Empty;
351             }
352
353             StringBuilder selectProperties = new StringBuilder();
354             bool addComma = false;
355             foreach (int propertyIndex in _selectedEntityTypeProperties)
356             {
357                 if (addComma)
358                 {
359                     selectProperties.Append(", ");
360                 }
361                 else
362                 {
363                     addComma = true;
364                 }
365
366                 selectProperties.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", s_itKeyword, EscapePropertyName(_entityTypeProperties[propertyIndex]));
367
368             }
369             return selectProperties.ToString();
370         }
371
372         private static string EscapePropertyName(string propertyName)
373         {
374             return "[" + propertyName.Replace("]", "]]") + "]";
375         }
376
377         static string UnescapePropertyName(string name)
378         {
379             if (name[0] == '[' && name[name.Length - 1] == ']')
380             {
381                 return name.Substring(1, name.Length - 2).Replace("]]", "]");
382             }
383             else
384             {
385                 // else the property is not escaped at all or is not properly escaped. We can't parse it so just return.
386                 return name;
387             }            
388         }
389
390         // Parses the current Select property on the data source to see if it matches a specific format that we can use to display the properties
391         // in the CheckedListBox in the simple select wizard view
392         private bool TryParseSelect(string currentSelect)
393         {
394             bool parseSuccess = false; // gets set to true after the statement has been successfully parsed
395             if (!String.IsNullOrEmpty(currentSelect))
396             {
397                 // first try to split the string up into pieces divided by commas
398                 // expects a format like the following: (extra spaces around the commas should work as well)
399                 //     "it.KnownPropertyName1, it.KnownPropertyName2, it.KnownPropertyName3"                
400                 string[] tokenizedSelect = currentSelect.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
401
402                 bool foundUnknownProperty = false;
403                 List<int> selectedProperties = new List<int>();
404                 foreach (string token in tokenizedSelect)
405                 {
406                     string propertyName = token.Trim();
407
408                     // Does the current property token start with "it."?
409                     if (ReadItKeyword(propertyName))
410                     {
411                         // Does the rest of the property token match a known property name for the selected EntityTypeFilter?
412                         int propertyIndex = ReadPropertyName(propertyName.Substring(s_itKeyword.Length));
413                         if (propertyIndex == -1)
414                         {
415                             // the property was not known, so we can just stop looking
416                             foundUnknownProperty = true;
417                             break;
418                         }
419                         else
420                         {
421                             // this is a known property, so add its index to the list
422                             selectedProperties.Add(propertyIndex);
423                         }
424                     }
425                     else
426                     {
427                         // the property was not known, so we can just stop looking
428                         foundUnknownProperty = true;
429                         break;
430                     }
431                 }
432                 if (!foundUnknownProperty)
433                 {
434                     // if we never found anything unknown, the current list of properties is what we'll use to fill in the CheckedListBox
435                     _selectedEntityTypeProperties = selectedProperties;
436                     parseSuccess = true;
437                 }
438                 else
439                 {
440                     _selectedEntityTypeProperties = null;
441                 }
442             }
443             else
444             {
445                 // if Select is empty, we just want to add 'Select All (Entity Value)' to the list
446                 _selectedEntityTypeProperties = new List<int>();
447                 _selectedEntityTypeProperties.Add(0);
448                 parseSuccess = true;
449             }
450
451             return parseSuccess;
452         }
453
454         // Determines if the specified propertyName starts with "it." (case-insensitive)
455         private bool ReadItKeyword(string propertyName)
456         {
457             // will accept any casing of "it." here, although when the value is saved back to the property, it will be correctly lower-cased
458             return propertyName.StartsWith(s_itKeyword, StringComparison.OrdinalIgnoreCase);
459         }
460
461         // Determines if the specified propertyName matches one of the known properties for the selected type
462         private int ReadPropertyName(string propertyName)
463         {
464             for (int propIndex = 0; propIndex < _entityTypeProperties.Count; propIndex++)
465             {
466                 // Ignore case here when searching the list for a matching item, but set the temporary state property to the
467                 // correctly-cased version from metadata so that if the user clicks Finish, the correct one will be saved. This
468                 // allows some flexibility the designer without preserving an incorrectly-cased value that could cause errors at runtime.
469
470                 // Does the specified property name exactly match any of the properties for the selected EntityTypeFilter?
471                 if (String.Equals(UnescapePropertyName(propertyName), _entityTypeProperties[propIndex], StringComparison.OrdinalIgnoreCase))
472                 {
473                     return propIndex;
474                 }
475             }
476
477             return -1;
478         }
479
480         // Add the specified property to the list of selected entity properties used to build up the Select property
481         internal void SelectEntityProperty(int propertyIndex)
482         {
483             _selectedEntityTypeProperties.Add(propertyIndex);
484         }
485
486         internal void ClearAllSelectedProperties()
487         {
488             _selectedEntityTypeProperties.Clear();
489         }
490
491         // Remove specified entity property index from the selected list
492         internal void DeselectEntityProperty(int propertyIndex)
493         {
494             _selectedEntityTypeProperties.Remove(propertyIndex);
495         }
496
497         // Set Select property to the specified string (used with advanced select view)
498         internal void SelectAdvancedSelect(string select)
499         {
500             _select = select;
501         }
502
503         private void SaveSelect()
504         {
505             if (_select != null)
506             {
507                 _entityDataSourceState.Select = _select;
508             }
509             else
510             {
511                 Debug.Assert(_selectedEntityTypeProperties != null, "expected _entityTypeProperties to be non-null if _select is null");
512                 _entityDataSourceState.Select = BuildSelect();
513             }
514         }
515         #endregion
516
517         #region EnableInsertUpdateDelete
518         // Load the initial values for EnableInsert/EnableUpdate/EnableDelete CheckBoxes
519         private void LoadInsertUpdateDelete()
520         {
521             SelectEnableInsert(_entityDataSourceState.EnableInsert);
522             SelectEnableUpdate(_entityDataSourceState.EnableUpdate);
523             SelectEnableDelete(_entityDataSourceState.EnableDelete);
524
525             UpdateInsertUpdateDeleteState();
526         }
527
528         // Set EnableDelete in temporary storage
529         internal void SelectEnableDelete(bool enableDelete)
530         {
531             _enableDelete = enableDelete;
532         }
533
534         // Set EnableInsert in temporary storage
535         internal void SelectEnableInsert(bool enableInsert)
536         {
537             _enableInsert = enableInsert;
538         }
539
540         // Set EnableUpdate in temporary storage
541         internal void SelectEnableUpdate(bool enableUpdate)
542         {
543             _enableUpdate = enableUpdate;
544         }
545
546         private void SaveInsertUpdateDelete()
547         {
548             _entityDataSourceState.EnableInsert = _enableInsert;
549             _entityDataSourceState.EnableUpdate = _enableUpdate;
550             _entityDataSourceState.EnableDelete = _enableDelete;
551         }
552
553         /// <summary>
554         /// Update the panel control state based on the valued of enableInsert,
555         /// enableUpdate, enableDelete, and the selectedEntityTypeProperties
556         /// </summary>
557         internal void UpdateInsertUpdateDeleteState()
558         {
559             // Set the checkbox state for the panel controls
560             _panel.SetEnableInsertUpdateDelete(_enableInsert, _enableUpdate, _enableDelete);
561
562             // The InsertUpdateDelete panel should be enabled if:
563             // 1. Insert, Update, or Delete is selected -OR-
564             // 2. The EntitySelection has SelectAll checked
565             bool enablePanel = (_enableInsert || _enableUpdate || _enableDelete || 
566                         (_selectedEntityTypeProperties != null && 
567                          _selectedEntityTypeProperties.Count == 1 && 
568                          _selectedEntityTypeProperties[0] == 0));
569
570             _panel.SetEnableInsertUpdateDeletePanel(enablePanel);
571         }
572         #endregion        
573
574         #region  EnableFlattening
575
576         private EntityType GetSelectedEntityType()
577         {
578             EntityType entityType = null;
579
580             // determine which EntityType to load properties for, based on the value selected for EntityTypeFilter
581             if (Object.ReferenceEquals(_selectedEntityTypeFilter, s_entityTypeFilterNoneItem))
582             {
583                 // If (None) is selected, use the base type for the EntitySet if available
584                 if (_selectedEntitySetName != null && _selectedEntitySetName.EntitySet != null)
585                 {
586                     entityType = _selectedEntitySetName.EntitySet.ElementType;
587                 }
588                 // else the EntitySet base type is not known
589             }
590             else
591             {
592                 entityType = _selectedEntityTypeFilter.EntityType; // could still be null if the type if not known in metadata
593             }
594
595             return entityType;
596         }
597
598         private void SaveEnableFlattening()
599         {
600             bool enableFlattening = false;
601
602             EntityType entityType = GetSelectedEntityType();
603
604             if (entityType != null)
605             {
606                 foreach (EdmMember member in entityType.Members)
607                 {
608                     // If there is a complex member, enable flattening
609                     if (member.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType)
610                     {
611                         enableFlattening = true;
612                         break;
613                     }
614                     else if (member.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
615                     {
616                         NavigationProperty navProp = (NavigationProperty)member;
617                         if (navProp.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)
618                         {
619                             AssociationType associationType = navProp.ToEndMember.DeclaringType as AssociationType;
620                             if (!associationType.IsForeignKey)
621                             {
622                                 // If there is an independent association, enable flattening
623                                 enableFlattening = true;
624                                 break;
625                             }
626                         }
627                     }
628                 }
629             }
630             else
631             {
632                 // Projection
633                 enableFlattening = true;
634             }
635
636             _entityDataSourceState.EnableFlattening = enableFlattening;
637         }
638
639         #endregion
640
641         #endregion
642
643         #region Wizard button state management
644         internal void UpdateWizardState()
645         {
646             // EntitySetName must be selected and a Select must be configured or must be the empty string
647             _wizardForm.SetCanFinish(_selectedEntitySetName != null && (_select != null || _selectedEntityTypeProperties.Count > 0));
648         }
649         #endregion
650     }
651 }