EntityDataSourceView.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataWebControls / System / Data / WebControls / EntityDataSourceView.cs / 1 / EntityDataSourceView.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System;
using System.Collections; 
using System.Data;
using System.Data.Common;
using System.Data.Objects;
using System.Data.Metadata.Edm; 
using System.Configuration;
using System.Diagnostics; 
using System.Globalization; 
using System.Linq;
using System.Reflection; 
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls; 
using System.Web.DynamicData;
using System.Data.Objects.DataClasses; 
using System.Collections.Specialized; 
using System.ComponentModel;
using System.Collections.Generic; 
using System.Text;
using System.Collections.ObjectModel;

 
namespace System.Web.UI.WebControls
{ 
 
    public sealed class EntityDataSourceView : DataSourceView, IStateManager
    { 
        private EntityDataSource _owner;
        private ObjectContext _ctx = null;
        private static readonly ObjectParameter[] _noParameters = new ObjectParameter[0] { };
        readonly static Dictionary emptyDictionary = new Dictionary(); 
        private ReadOnlyMetadataCollection _keyMembers = null;
        private EntityDataSourceWrapperCollection _wrapperCollection = null; 
 
        //
        private static readonly object EventContextCreated = new object(); 
        private static readonly object EventContextCreating = new object();
        private static readonly object EventContextDisposing = new object();
        private static readonly object EventDeleted = new object();
        private static readonly object EventDeleting = new object(); 
        private static readonly object EventInserted = new object();
        private static readonly object EventInserting = new object(); 
        private static readonly object EventSelected = new object(); 
        private static readonly object EventSelecting = new object();
        private static readonly object EventUpdated = new object(); 
        private static readonly object EventUpdating = new object();
        private static readonly object EventException = new object();

        // values saved in ViewState 
        private bool _disableUpdates = false;
        private bool _tracking = false; 
 
        private Dictionary _originalProperties;
 
        #region Constructor
        internal EntityDataSourceView(EntityDataSource owner, string viewName)
            : base(owner, viewName)
        { 
            _owner = owner;
        } 
        #endregion Constructor 

        #region ExecuteSelect 
        protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
        {
            if (!string.IsNullOrEmpty(_owner.Select))
            { 
                return ExecuteSelect(arguments, EntityDataSourceRecordQueryBuilder.Create);
            } 
            else 
            {
                return ExecuteSelect(arguments, EntityDataSourceObjectQueryBuilder.Create); 
            }
        }

        private IEnumerable ExecuteSelect(DataSourceSelectArguments arguments, EntityDataSourceQueryBuilder.Creator qbConstructor) 
        {
            _wrapperCollection = null; 
 
            AddSupportedCapabilities(arguments);
            arguments.RaiseUnsupportedCapabilitiesError(this); 

            ConstructContext();

            EntityDataSourceSelectingEventArgs selectArgs = 
                new EntityDataSourceSelectingEventArgs(_owner, arguments);
 
            OnSelecting(selectArgs); 
            if (selectArgs.Cancel)
            { 
                return null;
            }

            _disableUpdates = _owner.ValidateUpdatableConditions(); 

            if (_owner.ValidateWrappable()) 
            { 
                _wrapperCollection = new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType);
            } 

            string whereClause;
            ObjectParameter[] whereParameters;
            GenerateWhereClause(out whereClause, out whereParameters); 
            string entitySetQueryExpression = GenerateEntitySetQueryExpression();
 
 
            OrderByBuilder orderByBuilder = new OrderByBuilder(arguments.SortExpression, _wrapperCollection,
                _owner.OrderBy, _owner.AutoGenerateOrderByClause, _owner.OrderByParameters, 
                CanPage, //There's no need to generate the default OrderBy clause if paging is disabled. Prevents an unnecessary sort at the server.
                _owner);

            EntityDataSourceQueryBuilder queryBuilder = 
                qbConstructor(
                        arguments, 
                        _owner.CommandText, _owner.GetCommandParameters(), 
                        whereClause, whereParameters, entitySetQueryExpression,
                        _owner.Select, _owner.GroupBy, _owner.GetSelectParameters(), 
                        orderByBuilder,
                        _owner.Include);

 
            // SelectedEventArgs.TotalRowCount has three possible states:
            //  1. The databound control requests it via arguments.RetrieveTotalRowCount 
            //     This returns the total number of rows on all pages. 
            //     arguments.TotalRowCount is only set if arguments.RetrieveTotalRowCount is true.
            //  2. Paging is disabled via !CanPage. 
            //     This returns the number of rows returned. On one page.
            //  3. Else it returns negative one.
            int totalRowCount = -1;
 
            ObjectQuery queryT = queryBuilder.Build(Context, arguments.RetrieveTotalRowCount);
 
            if (arguments.RetrieveTotalRowCount) 
            {
                // The Selected event args gets the total number of rows on all pages. 
                totalRowCount = queryBuilder.TotalCount;
                // The databound control requests totalRowCount. We return the total rows on all pages.
                arguments.TotalRowCount = totalRowCount;
            } 

            if (!_disableUpdates) 
            { 
                Debug.Assert(null != EntitySet, "Can't be updatable with a null EntitySet");
                EntityDataSourceUtil. 
                  CheckNonPolymorphicTypeUsage(EntitySet.ElementType,
                                             Context.MetadataWorkspace.GetItemCollection(DataSpace.CSpace),
                                             _owner.EntityTypeFilter);
            } 

 
            IEnumerable entities = null; 
            try
            { 
                entities = queryBuilder.Execute(queryT);
            }
            catch (Exception e)
            { 
                entities = null;
                EntityDataSourceSelectedEventArgs selectedArgs = new EntityDataSourceSelectedEventArgs(e); 
                OnSelected(selectedArgs); 
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Select));
                if (!selectedArgs.ExceptionHandled) 
                {
                    throw;
                }
            } 

            // OnSelected outside "try" to prevent double-calling OnSelected if the first OnSelected throws 
            if (null != entities) 
            {
                if (!CanPage) 
                {
                    // Paging is disabled, totalRowCount gets the number of rows returned from this query.
                    totalRowCount = ((IList)entities).Count;
                } 
                OnSelected(new EntityDataSourceSelectedEventArgs(Context, entities, totalRowCount, arguments));
            } 
 
            if (null == _wrapperCollection)
            { 
                return entities;
            }

            foreach (object element in entities) 
            {
                _wrapperCollection.AddWrappedEntity(element); 
            } 

            return _wrapperCollection; 
        }

        /// 
        /// Restrict the query to the type specified by EntityTypeFilter. 
        /// 
        ///  
        private string GenerateEntitySetQueryExpression() 
        {
 
            if (null == EntitySet)
            {
                return String.Empty;
            } 

            string entitySetIdentifier = EntityDataSourceUtil.CreateEntitySqlSetIdentifier(EntitySet); 
 
            if (String.IsNullOrEmpty(_owner.EntityTypeFilter))
            { 
                return entitySetIdentifier;
            }

            Debug.Assert(EntityOSpaceType != null, "EntitySet is not null, EntityOSpaceType should also be defined."); 

            // oftype ([Northwind].[Products], only [Northwind].[ActiveProducts]) 
            StringBuilder queryExpressionBuilder = new StringBuilder(); 
            queryExpressionBuilder.Append("oftype (");
            queryExpressionBuilder.Append(entitySetIdentifier); 
            queryExpressionBuilder.Append(", only ");
            queryExpressionBuilder.Append(EntityDataSourceUtil.CreateEntitySqlTypeIdentifier(EntityOSpaceType));
            queryExpressionBuilder.Append(")");
 
            return queryExpressionBuilder.ToString();
        } 
 
        #endregion ExecuteSelect
 
        #region ExecuteUpdate

        protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
        { 
            if (!CanUpdate)
            { 
                throw new InvalidOperationException(Strings.EntityDataSourceView_UpdateDisabledForThisControl); 
            }
 
            ConstructContext();
            EntityDataSourceChangingEventArgs changingArgs;

            IDictionary mergedKeysAndOldValues = null; 
            EntityDataSourceWrapperCollection wrapperCollection =
                new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType); 
            EntityDataSourceWrapper modifiedEntityWrapper; 
            try
            { 
                object entity = EntityDataSourceUtil.InitializeType(this.EntityClrType);
                Context.AddObject(_owner.FQEntitySetName, entity);
                modifiedEntityWrapper = new EntityDataSourceWrapper(wrapperCollection, entity);
                mergedKeysAndOldValues = MergeDictionaries(keys, oldValues); 
                mergedKeysAndOldValues = ConvertProperties(mergedKeysAndOldValues, modifiedEntityWrapper.GetProperties(), /* ParameterCollection */null);
                InstantiateEntityFromViewState(modifiedEntityWrapper, mergedKeysAndOldValues); 
            } 
            catch (EntityDataSourceValidationException e)
            { 
                changingArgs = new EntityDataSourceChangingEventArgs(e);
                OnUpdating(changingArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Update));
                if (!changingArgs.ExceptionHandled) 
                {
                    throw; 
                } 
                return -1;
            } 

            //Modify the properties with the new values
            try
            { 
                Context.AcceptAllChanges(); //Puts modifiedEntityWrapper into unchanged state.
                UpdateEntity(mergedKeysAndOldValues, values, modifiedEntityWrapper, _owner.UpdateParameters); 
            } 
            catch (EntityDataSourceValidationException e)
            { 
                changingArgs = new EntityDataSourceChangingEventArgs(e);
                OnUpdating(changingArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Update));
                if (!changingArgs.ExceptionHandled) 
                {
                    throw; 
                } 
                return -1;
            } 

            changingArgs = new EntityDataSourceChangingEventArgs(Context, modifiedEntityWrapper.WrappedEntity);
            OnUpdating(changingArgs);
            if (changingArgs.Cancel) 
            {
                return -1; 
            } 

            try 
            {
                Context.SaveChanges();
            }
            catch (Exception e) 
            {
                // Catches SaveChanges exceptions. 
                EntityDataSourceChangedEventArgs changedArgs = new EntityDataSourceChangedEventArgs(e); 
                OnUpdated(changedArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Update)); 
                if (!changedArgs.ExceptionHandled)
                {
                    throw;
                } 
                return -1;
            } 
            OnUpdated(new EntityDataSourceChangedEventArgs(Context, modifiedEntityWrapper.WrappedEntity)); 
            return 1;
 
        }

        #endregion ExecuteUpdate
 
        #region ExecuteDelete
        protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues) 
        { 
            if (!CanDelete)
            { 
                throw new InvalidOperationException(Strings.EntityDataSourceView_DeleteDisabledForThiscontrol);
            }

            ConstructContext(); 

            EntityDataSourceWrapperCollection wrapperCollection = new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType); 
            EntityDataSourceWrapper entityWrapper; 
            object entity;
            EntityDataSourceChangingEventArgs changingArgs; 
            try
            {
                entity = EntityDataSourceUtil.InitializeType(this.EntityClrType);
                Context.AddObject(_owner.FQEntitySetName, entity); // Add/AcceptAllChanges because wrappers must contain attached entities 
                entityWrapper = new EntityDataSourceWrapper(wrapperCollection, entity);
                IDictionary mergedKeysAndOldValues = MergeDictionaries(keys, oldValues); 
                mergedKeysAndOldValues = ConvertProperties(mergedKeysAndOldValues, entityWrapper.GetProperties(), _owner.DeleteParameters); 
                InstantiateEntityFromViewState(entityWrapper, mergedKeysAndOldValues);
                Context.AcceptAllChanges(); //Force the entity just added into unchanged state. Wrapped entities must be tracked. 
            }
            catch (EntityDataSourceValidationException e)
            {
                changingArgs = new EntityDataSourceChangingEventArgs(e); 
                OnDeleting(changingArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Delete)); 
                if (!changingArgs.ExceptionHandled) 
                {
                    throw; 
                }
                return -1;
            }
            changingArgs = new EntityDataSourceChangingEventArgs(Context, entityWrapper.WrappedEntity); 
            OnDeleting(changingArgs); //Outside "try" to prevent from begin called twice.
            if (changingArgs.Cancel) 
            { 
                return -1;
            } 

            try
            {
                Context.DeleteObject(entityWrapper.WrappedEntity); 
                Context.SaveChanges();
            } 
            catch (Exception e) 
            {
                // Catches errors on the context. 
                EntityDataSourceChangedEventArgs changedArgs = new EntityDataSourceChangedEventArgs(e);
                OnDeleted(changedArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Delete));
                if (!changedArgs.ExceptionHandled) 
                {
                    throw; 
                } 
                return -1;
            } 
            OnDeleted(new EntityDataSourceChangedEventArgs(Context, entity)); //Outside "try" to prevent being called twice.
            return 1;
        }
        #endregion ExecuteDelete 

        #region ExecuteInsert 
 
        protected override int ExecuteInsert(IDictionary values)
        { 
            if (!CanInsert)
            {
                throw new InvalidOperationException(Strings.EntityDataSourceView_InsertDisabledForThisControl);
            } 

            ConstructContext(); 
 
            EntityDataSourceChangingEventArgs changingArgs;
            EntityDataSourceWrapperCollection wrapperCollection = new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType); 
            EntityDataSourceWrapper entityWrapper;
            try
            {
                object entity = EntityDataSourceUtil.InitializeType(this.EntityClrType); 
                Context.AddObject(_owner.FQEntitySetName, entity);
                entityWrapper = new EntityDataSourceWrapper(wrapperCollection, entity); 
                CreateEntityForInsert(entityWrapper, values, _owner.InsertParameters); 
            }
            catch (EntityDataSourceValidationException e) 
            {
                changingArgs = new EntityDataSourceChangingEventArgs(e);
                OnInserting(changingArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Insert)); 
                if (!changingArgs.ExceptionHandled)
                { 
                    throw; 
                }
                return -1; 
            }

            changingArgs = new EntityDataSourceChangingEventArgs(Context, entityWrapper.WrappedEntity);
            OnInserting(changingArgs); //Could return an entity to insert. 
            if (changingArgs.Cancel)
            { 
                return -1; 
            }
 
            if (!Object.ReferenceEquals(entityWrapper.WrappedEntity, changingArgs.Entity))
            {
                Context.Detach(entityWrapper.WrappedEntity);
                Context.AddObject(_owner.EntitySetName, changingArgs.Entity); 
                entityWrapper = new EntityDataSourceWrapper(wrapperCollection, changingArgs.Entity);
            } 
 
            try
            { 
                Context.SaveChanges();
            }
            catch (Exception e)
            { 
                EntityDataSourceChangedEventArgs changedArgs = new EntityDataSourceChangedEventArgs(e);
                OnInserted(changedArgs); 
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Insert)); 
                if (!changedArgs.ExceptionHandled)
                { 
                    throw;
                }
                return -1;
            } 
            OnInserted(new EntityDataSourceChangedEventArgs(Context, entityWrapper.WrappedEntity));
            return 1; 
        } 

        #endregion ExecuteInsert 

        #region Public Methods
        public DataTable GetViewSchema()
        { 
            EntityDataSourceViewSchema propTable;
            if (_owner.ValidateWrappable()) 
            { 
                ConstructContext();
                EntityDataSourceWrapperCollection wrappers = new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType); 
                propTable = new EntityDataSourceViewSchema(wrappers);
            }
            else
            { 
                string where = _owner.Where;
                _owner.Where = "0=1"; 
                try 
                {
                    DataSourceSelectArguments args = new DataSourceSelectArguments(); 
                    args.RetrieveTotalRowCount = false;
                    IEnumerable results = ExecuteSelect(args);
                    ITypedList typedList = results as ITypedList;
                    if (null != typedList) 
                    {
                        propTable = new EntityDataSourceViewSchema(typedList); 
                    } 
                    else
                    { 
                        propTable = new EntityDataSourceViewSchema(results);
                    }
                }
                finally 
                {
                    _owner.Where = where; 
                } 
            }
            return propTable; 
        }
        #endregion Public Methods

        #region Private Methods 

        #region ExecuteSelect Support 
 
        private void GenerateWhereClause(out string whereClause, out ObjectParameter[] whereParameters)
        { 
            if (!_owner.AutoGenerateWhereClause)
            {
                whereClause = _owner.Where;
                whereParameters = _owner.GetWhereParameters(); 
                return;
            } 
 
            //This is the automatically generated Where clause.
            IOrderedDictionary paramValues = _owner.WhereParameters.GetValues(_owner.HttpContext, _owner); 
            // Under some conditions, the paramValues has a null entry.
            StringBuilder whereClauseBuilder = new StringBuilder();
            List whereParameterList = new List();
            bool first = true; 

            int idx = 0; 
            foreach (DictionaryEntry de in paramValues) 
            {
                string propertyName = (string)(de.Key); 
                if (0 < propertyName.Length && null != de.Value)
                {
                    if (!String.IsNullOrEmpty(_owner.EntitySetName) && !EntityDataSourceUtil.PropertyIsOnEntity(propertyName, _wrapperCollection, EntitySet, null))
                    { 
                        throw new InvalidOperationException(Strings.EntityDataSourceView_PropertyDoesNotExistOnEntity(propertyName, EntityClrType.FullName));
                    } 
 
                    if (first)
                    { 
                        first = false;
                    }
                    else
                    { 
                        whereClauseBuilder.Append(" AND ");
                    } 
 
                    string namedParameterName = "NamedParameter" + idx.ToString(CultureInfo.InvariantCulture);
 
                    whereClauseBuilder.Append(EntityDataSourceUtil.GetEntitySqlValueForColumnName(propertyName, _wrapperCollection));
                    whereClauseBuilder.Append("=@");
                    whereClauseBuilder.Append(namedParameterName);
 
                    whereParameterList.Add(new ObjectParameter(namedParameterName, de.Value));
                    idx++; 
                } 
            }
 
            whereParameters = whereParameterList.ToArray();
            whereClause = whereClauseBuilder.ToString();
        }
 
        #endregion ExecuteSelect Support
 
        #region Entity Creation Support for Update/Insert/Delete 

        private object ConvertProperty(object origPropertyValue, PropertyDescriptor propertyDescriptor, WebControlParameterProxy referenceParameter) 
        {
            if (null == propertyDescriptor)
            {
                return null; 
            }
 
            object propValue = origPropertyValue; 
            propValue = Parameter_GetValue(propValue, referenceParameter, true);
            propValue = ConvertType(propValue, propertyDescriptor.PropertyType, propertyDescriptor.Name); 
            return propValue;
        }

        private void ConvertWCProperty(IDictionary values, 
            Dictionary convertedValues,
            List visitedProperties, 
            PropertyDescriptor pd, 
            ParameterCollection referenceParameters,
            ref Dictionary exceptions) 
        {
            string propertyName = pd.Name;
            WebControlParameterProxy wcParameter = new WebControlParameterProxy(propertyName, referenceParameters, _owner);
            object propValue = null; 
            object value = null;
            if (null != values) 
            { 
                value = values[propertyName];
            } 
            try
            {
                propValue = ConvertProperty(value, pd, wcParameter);
            } 
            catch (Exception e)
            { 
                if (null == exceptions) 
                {
                    exceptions = new Dictionary(); 
                }
                exceptions.Add(propertyName, e);
            }
            convertedValues[propertyName] = propValue; 
            visitedProperties.Add(propertyName);
        } 
        private IDictionary ConvertProperties(IDictionary values, PropertyDescriptorCollection propertyDescriptors, ParameterCollection referenceParameters) 
        {
            Dictionary convertedValues = new Dictionary(); 
            List visitedProperties = new List();
            Dictionary exceptions = null;

            // "values" come from the web page. Either via keys or original values. This loops sets them as first priority 
            foreach (string propertyName in values.Keys)
            { 
                PropertyDescriptor pd = propertyDescriptors.Find(propertyName, /*ignoreCase*/ false); 
                ConvertWCProperty(values, convertedValues, visitedProperties,
                    pd, referenceParameters, ref exceptions); 
            }

            // If a property hasn't been set by visible columns or DataKeyNames, then we set them here.
            // "referenceParameters" are Insert, Update or DeleteParameters assigning default values to 
            // columns that are not set by the control
            if (null != referenceParameters) 
            { 
                IOrderedDictionary referenceValues = referenceParameters.GetValues(_owner.HttpContext, _owner);
                foreach (string propertyName in referenceValues.Keys) 
                {
                    if (!visitedProperties.Contains(propertyName))
                    {
                        PropertyDescriptor pd = propertyDescriptors.Find(propertyName, /*ignoreCase*/ false); 
                        ConvertWCProperty(null, convertedValues, visitedProperties,
                            pd, referenceParameters, ref exceptions); 
 
                    }
                } 
            }

            if (null != exceptions)
            { 
                // The IDynamicValidationException encapsulates all of the data conversion errors.
                // This exposes one of the encapsulated errors in its own message like: 
                //   "Error while setting property 'ProductName': 'The value cannot be null.'." 
                string key = exceptions.Keys.First();
                throw new EntityDataSourceValidationException( 
                    Strings.EntityDataSourceView_DataConversionError(
                        key, exceptions[key].Message), exceptions);
            }
 
            return convertedValues;
        } 
 
        private void CreateEntityForInsert(EntityDataSourceWrapper entityWrapper, IDictionary values, ParameterCollection insertParameters)
        { 
            EntityDataSourceUtil.ValidateWebControlParameterNames(entityWrapper, insertParameters, _owner);

            // Throws EntityDataSourceValidationException if data conversion fails
            values = ConvertProperties(values, entityWrapper.GetProperties(), insertParameters); 

            EntityDataSourceUtil.SetAllPropertiesWithVerification(entityWrapper, (Dictionary)values, /*overwrite*/true); 
        } 

        private void UpdateEntity(IDictionary mergedKeysAndOldValues, IDictionary values, 
                                  EntityDataSourceWrapper entityWrapper, ParameterCollection updateParameters)
        {
            EntityDataSourceUtil.ValidateWebControlParameterNames(entityWrapper, updateParameters, _owner);
 
            values = ConvertProperties(values, entityWrapper.GetProperties(), updateParameters);
            Dictionary allModifiedProperties = new Dictionary(); 
 
            // Compare the propertyValues from the original values from the page to those in the new "values" properties.
            // Note that the comparison is between the values that came back from the databound control, BUT 
            // The values on the entity in the entityWrapper came from ViewState. We can't compare the values from the page
            // To those stored in ViewState in case they didn't roundtrip well. Hence this next loop.
            foreach (string propertyName in values.Keys)
            { 
                object originalValue = mergedKeysAndOldValues[propertyName];
                object newValue = values[propertyName]; 
 
                if (!OriginalValueMatches(originalValue, newValue))
                { 
                    allModifiedProperties[propertyName] = newValue;
                }
            }
 
            EntityDataSourceUtil.SetAllPropertiesWithVerification(entityWrapper, allModifiedProperties, /*overwrite*/false);
        } 
 
        private IDictionary MergeDictionaries(IDictionary keys, IDictionary values)
        { 
            Dictionary mergedValues = new Dictionary();
            // Add all the keys
            foreach (string propName in keys.Keys)
            { 
                mergedValues.Add(propName, keys[propName]);
            } 
            // Add the properties from values 
            foreach (string propName in values.Keys)
            { 
                if (!mergedValues.ContainsKey(propName))
                {
                    mergedValues.Add(propName, values[propName]);
                } 
            }
            return mergedValues; 
        } 

        private static object ConvertType(object value, Type type, string paramName) 
        {
            // NOTE: This method came from ObjectDataSource via LinqDataSource.
            string s = value as string;
            if (s != null) 
            {
                // Get the type converter for the destination type 
                TypeConverter converter = TypeDescriptor.GetConverter(type); 
                if (converter != null)
                { 
                    // Perform the conversion
                    try
                    {
                        value = converter.ConvertFromString(s); 
                    }
                    catch (NotSupportedException) 
                    { 
                        throw new InvalidOperationException(Strings.EntityDataSourceView_UnableToConvertStringToType(s, type.FullName));
                    } 
                    catch (FormatException)
                    {
                        throw new InvalidOperationException(Strings.EntityDataSourceView_UnableToConvertStringToType(s, type.FullName));
                    } 
                }
            } 
            return value; 
        }
 
        #endregion Entity Creation Support

        #region ViewState Storage
 
        internal void StoreOriginalPropertiesIntoViewState()
        { 
            if (null == _wrapperCollection || 
                0 == _wrapperCollection.Count ||
                !_owner.StoreOriginalValuesInViewState || 
                (!CanDelete && !CanUpdate)) //Only store entities into viewstate if Delete or Update is enabled.
            {
                return;
            } 

            _originalProperties = new Dictionary(); 
 
            PropertyDescriptorCollection collection = _wrapperCollection.GetItemProperties(null);
 
            // Retrieve the named properties from each entity and place them into the viewstate collection.
            foreach (EntityDataSourceWrapper wrapper in _wrapperCollection)
            {
                foreach (PropertyDescriptor propertyDescriptor in collection) 
                {
                    EntityDataSourceWrapperPropertyDescriptor wrapperPropertyDescriptor = (EntityDataSourceWrapperPropertyDescriptor)propertyDescriptor; 
 
                    if (wrapperPropertyDescriptor.Column.IsInteresting && wrapperPropertyDescriptor.Column.IsPrimitive)
                    { 
                        object property = wrapperPropertyDescriptor.GetValue(wrapper);
                        string propertyName = wrapperPropertyDescriptor.DisplayName;

                        if (!_originalProperties.ContainsKey(propertyName)) 
                        {
                            _originalProperties[propertyName] = new ArrayList(); 
                        } 

                        (_originalProperties[propertyName]).Add(property); 
                    }
                }
            }
        } 

        private void InstantiateEntityFromViewState(EntityDataSourceWrapper entityWrapper, IDictionary mergedKeysAndOldValues) 
        { 
            int idx = FindIdxOfPropertiesStoredInViewState(mergedKeysAndOldValues);
            Dictionary changedProperties = new Dictionary(); 

            if (0 <= idx) // "-1" indicates that the value was not found in ViewState
            {
                foreach (string propertyName in _originalProperties.Keys) 
                {
                    object property = (_originalProperties[propertyName])[idx]; 
                    changedProperties.Add(propertyName, property); 
                }
            } 

            // Now, add all the values from the page to the constructed entity.
            // This fills in the values from the page, into all properties not stored in ViewState.
            Dictionary mergedDict = mergedKeysAndOldValues as Dictionary; 
            if (null != mergedDict)
            { 
                foreach (string key in mergedDict.Keys) 
                {
                    if (!changedProperties.ContainsKey(key)) 
                    {
                        changedProperties.Add(key, mergedDict[key]);
                    }
                } 
            }
 
            EntityDataSourceUtil.SetAllPropertiesWithVerification(entityWrapper, changedProperties, /*overwrite*/true); 
        }
 
        /// 
        /// Returns the index of the entity in ViewState that has key values that match the values from the page.
        /// If a match is not found, it returns -1.
        ///  
        /// 
        ///  
        private int FindIdxOfPropertiesStoredInViewState(IDictionary mergedKeysAndOldValues) 
        {
            if (null == _originalProperties || 0 == _originalProperties.Count) 
            {
                return -1;
            }
 
            Dictionary localMergedKeysAndOldValues = (Dictionary)mergedKeysAndOldValues;
 
            // This get the number of entities from the first property's values. 
            // The ArrayList that holds the values contains the count of the number of entities.
            int numEntities = (_originalProperties.First().Value).Count; 
            for (int idx = 0; idx < numEntities; idx++)
            {
                bool match = true;
                foreach (EdmMember edmMember in KeyMembers) 
                {
                    string propertyName = edmMember.Name; 
                    // The page must return the key values. 
                    if (!localMergedKeysAndOldValues.ContainsKey(propertyName))
                    { 
                        match = false;
                        break;
                    }
 
                    object origValue = (_originalProperties[propertyName])[idx];
                    object pageValue = localMergedKeysAndOldValues[propertyName]; 
                    if (!OriginalValueMatches(origValue, pageValue)) 
                    {
                        match = false; 
                        break;
                    }
                }
                if (match) 
                {
                    return idx; 
                } 
            }
            return -1; 
        }

        private bool OriginalValueMatches(object originalValue, object value)
        { 
            if (null == originalValue)
            { 
                if (null == value) 
                {
                    return true; 
                }
                return false;
            }
 
            // NOTE: Comparing IEnumerable contents instead of instances to ensure that
            // timestamp columns (of type byte[]) can be matched appropriately. 
            IEnumerable originalValueEnumerable = originalValue as IEnumerable; 
            IEnumerable valueEnumerable = value as IEnumerable;
            if ((originalValueEnumerable != null) && (valueEnumerable != null)) 
            {
                return EnumerableContentEquals(originalValueEnumerable, valueEnumerable);
            }
 
            return originalValue.Equals(value);
        } 
 
        #endregion ViewState Storage
 
        #region Context and Query construction

        private void ConstructContext()
        { 
            this.DisposeContext();
 
            Type contextType = null; 
            EntityDataSourceContextCreatingEventArgs creatingArgs = new EntityDataSourceContextCreatingEventArgs();
            OnContextCreating(creatingArgs); 
            if (null != creatingArgs.Context) //Context was created in event code
            {
                _ctx = creatingArgs.Context;
                contextType = _ctx.GetType(); 
            }
            else if (null != _owner.ContextType || !String.IsNullOrEmpty(_owner.ContextTypeName)) 
            { 
                //Construct the context.
                if (null != _owner.ContextType) 
                {
                    contextType = _owner.ContextType;
                }
                else 
                {
                    contextType = System.Web.Compilation.BuildManager.GetType(_owner.ContextTypeName, /*throw on error*/ true); 
                } 

                ConstructorInfo ctxInfo = contextType.GetConstructor( 
                    BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance,
                    null, System.Type.EmptyTypes, null);

                if (null == ctxInfo) 
                {
                    throw new InvalidOperationException(Strings.EntityDataSourceView_NoParameterlessConstructorForTheContext); 
                } 

                _ctx = (ObjectContext)ctxInfo.Invoke(new object[0]); 
            }
            else // Non-strongly-typed context built from ConnectionString and DefaultContainerName
            {
                if (!String.IsNullOrEmpty(_owner.DefaultContainerName) && 
                    !String.IsNullOrEmpty(_owner.ConnectionString))
                { 
                    _ctx = new ObjectContext(_owner.ConnectionString); 

                    if (System.Web.Hosting.HostingEnvironment.IsHosted) 
                    {
                        // Since we don't have the type from the strongly-typed context,
                        // load from all of the referenced assemblies, including code from App_Code and the top-level directory:
                        // http://msdn2.microsoft.com/en-us/library/system.web.compilation.buildmanager.getreferencedassemblies.aspx 
                        ICollection codeAssemblies = System.Web.Compilation.BuildManager.GetReferencedAssemblies();
                        foreach (Assembly assembly in codeAssemblies) 
                        { 
                            _ctx.MetadataWorkspace.LoadFromAssembly(assembly);
                        } 
                    }
                }
                else if (!String.IsNullOrEmpty(_owner.DefaultContainerName) &&
                          null != _owner.Connection) 
                {
                    _ctx = new ObjectContext(_owner.Connection); 
                } 
                else
                { 
                    throw new InvalidOperationException(Strings.EntityDataSourceView_ObjectContextMustBeSpecified);
                }
                // Must set the DefaultContainerName for both of the above conditions.
                _ctx.DefaultContainerName = _owner.DefaultContainerName; 
                contextType = typeof(ObjectContext);
 
            } 

            _ctx.MetadataWorkspace.LoadFromAssembly(System.Reflection.Assembly.GetCallingAssembly()); 
            _ctx.MetadataWorkspace.LoadFromAssembly(contextType.Assembly);


            // Error Checking on the Context 
            ValidateContainerName();
 
            OnContextCreated(new EntityDataSourceContextCreatedEventArgs(Context)); 
        }
 

        internal void DisposeContext()
        {
            if (null != _ctx) 
            {
                EntityDataSourceContextDisposingEventArgs disposeArgs = new EntityDataSourceContextDisposingEventArgs(_ctx); 
                OnContextDisposing(disposeArgs); 
                if (!disposeArgs.Cancel)
                { 
                    _ctx.Dispose();
                    _ctx = null;
                }
            } 
        }
 
        #endregion Context and Query construction 
        #endregion Private Methods
 
        #region Public Overrides
        public override bool CanInsert
        {
            get { return _owner.EnableInsert && !_disableUpdates; } 
        }
        public override bool CanUpdate 
        { 
            get { return _owner.EnableUpdate && !_disableUpdates; }
        } 
        public override bool CanDelete
        {
            get { return _owner.EnableDelete && !_disableUpdates; }
        } 
        public override bool CanSort
        { 
            get { return _owner.AutoSort; } 
        }
        public override bool CanPage 
        {
            get { return _owner.AutoPage; }
        }
        public override bool CanRetrieveTotalRowCount 
        {
            get { return _owner.AutoPage; } 
        } 
        #endregion Public Overrides
 
        #region Events

        //Oryx exception event
        private void OnException(DynamicValidatorEventArgs args) 
        {
            EventHandler handler = 
                (EventHandler)Events[EventException]; 
            if (null != handler)
            { 
                handler(this, args);
            }
        }
        public event EventHandler Exception 
        {
            add { Events.AddHandler(EventException, value); } 
            remove { Events.RemoveHandler(EventException, value); } 
        }
 
        // ContextCreating Event
        private void OnContextCreating(EntityDataSourceContextCreatingEventArgs e)
        {
            EventHandler handler = 
                (EventHandler)Events[EventContextCreating];
 
            if (null != handler) 
            {
                handler(this, e); 
            }
        }
        public event EventHandler ContextCreating
        { 
            add { Events.AddHandler(EventContextCreating, value); }
            remove { Events.RemoveHandler(EventContextCreating, value); } 
        } 

        // ContextCreated Event 
        private void OnContextCreated(EntityDataSourceContextCreatedEventArgs e)
        {
            EventHandler handler =
                (EventHandler)Events[EventContextCreated]; 

            if (null != handler) 
            { 
                handler(this, e);
            } 
        }
        public event EventHandler ContextCreated
        {
            add { Events.AddHandler(EventContextCreated, value); } 
            remove { Events.RemoveHandler(EventContextCreated, value); }
        } 
 
        // ContextDisposing Event
        private void OnContextDisposing(EntityDataSourceContextDisposingEventArgs e) 
        {
            EventHandler handler =
                (EventHandler)Events[EventContextDisposing];
 
            if (null != handler)
            { 
                handler(this, e); 
            }
        } 
        public event EventHandler ContextDisposing
        {
            add { Events.AddHandler(EventContextDisposing, value); }
            remove { Events.RemoveHandler(EventContextDisposing, value); } 
        }
 
        // Selecting Event 
        private void OnSelecting(EntityDataSourceSelectingEventArgs e)
        { 
            EventHandler handler =
                (EventHandler)Events[EventSelecting];
            if (null != handler)
            { 
                handler(this, e);
            } 
        } 
        public event EventHandler Selecting
        { 
            add { Events.AddHandler(EventSelecting, value); }
            remove { Events.RemoveHandler(EventSelecting, value); }
        }
 
        // Selected Event
        private void OnSelected(EntityDataSourceSelectedEventArgs e) 
        { 
            EventHandler handler =
                (EventHandler)Events[EventSelected]; 
            if (null != handler)
            {
                handler(this, e);
            } 
        }
        public event EventHandler Selected 
        { 
            add { Events.AddHandler(EventSelected, value); }
            remove { Events.RemoveHandler(EventSelected, value); } 
        }

        // Deleting Event
        private void OnDeleting(EntityDataSourceChangingEventArgs e) 
        {
            EventHandler handler = 
                (EventHandler)Events[EventDeleting]; 
            if (null != handler)
            { 
                handler(this, e);
            }
        }
        public event EventHandler Deleting 
        {
            add { Events.AddHandler(EventDeleting, value); } 
            remove { Events.RemoveHandler(EventDeleting, value); } 
        }
 
        // Deleted Event
        private void OnDeleted(EntityDataSourceChangedEventArgs e)
        {
            EventHandler handler = 
                (EventHandler)Events[EventDeleted];
            if (null != handler) 
            { 
                handler(this, e);
            } 
        }
        public event EventHandler Deleted
        {
            add { Events.AddHandler(EventDeleted, value); } 
            remove { Events.RemoveHandler(EventDeleted, value); }
        } 
 
        //Inserting Event
        private void OnInserting(EntityDataSourceChangingEventArgs e) 
        {
            EventHandler handler =
                (EventHandler)Events[EventInserting];
            if (handler != null) 
            {
                handler(this, e); 
            } 
        }
        public event EventHandler Inserting 
        {
            add { Events.AddHandler(EventInserting, value); }
            remove { Events.RemoveHandler(EventInserting, value); }
        } 

        //Inserted Event 
        private void OnInserted(EntityDataSourceChangedEventArgs e) 
        {
            EventHandler handler = 
                (EventHandler)Events[EventInserted];
            if (handler != null)
            {
                handler(this, e); 
            }
        } 
        public event EventHandler Inserted 
        {
            add { Events.AddHandler(EventInserted, value); } 
            remove { Events.RemoveHandler(EventInserted, value); }
        }

        //Updating Event 
        private void OnUpdating(EntityDataSourceChangingEventArgs e)
        { 
            EventHandler handler = 
                (EventHandler)Events[EventUpdating];
            if (handler != null) 
            {
                handler(this, e);
            }
        } 
        public event EventHandler Updating
        { 
            add { Events.AddHandler(EventUpdating, value); } 
            remove { Events.RemoveHandler(EventUpdating, value); }
        } 

        //Updated Event
        private void OnUpdated(EntityDataSourceChangedEventArgs e)
        { 
            EventHandler handler =
                (EventHandler)Events[EventUpdated]; 
            if (handler != null) 
            {
                handler(this, e); 
            }
        }
        public event EventHandler Updated
        { 
            add { Events.AddHandler(EventUpdated, value); }
            remove { Events.RemoveHandler(EventUpdated, value); } 
        } 

 
        internal void RaiseChangedEvent()
        {
            OnDataSourceViewChanged(EventArgs.Empty);
        } 

        #endregion Events 
 
        #region Private Properties
 
        private StructuralType EntityOSpaceType
        {
            get
            { 
                // If the CSpaceType is not determinable (e.g. DataSource.EntitySetName is not specified),
                // then return null. 
                if (null == EntityCSpaceType) 
                {
                    return null; 
                }
                StructuralType oSpaceType = Context.MetadataWorkspace.GetObjectSpaceType(EntityCSpaceType);
                return oSpaceType;
            } 
        }
 
        // Returns the type for EntityTypeFilter, or 
        // null if both EntitySet and EntityTypeFilter are not specified.
        private EntityType CSpaceFilteredEntityType 
        {
            get
            {
                EntityType cSpaceType = null; 
                if (null == EntitySet)
                { 
                    return null; //read-only scenario in which the EntitySet is not specified on the DataSource 
                }
 
                // Return the type specified in EntityTypeFilter
                if (!String.IsNullOrEmpty(_owner.EntityTypeFilter))
                {
                    cSpaceType = (EntityType)Context.MetadataWorkspace.GetType(_owner.EntityTypeFilter, EntitySet.ElementType.NamespaceName, DataSpace.CSpace); 
                    if (!EntityDataSourceUtil.IsTypeOrSubtypeOf(EntitySet.ElementType, cSpaceType, Context.MetadataWorkspace.GetItemCollection(DataSpace.CSpace)))
                    { 
                        throw new InvalidOperationException(Strings.EntityDataSourceView_FilteredEntityTypeMustBeDerivableFromEntitySet(_owner.EntityTypeFilter, _owner.EntitySetName)); 
                    }
                    return cSpaceType; 
                }

                return null;
 
            }
        } 
 
        // Returns the actualy EntityCSpaceType to be returned from the query, either via
        // EntityTypeFilter or via the base type for the EntitySet. 
        private EntityType EntityCSpaceType
        {
            get
            { 
                EntityType cSpaceType = CSpaceFilteredEntityType;
                if (null != cSpaceType) 
                { 
                    return cSpaceType;
                } 

                if (null != EntitySet)
                {
                    //If EntityDataSource.EntityTypeFilter is not specified, return the base type for the EntitySet. 
                    cSpaceType = EntitySet.ElementType;
                } 
                return cSpaceType; 
            }
 
        }

        private EntityContainer EntityContainer
        { 
            get
            { 
                return Context.MetadataWorkspace.GetEntityContainer(ContainerName, DataSpace.CSpace); 
            }
        } 

        /// 
        /// The EntitySet Associated with this DataSource. If EntityDataSource.EntitySetName is not set, then
        /// This property returns null. 
        /// 
        private EntitySet EntitySet 
        { 
            get
            { 
                if (String.IsNullOrEmpty(_owner.EntitySetName))
                {
                    return null;
                } 
                return EntityContainer.GetEntitySetByName(_owner.EntitySetName, /*ignoreCase*/ false);
            } 
        } 

        private Type EntityClrType 
        {
            get
            {
                ObjectItemCollection objectItemCollection = 
                    (ObjectItemCollection)(Context.MetadataWorkspace.GetItemCollection(DataSpace.OSpace));
                Type clrType = objectItemCollection.GetClrType(EntityOSpaceType); 
                return clrType; 
            }
        } 

        private ObjectContext Context
        {
            get 
            {
                Debug.Assert(null != _ctx, "The context hasn't yet been constructed"); 
                return _ctx; 
            }
        } 

        private ReadOnlyMetadataCollection KeyMembers
        {
            get 
            {
                if (null == _keyMembers) 
                { 
                    EntityContainer entityContainer = Context.MetadataWorkspace.GetEntityContainer(ContainerName, DataSpace.CSpace);
                    EntitySet entitySet = entityContainer.GetEntitySetByName(_owner.EntitySetName, false); 

                    _keyMembers = ((EntityType)(entitySet.ElementType)).KeyMembers;
                }
                return _keyMembers; 
            }
        } 
 
        private string ContainerName
        { 
            get
            {
                if (!string.IsNullOrEmpty(_owner.DefaultContainerName))
                { 
                    return _owner.DefaultContainerName;
                } 
                if (!string.IsNullOrEmpty(Context.DefaultContainerName)) 
                {
                    return Context.DefaultContainerName; 
                }
                throw new InvalidOperationException(Strings.EntityDataSourceView_ContainerNameMustBeSpecified);
            }
        } 

        #endregion Private Properties 
 
        #region private getters
 
        #endregion private getters

        #region IStateManager implementation
        bool IStateManager.IsTrackingViewState 
        {
            get { return _tracking; } 
        } 
        void IStateManager.LoadViewState(object savedState)
        { 
            if (null != savedState)
            {
                Hashtable myState = (Hashtable)savedState;
                _tracking = (bool)myState["_tracking"]; 
                _disableUpdates = (bool)myState["_disableUpdates"];
                _originalProperties = (Dictionary)myState["_originalProperties"]; 
            } 
        }
 
        object IStateManager.SaveViewState()
        {
            StoreOriginalPropertiesIntoViewState();
 
            Hashtable myState = new Hashtable();
            myState.Add("_tracking", _tracking); 
            myState.Add("_disableUpdates", _disableUpdates); 
            myState.Add("_originalProperties", _originalProperties);
            return myState; 
        }
        void IStateManager.TrackViewState()
        {
            _tracking = true; 
        }
        #endregion 
 
        #region Utilities and Error Checking
 
        private void AddSupportedCapabilities(DataSourceSelectArguments arguments)
        {
            if (CanSort)
            { 
                arguments.AddSupportedCapabilities(DataSourceCapabilities.Sort);
            } 
            if (CanPage) 
            {
                arguments.AddSupportedCapabilities(DataSourceCapabilities.Page); 
                arguments.AddSupportedCapabilities(DataSourceCapabilities.RetrieveTotalRowCount);
            }
        }
 
        internal void ValidateEntitySetName()
        { 
            EntityContainer entityContainer = Context.MetadataWorkspace.GetEntityContainer(ContainerName, DataSpace.CSpace); 
            EntitySet entitySet;
            if (!entityContainer.TryGetEntitySetByName(_owner.EntitySetName, /*ignoreCase*/false, out entitySet)) 
            {
                throw new InvalidOperationException(Strings.EntityDataSourceView_EntitySetDoesNotExistOnTheContainer(_owner.EntitySetName));
            }
        } 

        private void ValidateContainerName() 
        { 
            EntityContainer container;
            if (!Context.MetadataWorkspace.TryGetEntityContainer(ContainerName, DataSpace.CSpace, out container)) 
            {
                throw new InvalidOperationException(Strings.EntityDataSourceView_ContainerNameDoesNotExistOnTheContext(ContainerName));
            }
        } 

        // From LinqDataSourceHelper. Timestamp columns (of type byte[]) are of type IEnumerable. 
        // This routine compares all elements of the IEnumerable. 
        private static bool EnumerableContentEquals(IEnumerable enumerableA, IEnumerable enumerableB)
        { 
            IEnumerator enumeratorA = enumerableA.GetEnumerator();
            IEnumerator enumeratorB = enumerableB.GetEnumerator();
            while (enumeratorA.MoveNext())
            { 
                if (!enumeratorB.MoveNext())
                    return false; 
                object itemA = enumeratorA.Current; 
                object itemB = enumeratorB.Current;
                if (itemA == null) 
                {
                    if (itemB != null)
                        return false;
                } 
                else if (!itemA.Equals(itemB))
                    return false; 
            } 
            if (enumeratorB.MoveNext())
                return false; 
            return true;
        }

        // This routine modified from System.Web.Ui.WebControls. 
        private static object Parameter_GetValue(object value, WebControlParameterProxy parameter, bool ignoreNullableTypeChanges)
        { 
            // Convert.ChangeType() throws if you attempt to convert to DBNull, so we have to special case it. 
            if (parameter.TypeCode == TypeCode.DBNull)
            { 
                return DBNull.Value;
            }

            // Get the value and convert it to the default value if it is null 
            if (parameter.ConvertEmptyStringToNull)
            { 
                string stringValue = value as string; 
                if ((stringValue != null) && (stringValue.Length == 0))
                { 
                    value = null;
                }

            } 

            if (value == null) // Fill it with values from referenceParameters 
            { 
                // Use the parameter value if it is non-null
                if (!parameter.HasValue) 
                {
                    return value;
                }
 
                object parameterValue = parameter.Value;
                string valueString = parameterValue as String; 
                if (parameter.TypeCode == TypeCode.String && parameter.ConvertEmptyStringToNull && String.IsNullOrEmpty(valueString)) 
                {
                    parameterValue = null; 
                }

                if (null == parameterValue)
                { 
                    return null;
                } 
                value = parameterValue; 
            }
 
            Debug.Assert(value != null, "Value should not be null at this point.");

            if (parameter.TypeCode == TypeCode.Object || parameter.TypeCode == TypeCode.Empty)
            { 
                return value;
            } 
 
            // For ObjectDataSource we special-case Nullable and do nothing because these
            // types will get converted when we actually call the method. 
            if (ignoreNullableTypeChanges)
            {
                Type valueType = value.GetType();
                if (valueType.IsGenericType && (valueType.GetGenericTypeDefinition() == typeof(Nullable<>))) 
                {
                    return value; 
                } 
            }
            return value = Convert.ChangeType(value, parameter.TypeCode, CultureInfo.CurrentCulture); ; 
        }


        #endregion Utilities 

        //public override void Delete(IDictionary keys, IDictionary oldValues, DataSourceViewOperationCallback callback){} 
        //public override void Insert(IDictionary values, DataSourceViewOperationCallback callback){} 
        //public override void Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback){}
        //public override void Update(IDictionary keys, IDictionary values, IDictionary oldValues, DataSourceViewOperationCallback callback){} 

    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System;
using System.Collections; 
using System.Data;
using System.Data.Common;
using System.Data.Objects;
using System.Data.Metadata.Edm; 
using System.Configuration;
using System.Diagnostics; 
using System.Globalization; 
using System.Linq;
using System.Reflection; 
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls; 
using System.Web.DynamicData;
using System.Data.Objects.DataClasses; 
using System.Collections.Specialized; 
using System.ComponentModel;
using System.Collections.Generic; 
using System.Text;
using System.Collections.ObjectModel;

 
namespace System.Web.UI.WebControls
{ 
 
    public sealed class EntityDataSourceView : DataSourceView, IStateManager
    { 
        private EntityDataSource _owner;
        private ObjectContext _ctx = null;
        private static readonly ObjectParameter[] _noParameters = new ObjectParameter[0] { };
        readonly static Dictionary emptyDictionary = new Dictionary(); 
        private ReadOnlyMetadataCollection _keyMembers = null;
        private EntityDataSourceWrapperCollection _wrapperCollection = null; 
 
        //
        private static readonly object EventContextCreated = new object(); 
        private static readonly object EventContextCreating = new object();
        private static readonly object EventContextDisposing = new object();
        private static readonly object EventDeleted = new object();
        private static readonly object EventDeleting = new object(); 
        private static readonly object EventInserted = new object();
        private static readonly object EventInserting = new object(); 
        private static readonly object EventSelected = new object(); 
        private static readonly object EventSelecting = new object();
        private static readonly object EventUpdated = new object(); 
        private static readonly object EventUpdating = new object();
        private static readonly object EventException = new object();

        // values saved in ViewState 
        private bool _disableUpdates = false;
        private bool _tracking = false; 
 
        private Dictionary _originalProperties;
 
        #region Constructor
        internal EntityDataSourceView(EntityDataSource owner, string viewName)
            : base(owner, viewName)
        { 
            _owner = owner;
        } 
        #endregion Constructor 

        #region ExecuteSelect 
        protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
        {
            if (!string.IsNullOrEmpty(_owner.Select))
            { 
                return ExecuteSelect(arguments, EntityDataSourceRecordQueryBuilder.Create);
            } 
            else 
            {
                return ExecuteSelect(arguments, EntityDataSourceObjectQueryBuilder.Create); 
            }
        }

        private IEnumerable ExecuteSelect(DataSourceSelectArguments arguments, EntityDataSourceQueryBuilder.Creator qbConstructor) 
        {
            _wrapperCollection = null; 
 
            AddSupportedCapabilities(arguments);
            arguments.RaiseUnsupportedCapabilitiesError(this); 

            ConstructContext();

            EntityDataSourceSelectingEventArgs selectArgs = 
                new EntityDataSourceSelectingEventArgs(_owner, arguments);
 
            OnSelecting(selectArgs); 
            if (selectArgs.Cancel)
            { 
                return null;
            }

            _disableUpdates = _owner.ValidateUpdatableConditions(); 

            if (_owner.ValidateWrappable()) 
            { 
                _wrapperCollection = new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType);
            } 

            string whereClause;
            ObjectParameter[] whereParameters;
            GenerateWhereClause(out whereClause, out whereParameters); 
            string entitySetQueryExpression = GenerateEntitySetQueryExpression();
 
 
            OrderByBuilder orderByBuilder = new OrderByBuilder(arguments.SortExpression, _wrapperCollection,
                _owner.OrderBy, _owner.AutoGenerateOrderByClause, _owner.OrderByParameters, 
                CanPage, //There's no need to generate the default OrderBy clause if paging is disabled. Prevents an unnecessary sort at the server.
                _owner);

            EntityDataSourceQueryBuilder queryBuilder = 
                qbConstructor(
                        arguments, 
                        _owner.CommandText, _owner.GetCommandParameters(), 
                        whereClause, whereParameters, entitySetQueryExpression,
                        _owner.Select, _owner.GroupBy, _owner.GetSelectParameters(), 
                        orderByBuilder,
                        _owner.Include);

 
            // SelectedEventArgs.TotalRowCount has three possible states:
            //  1. The databound control requests it via arguments.RetrieveTotalRowCount 
            //     This returns the total number of rows on all pages. 
            //     arguments.TotalRowCount is only set if arguments.RetrieveTotalRowCount is true.
            //  2. Paging is disabled via !CanPage. 
            //     This returns the number of rows returned. On one page.
            //  3. Else it returns negative one.
            int totalRowCount = -1;
 
            ObjectQuery queryT = queryBuilder.Build(Context, arguments.RetrieveTotalRowCount);
 
            if (arguments.RetrieveTotalRowCount) 
            {
                // The Selected event args gets the total number of rows on all pages. 
                totalRowCount = queryBuilder.TotalCount;
                // The databound control requests totalRowCount. We return the total rows on all pages.
                arguments.TotalRowCount = totalRowCount;
            } 

            if (!_disableUpdates) 
            { 
                Debug.Assert(null != EntitySet, "Can't be updatable with a null EntitySet");
                EntityDataSourceUtil. 
                  CheckNonPolymorphicTypeUsage(EntitySet.ElementType,
                                             Context.MetadataWorkspace.GetItemCollection(DataSpace.CSpace),
                                             _owner.EntityTypeFilter);
            } 

 
            IEnumerable entities = null; 
            try
            { 
                entities = queryBuilder.Execute(queryT);
            }
            catch (Exception e)
            { 
                entities = null;
                EntityDataSourceSelectedEventArgs selectedArgs = new EntityDataSourceSelectedEventArgs(e); 
                OnSelected(selectedArgs); 
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Select));
                if (!selectedArgs.ExceptionHandled) 
                {
                    throw;
                }
            } 

            // OnSelected outside "try" to prevent double-calling OnSelected if the first OnSelected throws 
            if (null != entities) 
            {
                if (!CanPage) 
                {
                    // Paging is disabled, totalRowCount gets the number of rows returned from this query.
                    totalRowCount = ((IList)entities).Count;
                } 
                OnSelected(new EntityDataSourceSelectedEventArgs(Context, entities, totalRowCount, arguments));
            } 
 
            if (null == _wrapperCollection)
            { 
                return entities;
            }

            foreach (object element in entities) 
            {
                _wrapperCollection.AddWrappedEntity(element); 
            } 

            return _wrapperCollection; 
        }

        /// 
        /// Restrict the query to the type specified by EntityTypeFilter. 
        /// 
        ///  
        private string GenerateEntitySetQueryExpression() 
        {
 
            if (null == EntitySet)
            {
                return String.Empty;
            } 

            string entitySetIdentifier = EntityDataSourceUtil.CreateEntitySqlSetIdentifier(EntitySet); 
 
            if (String.IsNullOrEmpty(_owner.EntityTypeFilter))
            { 
                return entitySetIdentifier;
            }

            Debug.Assert(EntityOSpaceType != null, "EntitySet is not null, EntityOSpaceType should also be defined."); 

            // oftype ([Northwind].[Products], only [Northwind].[ActiveProducts]) 
            StringBuilder queryExpressionBuilder = new StringBuilder(); 
            queryExpressionBuilder.Append("oftype (");
            queryExpressionBuilder.Append(entitySetIdentifier); 
            queryExpressionBuilder.Append(", only ");
            queryExpressionBuilder.Append(EntityDataSourceUtil.CreateEntitySqlTypeIdentifier(EntityOSpaceType));
            queryExpressionBuilder.Append(")");
 
            return queryExpressionBuilder.ToString();
        } 
 
        #endregion ExecuteSelect
 
        #region ExecuteUpdate

        protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
        { 
            if (!CanUpdate)
            { 
                throw new InvalidOperationException(Strings.EntityDataSourceView_UpdateDisabledForThisControl); 
            }
 
            ConstructContext();
            EntityDataSourceChangingEventArgs changingArgs;

            IDictionary mergedKeysAndOldValues = null; 
            EntityDataSourceWrapperCollection wrapperCollection =
                new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType); 
            EntityDataSourceWrapper modifiedEntityWrapper; 
            try
            { 
                object entity = EntityDataSourceUtil.InitializeType(this.EntityClrType);
                Context.AddObject(_owner.FQEntitySetName, entity);
                modifiedEntityWrapper = new EntityDataSourceWrapper(wrapperCollection, entity);
                mergedKeysAndOldValues = MergeDictionaries(keys, oldValues); 
                mergedKeysAndOldValues = ConvertProperties(mergedKeysAndOldValues, modifiedEntityWrapper.GetProperties(), /* ParameterCollection */null);
                InstantiateEntityFromViewState(modifiedEntityWrapper, mergedKeysAndOldValues); 
            } 
            catch (EntityDataSourceValidationException e)
            { 
                changingArgs = new EntityDataSourceChangingEventArgs(e);
                OnUpdating(changingArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Update));
                if (!changingArgs.ExceptionHandled) 
                {
                    throw; 
                } 
                return -1;
            } 

            //Modify the properties with the new values
            try
            { 
                Context.AcceptAllChanges(); //Puts modifiedEntityWrapper into unchanged state.
                UpdateEntity(mergedKeysAndOldValues, values, modifiedEntityWrapper, _owner.UpdateParameters); 
            } 
            catch (EntityDataSourceValidationException e)
            { 
                changingArgs = new EntityDataSourceChangingEventArgs(e);
                OnUpdating(changingArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Update));
                if (!changingArgs.ExceptionHandled) 
                {
                    throw; 
                } 
                return -1;
            } 

            changingArgs = new EntityDataSourceChangingEventArgs(Context, modifiedEntityWrapper.WrappedEntity);
            OnUpdating(changingArgs);
            if (changingArgs.Cancel) 
            {
                return -1; 
            } 

            try 
            {
                Context.SaveChanges();
            }
            catch (Exception e) 
            {
                // Catches SaveChanges exceptions. 
                EntityDataSourceChangedEventArgs changedArgs = new EntityDataSourceChangedEventArgs(e); 
                OnUpdated(changedArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Update)); 
                if (!changedArgs.ExceptionHandled)
                {
                    throw;
                } 
                return -1;
            } 
            OnUpdated(new EntityDataSourceChangedEventArgs(Context, modifiedEntityWrapper.WrappedEntity)); 
            return 1;
 
        }

        #endregion ExecuteUpdate
 
        #region ExecuteDelete
        protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues) 
        { 
            if (!CanDelete)
            { 
                throw new InvalidOperationException(Strings.EntityDataSourceView_DeleteDisabledForThiscontrol);
            }

            ConstructContext(); 

            EntityDataSourceWrapperCollection wrapperCollection = new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType); 
            EntityDataSourceWrapper entityWrapper; 
            object entity;
            EntityDataSourceChangingEventArgs changingArgs; 
            try
            {
                entity = EntityDataSourceUtil.InitializeType(this.EntityClrType);
                Context.AddObject(_owner.FQEntitySetName, entity); // Add/AcceptAllChanges because wrappers must contain attached entities 
                entityWrapper = new EntityDataSourceWrapper(wrapperCollection, entity);
                IDictionary mergedKeysAndOldValues = MergeDictionaries(keys, oldValues); 
                mergedKeysAndOldValues = ConvertProperties(mergedKeysAndOldValues, entityWrapper.GetProperties(), _owner.DeleteParameters); 
                InstantiateEntityFromViewState(entityWrapper, mergedKeysAndOldValues);
                Context.AcceptAllChanges(); //Force the entity just added into unchanged state. Wrapped entities must be tracked. 
            }
            catch (EntityDataSourceValidationException e)
            {
                changingArgs = new EntityDataSourceChangingEventArgs(e); 
                OnDeleting(changingArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Delete)); 
                if (!changingArgs.ExceptionHandled) 
                {
                    throw; 
                }
                return -1;
            }
            changingArgs = new EntityDataSourceChangingEventArgs(Context, entityWrapper.WrappedEntity); 
            OnDeleting(changingArgs); //Outside "try" to prevent from begin called twice.
            if (changingArgs.Cancel) 
            { 
                return -1;
            } 

            try
            {
                Context.DeleteObject(entityWrapper.WrappedEntity); 
                Context.SaveChanges();
            } 
            catch (Exception e) 
            {
                // Catches errors on the context. 
                EntityDataSourceChangedEventArgs changedArgs = new EntityDataSourceChangedEventArgs(e);
                OnDeleted(changedArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Delete));
                if (!changedArgs.ExceptionHandled) 
                {
                    throw; 
                } 
                return -1;
            } 
            OnDeleted(new EntityDataSourceChangedEventArgs(Context, entity)); //Outside "try" to prevent being called twice.
            return 1;
        }
        #endregion ExecuteDelete 

        #region ExecuteInsert 
 
        protected override int ExecuteInsert(IDictionary values)
        { 
            if (!CanInsert)
            {
                throw new InvalidOperationException(Strings.EntityDataSourceView_InsertDisabledForThisControl);
            } 

            ConstructContext(); 
 
            EntityDataSourceChangingEventArgs changingArgs;
            EntityDataSourceWrapperCollection wrapperCollection = new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType); 
            EntityDataSourceWrapper entityWrapper;
            try
            {
                object entity = EntityDataSourceUtil.InitializeType(this.EntityClrType); 
                Context.AddObject(_owner.FQEntitySetName, entity);
                entityWrapper = new EntityDataSourceWrapper(wrapperCollection, entity); 
                CreateEntityForInsert(entityWrapper, values, _owner.InsertParameters); 
            }
            catch (EntityDataSourceValidationException e) 
            {
                changingArgs = new EntityDataSourceChangingEventArgs(e);
                OnInserting(changingArgs);
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Insert)); 
                if (!changingArgs.ExceptionHandled)
                { 
                    throw; 
                }
                return -1; 
            }

            changingArgs = new EntityDataSourceChangingEventArgs(Context, entityWrapper.WrappedEntity);
            OnInserting(changingArgs); //Could return an entity to insert. 
            if (changingArgs.Cancel)
            { 
                return -1; 
            }
 
            if (!Object.ReferenceEquals(entityWrapper.WrappedEntity, changingArgs.Entity))
            {
                Context.Detach(entityWrapper.WrappedEntity);
                Context.AddObject(_owner.EntitySetName, changingArgs.Entity); 
                entityWrapper = new EntityDataSourceWrapper(wrapperCollection, changingArgs.Entity);
            } 
 
            try
            { 
                Context.SaveChanges();
            }
            catch (Exception e)
            { 
                EntityDataSourceChangedEventArgs changedArgs = new EntityDataSourceChangedEventArgs(e);
                OnInserted(changedArgs); 
                OnException(new DynamicValidatorEventArgs(e, DynamicDataSourceOperation.Insert)); 
                if (!changedArgs.ExceptionHandled)
                { 
                    throw;
                }
                return -1;
            } 
            OnInserted(new EntityDataSourceChangedEventArgs(Context, entityWrapper.WrappedEntity));
            return 1; 
        } 

        #endregion ExecuteInsert 

        #region Public Methods
        public DataTable GetViewSchema()
        { 
            EntityDataSourceViewSchema propTable;
            if (_owner.ValidateWrappable()) 
            { 
                ConstructContext();
                EntityDataSourceWrapperCollection wrappers = new EntityDataSourceWrapperCollection(Context, EntitySet, CSpaceFilteredEntityType); 
                propTable = new EntityDataSourceViewSchema(wrappers);
            }
            else
            { 
                string where = _owner.Where;
                _owner.Where = "0=1"; 
                try 
                {
                    DataSourceSelectArguments args = new DataSourceSelectArguments(); 
                    args.RetrieveTotalRowCount = false;
                    IEnumerable results = ExecuteSelect(args);
                    ITypedList typedList = results as ITypedList;
                    if (null != typedList) 
                    {
                        propTable = new EntityDataSourceViewSchema(typedList); 
                    } 
                    else
                    { 
                        propTable = new EntityDataSourceViewSchema(results);
                    }
                }
                finally 
                {
                    _owner.Where = where; 
                } 
            }
            return propTable; 
        }
        #endregion Public Methods

        #region Private Methods 

        #region ExecuteSelect Support 
 
        private void GenerateWhereClause(out string whereClause, out ObjectParameter[] whereParameters)
        { 
            if (!_owner.AutoGenerateWhereClause)
            {
                whereClause = _owner.Where;
                whereParameters = _owner.GetWhereParameters(); 
                return;
            } 
 
            //This is the automatically generated Where clause.
            IOrderedDictionary paramValues = _owner.WhereParameters.GetValues(_owner.HttpContext, _owner); 
            // Under some conditions, the paramValues has a null entry.
            StringBuilder whereClauseBuilder = new StringBuilder();
            List whereParameterList = new List();
            bool first = true; 

            int idx = 0; 
            foreach (DictionaryEntry de in paramValues) 
            {
                string propertyName = (string)(de.Key); 
                if (0 < propertyName.Length && null != de.Value)
                {
                    if (!String.IsNullOrEmpty(_owner.EntitySetName) && !EntityDataSourceUtil.PropertyIsOnEntity(propertyName, _wrapperCollection, EntitySet, null))
                    { 
                        throw new InvalidOperationException(Strings.EntityDataSourceView_PropertyDoesNotExistOnEntity(propertyName, EntityClrType.FullName));
                    } 
 
                    if (first)
                    { 
                        first = false;
                    }
                    else
                    { 
                        whereClauseBuilder.Append(" AND ");
                    } 
 
                    string namedParameterName = "NamedParameter" + idx.ToString(CultureInfo.InvariantCulture);
 
                    whereClauseBuilder.Append(EntityDataSourceUtil.GetEntitySqlValueForColumnName(propertyName, _wrapperCollection));
                    whereClauseBuilder.Append("=@");
                    whereClauseBuilder.Append(namedParameterName);
 
                    whereParameterList.Add(new ObjectParameter(namedParameterName, de.Value));
                    idx++; 
                } 
            }
 
            whereParameters = whereParameterList.ToArray();
            whereClause = whereClauseBuilder.ToString();
        }
 
        #endregion ExecuteSelect Support
 
        #region Entity Creation Support for Update/Insert/Delete 

        private object ConvertProperty(object origPropertyValue, PropertyDescriptor propertyDescriptor, WebControlParameterProxy referenceParameter) 
        {
            if (null == propertyDescriptor)
            {
                return null; 
            }
 
            object propValue = origPropertyValue; 
            propValue = Parameter_GetValue(propValue, referenceParameter, true);
            propValue = ConvertType(propValue, propertyDescriptor.PropertyType, propertyDescriptor.Name); 
            return propValue;
        }

        private void ConvertWCProperty(IDictionary values, 
            Dictionary convertedValues,
            List visitedProperties, 
            PropertyDescriptor pd, 
            ParameterCollection referenceParameters,
            ref Dictionary exceptions) 
        {
            string propertyName = pd.Name;
            WebControlParameterProxy wcParameter = new WebControlParameterProxy(propertyName, referenceParameters, _owner);
            object propValue = null; 
            object value = null;
            if (null != values) 
            { 
                value = values[propertyName];
            } 
            try
            {
                propValue = ConvertProperty(value, pd, wcParameter);
            } 
            catch (Exception e)
            { 
                if (null == exceptions) 
                {
                    exceptions = new Dictionary(); 
                }
                exceptions.Add(propertyName, e);
            }
            convertedValues[propertyName] = propValue; 
            visitedProperties.Add(propertyName);
        } 
        private IDictionary ConvertProperties(IDictionary values, PropertyDescriptorCollection propertyDescriptors, ParameterCollection referenceParameters) 
        {
            Dictionary convertedValues = new Dictionary(); 
            List visitedProperties = new List();
            Dictionary exceptions = null;

            // "values" come from the web page. Either via keys or original values. This loops sets them as first priority 
            foreach (string propertyName in values.Keys)
            { 
                PropertyDescriptor pd = propertyDescriptors.Find(propertyName, /*ignoreCase*/ false); 
                ConvertWCProperty(values, convertedValues, visitedProperties,
                    pd, referenceParameters, ref exceptions); 
            }

            // If a property hasn't been set by visible columns or DataKeyNames, then we set them here.
            // "referenceParameters" are Insert, Update or DeleteParameters assigning default values to 
            // columns that are not set by the control
            if (null != referenceParameters) 
            { 
                IOrderedDictionary referenceValues = referenceParameters.GetValues(_owner.HttpContext, _owner);
                foreach (string propertyName in referenceValues.Keys) 
                {
                    if (!visitedProperties.Contains(propertyName))
                    {
                        PropertyDescriptor pd = propertyDescriptors.Find(propertyName, /*ignoreCase*/ false); 
                        ConvertWCProperty(null, convertedValues, visitedProperties,
                            pd, referenceParameters, ref exceptions); 
 
                    }
                } 
            }

            if (null != exceptions)
            { 
                // The IDynamicValidationException encapsulates all of the data conversion errors.
                // This exposes one of the encapsulated errors in its own message like: 
                //   "Error while setting property 'ProductName': 'The value cannot be null.'." 
                string key = exceptions.Keys.First();
                throw new EntityDataSourceValidationException( 
                    Strings.EntityDataSourceView_DataConversionError(
                        key, exceptions[key].Message), exceptions);
            }
 
            return convertedValues;
        } 
 
        private void CreateEntityForInsert(EntityDataSourceWrapper entityWrapper, IDictionary values, ParameterCollection insertParameters)
        { 
            EntityDataSourceUtil.ValidateWebControlParameterNames(entityWrapper, insertParameters, _owner);

            // Throws EntityDataSourceValidationException if data conversion fails
            values = ConvertProperties(values, entityWrapper.GetProperties(), insertParameters); 

            EntityDataSourceUtil.SetAllPropertiesWithVerification(entityWrapper, (Dictionary)values, /*overwrite*/true); 
        } 

        private void UpdateEntity(IDictionary mergedKeysAndOldValues, IDictionary values, 
                                  EntityDataSourceWrapper entityWrapper, ParameterCollection updateParameters)
        {
            EntityDataSourceUtil.ValidateWebControlParameterNames(entityWrapper, updateParameters, _owner);
 
            values = ConvertProperties(values, entityWrapper.GetProperties(), updateParameters);
            Dictionary allModifiedProperties = new Dictionary(); 
 
            // Compare the propertyValues from the original values from the page to those in the new "values" properties.
            // Note that the comparison is between the values that came back from the databound control, BUT 
            // The values on the entity in the entityWrapper came from ViewState. We can't compare the values from the page
            // To those stored in ViewState in case they didn't roundtrip well. Hence this next loop.
            foreach (string propertyName in values.Keys)
            { 
                object originalValue = mergedKeysAndOldValues[propertyName];
                object newValue = values[propertyName]; 
 
                if (!OriginalValueMatches(originalValue, newValue))
                { 
                    allModifiedProperties[propertyName] = newValue;
                }
            }
 
            EntityDataSourceUtil.SetAllPropertiesWithVerification(entityWrapper, allModifiedProperties, /*overwrite*/false);
        } 
 
        private IDictionary MergeDictionaries(IDictionary keys, IDictionary values)
        { 
            Dictionary mergedValues = new Dictionary();
            // Add all the keys
            foreach (string propName in keys.Keys)
            { 
                mergedValues.Add(propName, keys[propName]);
            } 
            // Add the properties from values 
            foreach (string propName in values.Keys)
            { 
                if (!mergedValues.ContainsKey(propName))
                {
                    mergedValues.Add(propName, values[propName]);
                } 
            }
            return mergedValues; 
        } 

        private static object ConvertType(object value, Type type, string paramName) 
        {
            // NOTE: This method came from ObjectDataSource via LinqDataSource.
            string s = value as string;
            if (s != null) 
            {
                // Get the type converter for the destination type 
                TypeConverter converter = TypeDescriptor.GetConverter(type); 
                if (converter != null)
                { 
                    // Perform the conversion
                    try
                    {
                        value = converter.ConvertFromString(s); 
                    }
                    catch (NotSupportedException) 
                    { 
                        throw new InvalidOperationException(Strings.EntityDataSourceView_UnableToConvertStringToType(s, type.FullName));
                    } 
                    catch (FormatException)
                    {
                        throw new InvalidOperationException(Strings.EntityDataSourceView_UnableToConvertStringToType(s, type.FullName));
                    } 
                }
            } 
            return value; 
        }
 
        #endregion Entity Creation Support

        #region ViewState Storage
 
        internal void StoreOriginalPropertiesIntoViewState()
        { 
            if (null == _wrapperCollection || 
                0 == _wrapperCollection.Count ||
                !_owner.StoreOriginalValuesInViewState || 
                (!CanDelete && !CanUpdate)) //Only store entities into viewstate if Delete or Update is enabled.
            {
                return;
            } 

            _originalProperties = new Dictionary(); 
 
            PropertyDescriptorCollection collection = _wrapperCollection.GetItemProperties(null);
 
            // Retrieve the named properties from each entity and place them into the viewstate collection.
            foreach (EntityDataSourceWrapper wrapper in _wrapperCollection)
            {
                foreach (PropertyDescriptor propertyDescriptor in collection) 
                {
                    EntityDataSourceWrapperPropertyDescriptor wrapperPropertyDescriptor = (EntityDataSourceWrapperPropertyDescriptor)propertyDescriptor; 
 
                    if (wrapperPropertyDescriptor.Column.IsInteresting && wrapperPropertyDescriptor.Column.IsPrimitive)
                    { 
                        object property = wrapperPropertyDescriptor.GetValue(wrapper);
                        string propertyName = wrapperPropertyDescriptor.DisplayName;

                        if (!_originalProperties.ContainsKey(propertyName)) 
                        {
                            _originalProperties[propertyName] = new ArrayList(); 
                        } 

                        (_originalProperties[propertyName]).Add(property); 
                    }
                }
            }
        } 

        private void InstantiateEntityFromViewState(EntityDataSourceWrapper entityWrapper, IDictionary mergedKeysAndOldValues) 
        { 
            int idx = FindIdxOfPropertiesStoredInViewState(mergedKeysAndOldValues);
            Dictionary changedProperties = new Dictionary(); 

            if (0 <= idx) // "-1" indicates that the value was not found in ViewState
            {
                foreach (string propertyName in _originalProperties.Keys) 
                {
                    object property = (_originalProperties[propertyName])[idx]; 
                    changedProperties.Add(propertyName, property); 
                }
            } 

            // Now, add all the values from the page to the constructed entity.
            // This fills in the values from the page, into all properties not stored in ViewState.
            Dictionary mergedDict = mergedKeysAndOldValues as Dictionary; 
            if (null != mergedDict)
            { 
                foreach (string key in mergedDict.Keys) 
                {
                    if (!changedProperties.ContainsKey(key)) 
                    {
                        changedProperties.Add(key, mergedDict[key]);
                    }
                } 
            }
 
            EntityDataSourceUtil.SetAllPropertiesWithVerification(entityWrapper, changedProperties, /*overwrite*/true); 
        }
 
        /// 
        /// Returns the index of the entity in ViewState that has key values that match the values from the page.
        /// If a match is not found, it returns -1.
        ///  
        /// 
        ///  
        private int FindIdxOfPropertiesStoredInViewState(IDictionary mergedKeysAndOldValues) 
        {
            if (null == _originalProperties || 0 == _originalProperties.Count) 
            {
                return -1;
            }
 
            Dictionary localMergedKeysAndOldValues = (Dictionary)mergedKeysAndOldValues;
 
            // This get the number of entities from the first property's values. 
            // The ArrayList that holds the values contains the count of the number of entities.
            int numEntities = (_originalProperties.First().Value).Count; 
            for (int idx = 0; idx < numEntities; idx++)
            {
                bool match = true;
                foreach (EdmMember edmMember in KeyMembers) 
                {
                    string propertyName = edmMember.Name; 
                    // The page must return the key values. 
                    if (!localMergedKeysAndOldValues.ContainsKey(propertyName))
                    { 
                        match = false;
                        break;
                    }
 
                    object origValue = (_originalProperties[propertyName])[idx];
                    object pageValue = localMergedKeysAndOldValues[propertyName]; 
                    if (!OriginalValueMatches(origValue, pageValue)) 
                    {
                        match = false; 
                        break;
                    }
                }
                if (match) 
                {
                    return idx; 
                } 
            }
            return -1; 
        }

        private bool OriginalValueMatches(object originalValue, object value)
        { 
            if (null == originalValue)
            { 
                if (null == value) 
                {
                    return true; 
                }
                return false;
            }
 
            // NOTE: Comparing IEnumerable contents instead of instances to ensure that
            // timestamp columns (of type byte[]) can be matched appropriately. 
            IEnumerable originalValueEnumerable = originalValue as IEnumerable; 
            IEnumerable valueEnumerable = value as IEnumerable;
            if ((originalValueEnumerable != null) && (valueEnumerable != null)) 
            {
                return EnumerableContentEquals(originalValueEnumerable, valueEnumerable);
            }
 
            return originalValue.Equals(value);
        } 
 
        #endregion ViewState Storage
 
        #region Context and Query construction

        private void ConstructContext()
        { 
            this.DisposeContext();
 
            Type contextType = null; 
            EntityDataSourceContextCreatingEventArgs creatingArgs = new EntityDataSourceContextCreatingEventArgs();
            OnContextCreating(creatingArgs); 
            if (null != creatingArgs.Context) //Context was created in event code
            {
                _ctx = creatingArgs.Context;
                contextType = _ctx.GetType(); 
            }
            else if (null != _owner.ContextType || !String.IsNullOrEmpty(_owner.ContextTypeName)) 
            { 
                //Construct the context.
                if (null != _owner.ContextType) 
                {
                    contextType = _owner.ContextType;
                }
                else 
                {
                    contextType = System.Web.Compilation.BuildManager.GetType(_owner.ContextTypeName, /*throw on error*/ true); 
                } 

                ConstructorInfo ctxInfo = contextType.GetConstructor( 
                    BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance,
                    null, System.Type.EmptyTypes, null);

                if (null == ctxInfo) 
                {
                    throw new InvalidOperationException(Strings.EntityDataSourceView_NoParameterlessConstructorForTheContext); 
                } 

                _ctx = (ObjectContext)ctxInfo.Invoke(new object[0]); 
            }
            else // Non-strongly-typed context built from ConnectionString and DefaultContainerName
            {
                if (!String.IsNullOrEmpty(_owner.DefaultContainerName) && 
                    !String.IsNullOrEmpty(_owner.ConnectionString))
                { 
                    _ctx = new ObjectContext(_owner.ConnectionString); 

                    if (System.Web.Hosting.HostingEnvironment.IsHosted) 
                    {
                        // Since we don't have the type from the strongly-typed context,
                        // load from all of the referenced assemblies, including code from App_Code and the top-level directory:
                        // http://msdn2.microsoft.com/en-us/library/system.web.compilation.buildmanager.getreferencedassemblies.aspx 
                        ICollection codeAssemblies = System.Web.Compilation.BuildManager.GetReferencedAssemblies();
                        foreach (Assembly assembly in codeAssemblies) 
                        { 
                            _ctx.MetadataWorkspace.LoadFromAssembly(assembly);
                        } 
                    }
                }
                else if (!String.IsNullOrEmpty(_owner.DefaultContainerName) &&
                          null != _owner.Connection) 
                {
                    _ctx = new ObjectContext(_owner.Connection); 
                } 
                else
                { 
                    throw new InvalidOperationException(Strings.EntityDataSourceView_ObjectContextMustBeSpecified);
                }
                // Must set the DefaultContainerName for both of the above conditions.
                _ctx.DefaultContainerName = _owner.DefaultContainerName; 
                contextType = typeof(ObjectContext);
 
            } 

            _ctx.MetadataWorkspace.LoadFromAssembly(System.Reflection.Assembly.GetCallingAssembly()); 
            _ctx.MetadataWorkspace.LoadFromAssembly(contextType.Assembly);


            // Error Checking on the Context 
            ValidateContainerName();
 
            OnContextCreated(new EntityDataSourceContextCreatedEventArgs(Context)); 
        }
 

        internal void DisposeContext()
        {
            if (null != _ctx) 
            {
                EntityDataSourceContextDisposingEventArgs disposeArgs = new EntityDataSourceContextDisposingEventArgs(_ctx); 
                OnContextDisposing(disposeArgs); 
                if (!disposeArgs.Cancel)
                { 
                    _ctx.Dispose();
                    _ctx = null;
                }
            } 
        }
 
        #endregion Context and Query construction 
        #endregion Private Methods
 
        #region Public Overrides
        public override bool CanInsert
        {
            get { return _owner.EnableInsert && !_disableUpdates; } 
        }
        public override bool CanUpdate 
        { 
            get { return _owner.EnableUpdate && !_disableUpdates; }
        } 
        public override bool CanDelete
        {
            get { return _owner.EnableDelete && !_disableUpdates; }
        } 
        public override bool CanSort
        { 
            get { return _owner.AutoSort; } 
        }
        public override bool CanPage 
        {
            get { return _owner.AutoPage; }
        }
        public override bool CanRetrieveTotalRowCount 
        {
            get { return _owner.AutoPage; } 
        } 
        #endregion Public Overrides
 
        #region Events

        //Oryx exception event
        private void OnException(DynamicValidatorEventArgs args) 
        {
            EventHandler handler = 
                (EventHandler)Events[EventException]; 
            if (null != handler)
            { 
                handler(this, args);
            }
        }
        public event EventHandler Exception 
        {
            add { Events.AddHandler(EventException, value); } 
            remove { Events.RemoveHandler(EventException, value); } 
        }
 
        // ContextCreating Event
        private void OnContextCreating(EntityDataSourceContextCreatingEventArgs e)
        {
            EventHandler handler = 
                (EventHandler)Events[EventContextCreating];
 
            if (null != handler) 
            {
                handler(this, e); 
            }
        }
        public event EventHandler ContextCreating
        { 
            add { Events.AddHandler(EventContextCreating, value); }
            remove { Events.RemoveHandler(EventContextCreating, value); } 
        } 

        // ContextCreated Event 
        private void OnContextCreated(EntityDataSourceContextCreatedEventArgs e)
        {
            EventHandler handler =
                (EventHandler)Events[EventContextCreated]; 

            if (null != handler) 
            { 
                handler(this, e);
            } 
        }
        public event EventHandler ContextCreated
        {
            add { Events.AddHandler(EventContextCreated, value); } 
            remove { Events.RemoveHandler(EventContextCreated, value); }
        } 
 
        // ContextDisposing Event
        private void OnContextDisposing(EntityDataSourceContextDisposingEventArgs e) 
        {
            EventHandler handler =
                (EventHandler)Events[EventContextDisposing];
 
            if (null != handler)
            { 
                handler(this, e); 
            }
        } 
        public event EventHandler ContextDisposing
        {
            add { Events.AddHandler(EventContextDisposing, value); }
            remove { Events.RemoveHandler(EventContextDisposing, value); } 
        }
 
        // Selecting Event 
        private void OnSelecting(EntityDataSourceSelectingEventArgs e)
        { 
            EventHandler handler =
                (EventHandler)Events[EventSelecting];
            if (null != handler)
            { 
                handler(this, e);
            } 
        } 
        public event EventHandler Selecting
        { 
            add { Events.AddHandler(EventSelecting, value); }
            remove { Events.RemoveHandler(EventSelecting, value); }
        }
 
        // Selected Event
        private void OnSelected(EntityDataSourceSelectedEventArgs e) 
        { 
            EventHandler handler =
                (EventHandler)Events[EventSelected]; 
            if (null != handler)
            {
                handler(this, e);
            } 
        }
        public event EventHandler Selected 
        { 
            add { Events.AddHandler(EventSelected, value); }
            remove { Events.RemoveHandler(EventSelected, value); } 
        }

        // Deleting Event
        private void OnDeleting(EntityDataSourceChangingEventArgs e) 
        {
            EventHandler handler = 
                (EventHandler)Events[EventDeleting]; 
            if (null != handler)
            { 
                handler(this, e);
            }
        }
        public event EventHandler Deleting 
        {
            add { Events.AddHandler(EventDeleting, value); } 
            remove { Events.RemoveHandler(EventDeleting, value); } 
        }
 
        // Deleted Event
        private void OnDeleted(EntityDataSourceChangedEventArgs e)
        {
            EventHandler handler = 
                (EventHandler)Events[EventDeleted];
            if (null != handler) 
            { 
                handler(this, e);
            } 
        }
        public event EventHandler Deleted
        {
            add { Events.AddHandler(EventDeleted, value); } 
            remove { Events.RemoveHandler(EventDeleted, value); }
        } 
 
        //Inserting Event
        private void OnInserting(EntityDataSourceChangingEventArgs e) 
        {
            EventHandler handler =
                (EventHandler)Events[EventInserting];
            if (handler != null) 
            {
                handler(this, e); 
            } 
        }
        public event EventHandler Inserting 
        {
            add { Events.AddHandler(EventInserting, value); }
            remove { Events.RemoveHandler(EventInserting, value); }
        } 

        //Inserted Event 
        private void OnInserted(EntityDataSourceChangedEventArgs e) 
        {
            EventHandler handler = 
                (EventHandler)Events[EventInserted];
            if (handler != null)
            {
                handler(this, e); 
            }
        } 
        public event EventHandler Inserted 
        {
            add { Events.AddHandler(EventInserted, value); } 
            remove { Events.RemoveHandler(EventInserted, value); }
        }

        //Updating Event 
        private void OnUpdating(EntityDataSourceChangingEventArgs e)
        { 
            EventHandler handler = 
                (EventHandler)Events[EventUpdating];
            if (handler != null) 
            {
                handler(this, e);
            }
        } 
        public event EventHandler Updating
        { 
            add { Events.AddHandler(EventUpdating, value); } 
            remove { Events.RemoveHandler(EventUpdating, value); }
        } 

        //Updated Event
        private void OnUpdated(EntityDataSourceChangedEventArgs e)
        { 
            EventHandler handler =
                (EventHandler)Events[EventUpdated]; 
            if (handler != null) 
            {
                handler(this, e); 
            }
        }
        public event EventHandler Updated
        { 
            add { Events.AddHandler(EventUpdated, value); }
            remove { Events.RemoveHandler(EventUpdated, value); } 
        } 

 
        internal void RaiseChangedEvent()
        {
            OnDataSourceViewChanged(EventArgs.Empty);
        } 

        #endregion Events 
 
        #region Private Properties
 
        private StructuralType EntityOSpaceType
        {
            get
            { 
                // If the CSpaceType is not determinable (e.g. DataSource.EntitySetName is not specified),
                // then return null. 
                if (null == EntityCSpaceType) 
                {
                    return null; 
                }
                StructuralType oSpaceType = Context.MetadataWorkspace.GetObjectSpaceType(EntityCSpaceType);
                return oSpaceType;
            } 
        }
 
        // Returns the type for EntityTypeFilter, or 
        // null if both EntitySet and EntityTypeFilter are not specified.
        private EntityType CSpaceFilteredEntityType 
        {
            get
            {
                EntityType cSpaceType = null; 
                if (null == EntitySet)
                { 
                    return null; //read-only scenario in which the EntitySet is not specified on the DataSource 
                }
 
                // Return the type specified in EntityTypeFilter
                if (!String.IsNullOrEmpty(_owner.EntityTypeFilter))
                {
                    cSpaceType = (EntityType)Context.MetadataWorkspace.GetType(_owner.EntityTypeFilter, EntitySet.ElementType.NamespaceName, DataSpace.CSpace); 
                    if (!EntityDataSourceUtil.IsTypeOrSubtypeOf(EntitySet.ElementType, cSpaceType, Context.MetadataWorkspace.GetItemCollection(DataSpace.CSpace)))
                    { 
                        throw new InvalidOperationException(Strings.EntityDataSourceView_FilteredEntityTypeMustBeDerivableFromEntitySet(_owner.EntityTypeFilter, _owner.EntitySetName)); 
                    }
                    return cSpaceType; 
                }

                return null;
 
            }
        } 
 
        // Returns the actualy EntityCSpaceType to be returned from the query, either via
        // EntityTypeFilter or via the base type for the EntitySet. 
        private EntityType EntityCSpaceType
        {
            get
            { 
                EntityType cSpaceType = CSpaceFilteredEntityType;
                if (null != cSpaceType) 
                { 
                    return cSpaceType;
                } 

                if (null != EntitySet)
                {
                    //If EntityDataSource.EntityTypeFilter is not specified, return the base type for the EntitySet. 
                    cSpaceType = EntitySet.ElementType;
                } 
                return cSpaceType; 
            }
 
        }

        private EntityContainer EntityContainer
        { 
            get
            { 
                return Context.MetadataWorkspace.GetEntityContainer(ContainerName, DataSpace.CSpace); 
            }
        } 

        /// 
        /// The EntitySet Associated with this DataSource. If EntityDataSource.EntitySetName is not set, then
        /// This property returns null. 
        /// 
        private EntitySet EntitySet 
        { 
            get
            { 
                if (String.IsNullOrEmpty(_owner.EntitySetName))
                {
                    return null;
                } 
                return EntityContainer.GetEntitySetByName(_owner.EntitySetName, /*ignoreCase*/ false);
            } 
        } 

        private Type EntityClrType 
        {
            get
            {
                ObjectItemCollection objectItemCollection = 
                    (ObjectItemCollection)(Context.MetadataWorkspace.GetItemCollection(DataSpace.OSpace));
                Type clrType = objectItemCollection.GetClrType(EntityOSpaceType); 
                return clrType; 
            }
        } 

        private ObjectContext Context
        {
            get 
            {
                Debug.Assert(null != _ctx, "The context hasn't yet been constructed"); 
                return _ctx; 
            }
        } 

        private ReadOnlyMetadataCollection KeyMembers
        {
            get 
            {
                if (null == _keyMembers) 
                { 
                    EntityContainer entityContainer = Context.MetadataWorkspace.GetEntityContainer(ContainerName, DataSpace.CSpace);
                    EntitySet entitySet = entityContainer.GetEntitySetByName(_owner.EntitySetName, false); 

                    _keyMembers = ((EntityType)(entitySet.ElementType)).KeyMembers;
                }
                return _keyMembers; 
            }
        } 
 
        private string ContainerName
        { 
            get
            {
                if (!string.IsNullOrEmpty(_owner.DefaultContainerName))
                { 
                    return _owner.DefaultContainerName;
                } 
                if (!string.IsNullOrEmpty(Context.DefaultContainerName)) 
                {
                    return Context.DefaultContainerName; 
                }
                throw new InvalidOperationException(Strings.EntityDataSourceView_ContainerNameMustBeSpecified);
            }
        } 

        #endregion Private Properties 
 
        #region private getters
 
        #endregion private getters

        #region IStateManager implementation
        bool IStateManager.IsTrackingViewState 
        {
            get { return _tracking; } 
        } 
        void IStateManager.LoadViewState(object savedState)
        { 
            if (null != savedState)
            {
                Hashtable myState = (Hashtable)savedState;
                _tracking = (bool)myState["_tracking"]; 
                _disableUpdates = (bool)myState["_disableUpdates"];
                _originalProperties = (Dictionary)myState["_originalProperties"]; 
            } 
        }
 
        object IStateManager.SaveViewState()
        {
            StoreOriginalPropertiesIntoViewState();
 
            Hashtable myState = new Hashtable();
            myState.Add("_tracking", _tracking); 
            myState.Add("_disableUpdates", _disableUpdates); 
            myState.Add("_originalProperties", _originalProperties);
            return myState; 
        }
        void IStateManager.TrackViewState()
        {
            _tracking = true; 
        }
        #endregion 
 
        #region Utilities and Error Checking
 
        private void AddSupportedCapabilities(DataSourceSelectArguments arguments)
        {
            if (CanSort)
            { 
                arguments.AddSupportedCapabilities(DataSourceCapabilities.Sort);
            } 
            if (CanPage) 
            {
                arguments.AddSupportedCapabilities(DataSourceCapabilities.Page); 
                arguments.AddSupportedCapabilities(DataSourceCapabilities.RetrieveTotalRowCount);
            }
        }
 
        internal void ValidateEntitySetName()
        { 
            EntityContainer entityContainer = Context.MetadataWorkspace.GetEntityContainer(ContainerName, DataSpace.CSpace); 
            EntitySet entitySet;
            if (!entityContainer.TryGetEntitySetByName(_owner.EntitySetName, /*ignoreCase*/false, out entitySet)) 
            {
                throw new InvalidOperationException(Strings.EntityDataSourceView_EntitySetDoesNotExistOnTheContainer(_owner.EntitySetName));
            }
        } 

        private void ValidateContainerName() 
        { 
            EntityContainer container;
            if (!Context.MetadataWorkspace.TryGetEntityContainer(ContainerName, DataSpace.CSpace, out container)) 
            {
                throw new InvalidOperationException(Strings.EntityDataSourceView_ContainerNameDoesNotExistOnTheContext(ContainerName));
            }
        } 

        // From LinqDataSourceHelper. Timestamp columns (of type byte[]) are of type IEnumerable. 
        // This routine compares all elements of the IEnumerable. 
        private static bool EnumerableContentEquals(IEnumerable enumerableA, IEnumerable enumerableB)
        { 
            IEnumerator enumeratorA = enumerableA.GetEnumerator();
            IEnumerator enumeratorB = enumerableB.GetEnumerator();
            while (enumeratorA.MoveNext())
            { 
                if (!enumeratorB.MoveNext())
                    return false; 
                object itemA = enumeratorA.Current; 
                object itemB = enumeratorB.Current;
                if (itemA == null) 
                {
                    if (itemB != null)
                        return false;
                } 
                else if (!itemA.Equals(itemB))
                    return false; 
            } 
            if (enumeratorB.MoveNext())
                return false; 
            return true;
        }

        // This routine modified from System.Web.Ui.WebControls. 
        private static object Parameter_GetValue(object value, WebControlParameterProxy parameter, bool ignoreNullableTypeChanges)
        { 
            // Convert.ChangeType() throws if you attempt to convert to DBNull, so we have to special case it. 
            if (parameter.TypeCode == TypeCode.DBNull)
            { 
                return DBNull.Value;
            }

            // Get the value and convert it to the default value if it is null 
            if (parameter.ConvertEmptyStringToNull)
            { 
                string stringValue = value as string; 
                if ((stringValue != null) && (stringValue.Length == 0))
                { 
                    value = null;
                }

            } 

            if (value == null) // Fill it with values from referenceParameters 
            { 
                // Use the parameter value if it is non-null
                if (!parameter.HasValue) 
                {
                    return value;
                }
 
                object parameterValue = parameter.Value;
                string valueString = parameterValue as String; 
                if (parameter.TypeCode == TypeCode.String && parameter.ConvertEmptyStringToNull && String.IsNullOrEmpty(valueString)) 
                {
                    parameterValue = null; 
                }

                if (null == parameterValue)
                { 
                    return null;
                } 
                value = parameterValue; 
            }
 
            Debug.Assert(value != null, "Value should not be null at this point.");

            if (parameter.TypeCode == TypeCode.Object || parameter.TypeCode == TypeCode.Empty)
            { 
                return value;
            } 
 
            // For ObjectDataSource we special-case Nullable and do nothing because these
            // types will get converted when we actually call the method. 
            if (ignoreNullableTypeChanges)
            {
                Type valueType = value.GetType();
                if (valueType.IsGenericType && (valueType.GetGenericTypeDefinition() == typeof(Nullable<>))) 
                {
                    return value; 
                } 
            }
            return value = Convert.ChangeType(value, parameter.TypeCode, CultureInfo.CurrentCulture); ; 
        }


        #endregion Utilities 

        //public override void Delete(IDictionary keys, IDictionary oldValues, DataSourceViewOperationCallback callback){} 
        //public override void Insert(IDictionary values, DataSourceViewOperationCallback callback){} 
        //public override void Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback){}
        //public override void Update(IDictionary keys, IDictionary values, IDictionary oldValues, DataSourceViewOperationCallback callback){} 

    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK