Saving changes is not permitted en SQL Server

Trabajando en una instalación limpia de SQL Server Management Studio, si intentas modificar la estructura de una tabla, puedes encontrarte este error al intentarla guardar:

Saving changes is not permitted. The changes you have made require the following tables to be dropped and re-created. You have either made changes to a table that can't be re-created or enabled the option Prevent saving changes that require the table to be re-created.

Si oprimes Cancel, no se guarda ningún cambio.

La solución es muy sencilla, abre Microsoft SQL Server Management Studio y haz clic en el menú Tools, y luego en Options.

Después en la columna izquierda selecciona Designers y desmarca la casilla Prevent saving changes that require table re-creation.

Y listo, ahora podrás modificar tu tabla y guardar los cambios.

Cómo mantener la posición vertical de una página después de un PostBack

En ocasiones tenemos un control de ASP.NET (por ejemplo un DataGrid o un DataPager) que está posicionado verticalmente en la mitad de una página, lo que significa que el visitante tuvo que haber desplazado la barra de desplazamiento (scrollbar) hacia abajo para llegar a ese punto.

Si este control realiza un PostBack, cuando la página regresa al navegador, se muestra la parte superior de la misma, sacando al visitante de contexto ya que tendrí­a que volver a utilizar la barra de desplazamiento para visualizar el control. Esto no es nada amigable.

Para mantener la posición después del postback, utiliza el siguiente atributo en pages en web.config:

<pages maintainscrollpositiononpostback="true">

O bien, en la declaración de la página:

<%@ Page MaintainScrollPositionOnPostback="true" %>

User, group, or role already exists in the current database.

Seguramente te ha pasado que al migrar una base de datos de SQL Server de un servidor a otro, haces tu backup usando Microsoft SQL Server Management Studio, transfieres el archivo, y haces el restore en el nuevo servidor. Todo parece perfecto, pero cuando vas a crear tu usuario obtienes el siguiente error:

User, group, or role '' already exists in the current database. (Microsoft SQL Server, Error: 15023)

Entonces expandes tu base de datos, vas al nodo Security -> Users, buscas el usuario, y lo intentas borrar, pero entonces aparece otro error:

Drop failed for User ''. (Microsoft.SqlServer.Smo). The database principal owns a database role and cannot be dropped. (Microsoft SQL Server, Error: 15421)

En este punto te das cuenta de que el usuario está “huérfano” (orphaned), y una solución para darle la vuelta al problema es crear un nuevo usuario con otro nombre, asignarlo a la base de datos, y modificar el nombre del usuario en el Connection String de tu aplicación.

Pero existe una manera de solucionar las cosas correcta y fácilmente.

Abre una ventana de query en tu base de datos y teclea lo siguiente para ver los usuarios huérfanos:

EXEC sp_change_users_login 'Report'

Si ves a tu usuario ahí significa que efectivamente está huérfano y para deshuerfanizarlo 😉 teclea lo siguiente:

EXEC sp_change_users_login 'Auto_Fix', 'user'

Modificando user por el nombre de tu usuario.

Y listo, asunto totalmente solucionado, no tienes que crear nuevos usuarios ni modificar las Connection Strings de tus aplicaciones.

Por último, si quieres crear un nuevo usuario para esta base de datos, usa:

EXEC sp_change_users_login 'Auto_Fix', 'user', 'login', 'password'

Cómo agregar el salto de lí­nea a los caracteres válidos de un FilteredTextBoxExtender

En el Ajax Control Toolkit de ASP.NET, hay un control llamado FilteredTextBoxExtender que sirve para filtrar los caracteres que se pueden ingresar en un TextBox. Esto es útil, por ejemplo, para permitir únicamente el ingreso de números si estamos capturando un código postal mexicano.

Bueno, estaba usándolo en un TextBox con la propiedad TextMode establecida en MultiLine, lo que en HTML se despliega como un textArea, una caja de texto con múltiples lí­neas.

Esta es la definición de mi caja de texto y el control de filtrado:

 <asp:TextBox 
      ID="Tags" 
      runat="server" 
      TextMode="MultiLine" 
      Width="200px" 
      Height="150px">
 </asp:TextBox>
  
 <cc1:FilteredTextBoxExtender 
      ID="Tags_FilteredTextBoxExtender" 
      runat="server" 
      Enabled="True" 
      TargetControlID="Tags" 
      ValidChars="abcdefghijklmnopqrstuvwxyz1234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ">
 </cc1:FilteredTextBoxExtender> 

Como puedes ver, estoy permitiendo la entrada de letras mayúsculas, minúsculas, números y espacios. Pero como se trata de una caja de texto con míltiples lí­neas, obviamente quiero que se permita teclear saltos de lí­nea cuando el individuo presione la tecla enter o intro.

Este caso es truculento, porque no puedes hacerlo en la página aspx, lo tienes que hacer en el código de servidor, de esta forma:

Tags_FilteredTextBoxExtender.ValidChars = Tags_FilteredTextBoxExtender.ValidChars & vbCrLf

Como sabrás, vbCrLf significa un salto de lí­nea en Visual Basic, y lo que hace esa lí­nea es añadirlo a los caracteres válidos.

¡Medio truculento pero funciona!

Cómo pasar el nombre del usuario logueado como parámetro a un ObjectDataSource

Los ObjectDataSource soportan por default diferentes tipos de parámetros, como controles, cookies, Form, QueryString, Profile y Session. Sin embargo si quieres pasar como parámetro el UserName del usuario logueado no lo puedes hacer con ninguno de los tipos antes mencionados.

Para hacerlo debes utilizar el evento Selecting del ObjectDataSource.

Esto funciona así, aquí­ tenemos el ObjectDataSource:

  1. <asp:ObjectDataSource
  2.     ID="ODSEjemplo"
  3.     runat="server"
  4.     OldValuesParameterFormatString="original_{0}"
  5.     SelectMethod="GetData"
  6.     TypeName="BLLObtenerDatos">
  7. </asp:ObjectDataSource>

Fí­jate como no hemos incluido ningún parámetro. Éste lo generamos en el code-behind:

  1. Protected Sub ODSEjemplo_Selecting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ObjectDataSourceSelectingEventArgs) Handles ODSEjemplo.Selecting
  2.      e.InputParameters("MiParametro") = User.Identity.Name
  3. End Sub

Nótese que en el ejemplo, el método GetData recibe un sólo parámetro de tipo String, mismo que coincide con el tipo de User.Identity.Name

Así­ de sencillo.