// The provider manifest token helps to distinguish between store versions.
        // We have only one backend version.
        // However, use the connection passed in to determine whether
        // the provider is local provider or remote provider
        //
        /// <summary>
        /// Returns provider manifest token for a given connection.
        /// </summary>
        /// <param name="connection">Connection to find manifest token from.</param>
        /// <returns>The provider manifest token for the specified connection.</returns>
        protected override string GetDbProviderManifestToken(DbConnection connection)
        {
            Check.NotNull(connection, "connection");

            // vamshikb: Do we need to validate the connection and connection string
            // before returning the ProviderManifestToken????

            // Determine the type of DbConnection
            // This method should never be called at runtime, so the provider
            // must be remote provider.
            // Throw if it is none.
            //
            if (connection.GetType() == typeof(SqlCeConnection))
            {
                _isLocalProvider = true;
            }
            else if (RemoteProviderHelper.CompareObjectEqualsToType(connection, RemoteProvider.SqlCeConnection))
            {
                _isLocalProvider = false;
            }
            else
            {
                throw ADP1.Argument(EntityRes.GetString(EntityRes.Mapping_Provider_WrongConnectionType, "SqlCeConnection"));
            }

            return(SqlCeProviderManifest.Token40);
        }
        /// <summary>
        /// API for checkin whether database exists or not.
        /// This will internally only check whether the file that the connection points to exists or not.
        /// Note: In case of SQLCE, timeout and storeItemCollection parameters are ignored.
        /// </summary>
        /// <param name="connection"> Connection </param>
        /// <param name="timeOut"> Timeout for internal commands. </param>
        /// <param name="storeItemCollection"> Item Collection. </param>
        /// <returns> Bool indicating whether database exists or not. </returns>
        protected override bool DbDatabaseExists(DbConnection connection, int?timeOut, Lazy <StoreItemCollection> storeItemCollection)
        {
            Check.NotNull(connection, "connection");
            Check.NotNull(storeItemCollection, "storeItemCollection");

            // Validate and cast the connection.
            ValidateConnection(connection);

            if (_isLocalProvider)
            {
                return(CommonUtils.DatabaseExists(DbInterception.Dispatch.Connection.GetDataSource(connection, new DbInterceptionContext())));
            }
            else
            {
                Type rdpType;

                // If we are working with RDP, then we will need to invoke the APIs through reflection.
                var engine = RemoteProviderHelper.GetRemoteSqlCeEngine(
                    DbInterception.Dispatch.Connection.GetConnectionString(connection, new DbInterceptionContext()),
                    out rdpType);
                Debug.Assert(engine != null);

                var mi = rdpType.GetMethod("FileExists", new[] { typeof(string), typeof(int?) });
                Debug.Assert(mi != null);

                // We will pass 'timeout' to RDP, this will be used as timeout period for connecting and executing on TDSServer.
                return((bool)(mi.Invoke(engine,
                                        new object[] { DbInterception.Dispatch.Connection.GetDataSource(connection, new DbInterceptionContext()), timeOut })));
            }
        }
        // Private helper for validatingn the SqlCeConnection.
        private void ValidateConnection(DbConnection connection)
        {
            // Check whether it is a valid SqlCeConnection.
            var isValid = _isLocalProvider
                              ? connection is SqlCeConnection
                              : RemoteProviderHelper.CompareObjectEqualsToType(connection, RemoteProvider.SqlCeConnection);

            if (!isValid)
            {
                throw ADP1.InvalidConnectionType();
            }
        }
        /// <summary>
        /// API for deleting the database.
        /// In SQLCE case, this will translate to File.Delete() call.
        /// Note: Timeout and storeItemCollection parameters are ignored.
        /// </summary>
        /// <param name="connection"> Connection </param>
        /// <param name="timeOut"> Timeout for internal commands. </param>
        /// <param name="storeItemCollection"> Item Collection. </param>
        protected override void DbDeleteDatabase(DbConnection connection, int?timeOut, StoreItemCollection storeItemCollection)
        {
            Check.NotNull(connection, "connection");
            Check.NotNull(storeItemCollection, "storeItemCollection");

            // Validate that connection is a SqlCeConnection.
            ValidateConnection(connection);

            // We don't support create/delete database operations inside a transaction as they can't be rolled back.
            if (InTransactionScope())
            {
                throw ADP1.DeleteDatabaseNotAllowedWithinTransaction();
            }

            // Throw an exception if connection is open.
            // We should not close the connection because user could have result sets/data readers associated with this connection.
            // Thus, it is users responsiblity to close the connection before calling delete database.
            if (DbInterception.Dispatch.Connection.GetState(connection, new DbInterceptionContext())
                == ConnectionState.Open)
            {
                throw ADP1.DeleteDatabaseWithOpenConnection();
            }

            if (_isLocalProvider)
            {
                CommonUtils.DeleteDatabase(DbInterception.Dispatch.Connection.GetDataSource(connection, new DbInterceptionContext()));
            }
            else
            {
                try
                {
                    Type rdpType;

                    // If we are working with RDP, then we will need to invoke the APIs through reflection.
                    var engine = RemoteProviderHelper.GetRemoteSqlCeEngine(
                        DbInterception.Dispatch.Connection.GetConnectionString(connection, new DbInterceptionContext()),
                        out rdpType);
                    Debug.Assert(engine != null);

                    // Invoke the required method on SqlCeEngine.
                    var mi = rdpType.GetMethod("DeleteDatabaseWithError", new[] { typeof(string), typeof(int?) });
                    Debug.Assert(mi != null);

                    // We will pass 'timeout' to RDP, this will be used as timeout period for connecting and executing on TDSServer.
                    mi.Invoke(engine,
                              new object[] { DbInterception.Dispatch.Connection.GetDataSource(connection, new DbInterceptionContext()), timeOut });
                }
                catch (Exception e)
                {
                    throw e.GetBaseException();
                }
            }
        }
        // <summary>
        // Constructs a SqlCeParameter
        // </summary>
        internal static DbParameter CreateSqlCeParameter(
            string name, TypeUsage type, object value, bool ignoreMaxLengthFacet, bool isLocalProvider)
        {
            var rdpSqlCeParameter = Type.GetType(RemoteProvider.SqlCeParameter);

            // No other parameter type is supported.
            //
            DebugCheck.NotNull(type);

            int? size;
            byte?precision;
            byte?scale;

            var result = isLocalProvider
                             ? new SqlCeParameter()
                             : (DbParameter)RemoteProviderHelper.CreateRemoteProviderType(RemoteProvider.SqlCeParameter);

            result.ParameterName = name;
            result.Value         = value;

            // .Direction
            // parameter.Direction - take the default. we don't support output parameters.
            result.Direction = ParameterDirection.Input;

            // .Size, .Precision, .Scale and .SqlDbType
            var sqlDbType = GetSqlDbType(type, out size, out precision, out scale);

            // Skip guessing the parameter type (only for strings & blobs) if parameter size is not available
            // Instead, let QP take proper guess at execution time with available details.
            //
            if ((null != size) ||
                (!TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String) &&
                 !TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Binary)))
            {
                if (isLocalProvider)
                {
                    var sqlCeParameter = (SqlCeParameter)result;
                    if (sqlCeParameter.SqlDbType != sqlDbType)
                    {
                        sqlCeParameter.SqlDbType = sqlDbType;
                    }
                }
                else
                {
                    // Remote Provider is loaded by reflection. As SqlDbType is not part of the base interface
                    // We need to access this using reflection only.
                    var rdpType = RemoteProviderHelper.GetRemoteProviderType(RemoteProvider.SqlCeParameter);
                    var rdpInfo = rdpType.GetProperty("SqlDbType");
                    rdpInfo.SetValue(result, sqlDbType, null);
                }
            }

            // Note that we overwrite 'facet' parameters where either the value is different or
            // there is an output parameter. This is because output parameters in SqlClient have their
            // facets clobbered if they are implicitly set (e.g. if the Precision was implicitly set
            // by setting the value)
            if (!ignoreMaxLengthFacet &&
                size.HasValue &&
                (result.Size != size.Value))
            {
                result.Size = size.Value;
            }

            if (precision.HasValue &&
                (((IDbDataParameter)result).Precision != precision.Value))
            {
                ((IDbDataParameter)result).Precision = precision.Value;
            }
            if (scale.HasValue &&
                (((IDbDataParameter)result).Scale != scale.Value))
            {
                ((IDbDataParameter)result).Scale = scale.Value;
            }

            // .IsNullable
            var isNullable = TypeSemantics.IsNullable(type);

            if (isNullable != result.IsNullable)
            {
                result.IsNullable = isNullable;
            }

            return(result);
        }
        private DbCommand CreateCommand(DbProviderManifest providerManifest, DbCommandTree commandTree)
        {
            // DEVNOTE/CAUTION: This method could be called either from Remote or Local Provider.
            // Ensure that the code works well with the both provider types.
            // The methods called from the below code also need to be capable
            // of handling both provider types.
            //
            // NOTE: Remote Provider is loaded at runtime, if available.
            // This is done to remove hard dependency on Remote Provider and
            // it might not be present in all scenarios. All Remote Provider
            // type checks need to be done using RemoteProviderHelper class.
            //

            Check.NotNull(providerManifest, "providerManifest");
            Check.NotNull(commandTree, "commandTree");

            if (commandTree is DbFunctionCommandTree)
            {
                throw ADP1.NotSupported(EntityRes.GetString(EntityRes.StoredProceduresNotSupported));
            }

            var command = _isLocalProvider
                              ? new SqlCeMultiCommand()
                              : (DbCommand)RemoteProviderHelper.CreateRemoteProviderType(RemoteProvider.SqlCeCommand);

            command.Connection = null; // don't hold on to the connection when we're going to cache this forever;

            List <DbParameter> parameters;
            CommandType        commandType;

            var commandTexts = SqlGenerator.GenerateSql(commandTree, out parameters, out commandType, _isLocalProvider);

            if (_isLocalProvider)
            {
                Debug.Assert(command is SqlCeMultiCommand, "SqlCeMultiCommand expected");
                // Set the multiple command texts for the command object
                ((SqlCeMultiCommand)command).CommandTexts = commandTexts;
            }
            else
            {
                // Set the command text for the RDP case.
                Debug.Assert(commandTexts.Length == 1, "BatchQueries are not supported in designer scenarios");
                command.CommandText = commandTexts[0];
            }

            command.CommandType = commandType;

            // Now make sure we populate the command's parameters from the CQT's parameters:
            //
            foreach (var queryParameter in commandTree.Parameters)
            {
                DbParameter parameter;
                const bool  ignoreMaxLengthFacet = false;
                parameter = CreateSqlCeParameter(
                    queryParameter.Key, queryParameter.Value, DBNull.Value, ignoreMaxLengthFacet, _isLocalProvider);
                command.Parameters.Add(parameter);
            }

            // Now add parameters added as part of SQL gen (note: this feature is only safe for DML SQL gen which
            // does not support user parameters, where there is no risk of name collision)
            //
            if (null != parameters &&
                0 < parameters.Count)
            {
                if (!(commandTree is DbDeleteCommandTree ||
                      commandTree is DbInsertCommandTree ||
                      commandTree is DbUpdateCommandTree))
                {
                    throw ADP1.InternalError(ADP1.InternalErrorCode.SqlGenParametersNotPermitted);
                }

                foreach (var parameter in parameters)
                {
                    command.Parameters.Add(parameter);
                }
            }

            return(command);
        }
        protected override void DbCreateDatabase(DbConnection connection, int?timeOut, StoreItemCollection storeItemCollection)
        {
            Check.NotNull(connection, "connection");
            Check.NotNull(storeItemCollection, "storeItemCollection");

            // Validate that connection is a SqlCeConnection.
            ValidateConnection(connection);

            // We don't support create/delete database operations inside a transaction as they can't be rolled back.
            if (InTransactionScope())
            {
                throw ADP1.CreateDatabaseNotAllowedWithinTransaction();
            }

            if (_isLocalProvider)
            {
                var engine = new SqlCeEngine(DbInterception.Dispatch.Connection.GetConnectionString(connection, new DbInterceptionContext()));
                engine.CreateDatabase();
                engine.Dispose();
            }
            else
            {
                try
                {
                    Type rdpType;

                    // If we are working with RDP, then we will need to invoke the APIs through reflection.
                    var engine = RemoteProviderHelper.GetRemoteSqlCeEngine(
                        DbInterception.Dispatch.Connection.GetConnectionString(connection, new DbInterceptionContext()),
                        out rdpType);
                    Debug.Assert(engine != null);

                    // Invoke the required method on SqlCeEngine.
                    var mi = rdpType.GetMethod("CreateDatabase", new[] { typeof(int?) });
                    Debug.Assert(mi != null);

                    // We will pass 'timeout' to RDP, this will be used as timeout period for connecting and executing on TDSServer.
                    mi.Invoke(engine, new object[] { timeOut });
                }
                catch (Exception e)
                {
                    throw e.GetBaseException();
                }
            }

            // Create the command object depending on provider.
            var command = connection.CreateCommand();

            // Create the command texts from StoreItemCollection.
            var commandTextCollection = SqlDdlBuilder.CreateObjectsScript(storeItemCollection, false);

            DbTransaction transaction = null;

            var interceptionContext = new DbInterceptionContext();

            try
            {
                // Open the connection.
                DbInterception.Dispatch.Connection.Open(connection, interceptionContext);

                // Open a transaction and attach to the command.
                transaction         = DbInterception.Dispatch.Connection.BeginTransaction(connection, new BeginTransactionInterceptionContext());
                command.Transaction = transaction;

                // Execute each statement.
                foreach (var text in commandTextCollection)
                {
                    command.CommandText = text;
                    DbInterception.Dispatch.Command.NonQuery(command, new DbCommandInterceptionContext());
                }

                // Commit the transaction.
                DbInterception.Dispatch.Transaction.Commit(transaction, interceptionContext);
            }
            catch (Exception e)
            {
                if (transaction != null)
                {
                    // Rollback the transaction.
                    DbInterception.Dispatch.Transaction.Rollback(transaction, interceptionContext);
                }

                // Throw IOE with SqlCeException embedded as inner exception.
                throw new InvalidOperationException(EntityRes.GetString(EntityRes.IncompleteDatabaseCreation), e);
            }
            finally
            {
                // Close connection and cleanup objects.
                if (command != null)
                {
                    command.Dispose();
                }
                if (transaction != null)
                {
                    DbInterception.Dispatch.Transaction.Dispose(transaction, interceptionContext);
                }
                if (connection != null)
                {
                    DbInterception.Dispatch.Connection.Close(connection, interceptionContext);
                }
            }
        }