Merge aspnetdb.mdf with my own database (automatically generated) - database

I've searched the internet thoroughly but couldn't find a clear answer to the problem. I have got the aspnet.db database. But i want to add my own tables and data to this database. If i try to connect to it with the connection string:
<add name ="ToernooiCompanionDBContext" connectionString ="Data Source= .\SQLEXPRESS; Integrated Security = SSPI; Trusted_Connection=True; Initial Catalog= aspnetdb" providerName ="System.Data.SqlClient"/>
A new database will be created (aspnetdb.mdf) in C:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA.
I want the database (which is automatically generated by codefirst) to merge with the existing one in my APP_DATA folder. What am I doing wrong?
I've tried adding AttachDbFilename=|DataDirectory|aspnetdb.mdf and User Instance=true to my connection string, or using the LocalSqlServer connection string which is defined in machine.config, but in all cases this overwrites the existing database. If I remove Initial Catalog=aspnetdb then I get an error that the initial catalog is needed.

I had the same problem but this link got me on the track to something that worked at least for me. I hope this helps someone at least! :)
Create a database
Add the aspnet tables to the new database
Fix the database connections in web.config so they point to the same database
Write some sql that removes all tables except the ones that start with "aspnet_"
Add the sql to the database initializer you write by your self
Add a call to the database initializer in Global.asax.cs
1. Create a database
I usually do this with SQL Server Management Studio. The database I used for this example code is SQL Server 2008R2 but I have done the same with SQL Server Express that you use.
2. Add the aspnet tables to the new database
I use the following tool which if you use it without any command line arguments works like a wizard.
%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regsql.exe
3. Fix the database connections so they point to the same database
The following two lines are from the test application I made. Notice that the name of the second connectionstring (MyHealthContext) is identical to the name of the DbContext I am using for my code first classes.
DbContext:
public class MyHealthContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<PersonAttribute> PeopleAttributes { get; set; }
}
Web.config
<add name="ApplicationServices" connectionString="Server=localhost\mssql2008r2;Database=MyHealth;Integrated Security=True;" providerName="System.Data.SqlClient"/>
<add name="MyHealthContext" connectionString="Server=localhost\mssql2008r2;Database=MyHealth;Integrated Security=True;" providerName="System.Data.SqlClient"/>
4. SQL that removes all but the aspnetdb-tables
DECLARE #cmdDropConstraints VARCHAR(4000)
DECLARE #cmdDropTables VARCHAR(4000)
-- ======================================================================
-- DROP ALL THE FOREIGN KEY CONSTRAINTS FROM THE TABLES WE WANT TO DROP
-- ======================================================================
DECLARE cursorDropConstraints CURSOR FOR
SELECT
'ALTER TABLE ['+ s.name + '].[' + t.name + '] DROP CONSTRAINT [' + f.name +']'
FROM
sys.foreign_keys f
INNER JOIN sys.tables t ON f.parent_object_id=t.object_id
INNER JOIN sys.schemas s ON t.schema_id=s.schema_id
WHERE
t.is_ms_shipped=0
AND t.name NOT LIKE 'aspnet_%'
AND t.name <> 'sysdiagrams'
OPEN cursorDropConstraints
WHILE 1=1
BEGIN
FETCH cursorDropConstraints INTO #cmdDropConstraints
IF ##fetch_status != 0 BREAK
EXEC(#cmdDropConstraints)
END
CLOSE cursorDropConstraints
DEALLOCATE cursorDropConstraints;
-- ======================================================================
-- DROP ALL THE RELEVANT TABLES SO THAT THEY CAN BE RECREATED
-- ======================================================================
DECLARE cursorDropTables CURSOR FOR
SELECT
'DROP TABLE [' + Table_Name + ']'
FROM
INFORMATION_SCHEMA.TABLES
WHERE
Table_Name NOT LIKE 'aspnet_%'
AND TABLE_TYPE <> 'VIEW'
AND TABLE_NAME <> 'sysdiagrams'
OPEN cursorDropTables
WHILE 1=1
BEGIN
FETCH cursorDropTables INTO #cmdDropTables
IF ##fetch_status != 0 BREAK
EXEC(#cmdDropTables)
END
CLOSE cursorDropTables
DEALLOCATE cursorDropTables;
5. Code for the database initializer:
Replace the "SQL CODE GOES HERE" below with the sql from step 4
public class MyHealthInitializerDropCreateTables : IDatabaseInitializer<MyHealthContext>
{
public void InitializeDatabase(MyHealthContext context)
{
bool dbExists;
using (new TransactionScope(TransactionScopeOption.Suppress))
{
dbExists = context.Database.Exists();
}
if (dbExists)
{
// Remove all tables which are specific to the MyHealthContext (not the aspnetdb tables)
context.Database.ExecuteSqlCommand(#"SQL CODE GOES HERE");
// Create all tables which are specific to the MyHealthContext (not the aspnetdb tables)
var dbCreationScript = ((IObjectContextAdapter)context).ObjectContext.CreateDatabaseScript();
context.Database.ExecuteSqlCommand(dbCreationScript);
Seed(context);
context.SaveChanges();
}
else
{
throw new ApplicationException("No database instance");
}
}
protected virtual void Seed(MyHealthContext context)
{
//TODO: Add code for seeding your database with some initial data...
}
}
6. Code that hooks in your new database initializer
To make sure that the custom database initializer isn't accidentily run in the production environment i added a #if DEBUG statement since I always compile my code in release mode before publishing.
protected void Application_Start()
{
//TODO: Comment out this database initializer(s) before going into production
#if DEBUG
Database.SetInitializer<MyHealthContext>(new MyHealthInitializerDropCreateTables()); // Create new tables in an existing database
#endif
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}

Open ASPNetDB db in sql server by attaching as new database
Make Creation scripts of tables / Stored proce3dures from ASPNetDB and run in your own database to create tables in your database
Open web.config of your application and attach application to your own database. Copy the name of connection string
Go to membership area and replace connectionstring name with copied one
Do above step with area as well

Related

SQL Server : get messages from referenced entities procedure in code

I'm running big dependency scan on legacy db and see that some objects have obsolete ref links, if you run this code in SSMS for View that points to not existing table like in my case, you will get your output on Results tab AND error info in Messages . Like in my case below.
I tried to check all env things I know and output of this stored procedure, but didn't see any indication.
How I can capture this event as I'm running this in looped dynamic SQL script and capture output in my table for further processing?
Updated:
it just text in Message box ,on error, you still have output on
Results tab
this is sp, it loop thru object list I took from sys.object and run this string as my sample to get all dependencies, load all into table. This call to
sql_reference_entities is the only way to get inter database
dependency on column level. So I need stick to this 100$>
--
Select *
From sys.dm_sql_referenced_entities('dbo.v_View_Obs_Table','Object')
--
----update------
This behavior was fixed in SQL Server 2014 SP3 and SQL Server 2016 SP2:
Starting from Microsoft SQL Server 2012, errors raised by
sys.dm_sql_referenced_entities (such as when an object has undergone a
schema change) cannot be caught in a TRY...CATCH Transact-SQL block.
While this behavior is expected in SQL Server 2012 and above, this
improvement introduces a new column that's called is_incomplete to the
Dynamic Management View (DMV).
KB4038418 - Update adds a new column to DMV sys.dm_sql_referenced_entities in SQL Server 2014 and 2016
----update-------
The tldr is that you can't capture these on the server side, and must use a client program in C#, PowerShell or some other client that can process info messages.
That DMV is doing something strange that I don't fully understand. It's generating errors (which a normal UDF is not allowed to do), and those errors do not trigger a TRY/CATCH block or set ##error. EG
create table tempdb.dbo.foo(id int)
go
create view dbo.v_View_Obs_Table
as
select * from tempdb.dbo.foo
go
drop table tempdb.dbo.foo
go
begin try
Select * From sys.dm_sql_referenced_entities('dbo.v_View_Obs_Table','Object')
end try
begin catch
select ERROR_MESSAGE(); --<-- not hit
end catch
However these are real errors, as you can see running this from client code:
using System;
using System.Data.SqlClient;
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
using (var con = new SqlConnection("Server=.;database=AdventureWorks;integrated security=true"))
{
con.Open();
con.FireInfoMessageEventOnUserErrors = true;
con.InfoMessage += (s, a) =>
{
Console.WriteLine($"{a.Message}");
foreach (SqlError e in a.Errors)
{
Console.WriteLine($"{e.Message} Number:{e.Number} Class:{e.Class} State:{e.State} at {e.Procedure}:{e.LineNumber}");
}
};
var cmd = con.CreateCommand();
cmd.CommandText = "Select * From sys.dm_sql_referenced_entities('dbo.v_View_Obs_Table','Object')";
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read() || (rdr.NextResult() && rdr.Read()))
{
Console.WriteLine(rdr[0]);
}
}
Console.ReadKey();
}
}
}
}
outputs
Invalid object name 'tempdb.dbo.foo'.
Invalid object name 'tempdb.dbo.foo'. Number:208 Class:16 State:3 at v_View_Obs_Table:4
0
The dependencies reported for entity "dbo.v_View_Obs_Table" might not include references to all columns. This is either because the entity references an object that does not exist or because of an error in one or more statements in the entity. Before rerunning the query, ensure that there are no errors in the entity and that all objects referenced by the entity exist.
The dependencies reported for entity "dbo.v_View_Obs_Table" might not include references to all columns. This is either because the entity references an object that does not exist or because of an error in one or more statements in the entity. Before rerunning the query, ensure that there are no errors in the entity and that all objects referenced by the entity exist. Number:2020 Class:16 State:1 at :1

Exec SP on Linked server and put that in temp table

Need some help on the below issue:
Case 1 : stored procedure is on server 1 - call is from server1
declare #tempCountry table (countryname char(50))
insert into #tempCountry
exec [database1_server1].[dbo].[getcountrylist]
Select * from #tempCountry
Result: successful execution
Case2 : iIf this same stored procedure is being called from a different server using linked server like this :
declare #tempCountry table (countryname char(50))
insert into #tempCountry
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
Select * from #tempCountry
Result
Msg 7391, level 16, state 2, line 2
The operation could not be performed because OLEDB provider "SQLNCLI" for linkedserver "Server2_Database2" was unable to begin a distributed transaction.
Case 3
But when tried to execute the stored procedure separately [without temp table insertion] like below
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
Result: that is executing the stored procedure without any error and returning data.
I forgot to mention that am using SQL Server 2005. As per the server administrator, the feature you've suggested that I use is not available in 2005.
You have (I believe) two options here:
To try to avoid the usage of MSDTC (and all these not pleasant things related to Distributed Transactions) by using OPENQUERY rowset function
/assume (here and below) that [database2_server2] is the name of the linked server/
declare #tempCountry table (countryname char(50))
insert into #tempCountry
select * from openquery([database2_server2], '[database1_server1].[dbo].[getcountrylist]')
select * from #tempCountry
OR
You can set the linked server's option Enable Promotion Of Distributed Transaction to False in order to prevent the local transaction to promote the distributed transaction and therefore use of MSDTC:
EXEC master.dbo.sp_serveroption
#server = N'database2_server2',
#optname = N'remote proc transaction promotion',
#optvalue = N'false'
and your original query should work fine:
declare #tempCountry table (countryname char(50))
insert into #tempCountry
exec [database2_server2].[database1_server1].[dbo].[getcountrylist]
select * from #tempCountry
It is possible to avoid Linked Servers altogether. You can create a SQLCLR stored procedure that makes a standard connection to the remote instance (i.e. Database1).
The C# code below is for a SQLCLR Stored Procedure that:
allows for an optional database name. If empty the current database will be the default database, or if provided it will change to that database after connecting (so that the current database can be different than the default database)
allows for optionally using Impersonation. Without impersonation (the default behavior) the connections are made by the Windows Login that the SQL Server Service is running under (i.e. the "Log On As" account in "Services"). This might not be desired as it does typically provide an elevated level of permissions than the caller usually has. Using Impersonation will maintain the security context of the Login executing the stored procedure, if that Login is associated with a Windows Login. A SQL Server Login does not have a security context and will hence get an error if attempting to use Impersonation.
The ability to toggle Impersonation on and off in the code provided here is for testing purposes so it is easier to see the differences between using Impersonation and not using it. When using this code in a real project, there usually would not be a reason to allow the end-user (i.e. the caller) to change the setting. It is generally safer to use Impersonation. But, the main difficulty in using Impersonation is that it is restricted to the local machine, unless the Windows Login is enabled for Delegation in Active Directory.
should be created on the instance that will be calling Server1: Server2 in Database2
requires a PERMISSION_SET of EXTERNAL_ACCESS. This is best handled by:
signing the Assembly in Visual Studio
in [master], create an Asymmetric Key from the DLL
in [master], create a Login from this new Asymmetric Key
GRANT the EXTERNAL ACCESS ASSEMBLY permission to the new Key-based Login
in [Database2], execute the following:
ALTER ASSEMBLY [NoLinkedServer] WITH PERMISSION_SET = EXTERNAL_ACCESS;
should be executed as:
EXEC dbo.RemoteExec N'Server1', N'Database1', 0;
and:
EXEC dbo.RemoteExec N'Server1', N'Database1', 1;
After each execution, run the following and pay attention to those first two fields:
SELECT [login_name], [original_login_name], *
FROM sys.dm_exec_sessions
WHERE LEFT([program_name], 14) = N'Linked Server?';
The C# code:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Security.Principal;
using Microsoft.SqlServer.Server;
public class LinkedServersSuck
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void RemoteExec(
[SqlFacet(MaxSize = 128)] SqlString RemoteInstance,
[SqlFacet(MaxSize = 128)] SqlString RemoteDatabase,
SqlBoolean UseImpersonation)
{
if (RemoteInstance.IsNull)
{
return;
}
SqlConnectionStringBuilder _ConnectionString =
new SqlConnectionStringBuilder();
_ConnectionString.DataSource = RemoteInstance.Value;
_ConnectionString.Enlist = false;
_ConnectionString.IntegratedSecurity = true;
_ConnectionString.ApplicationName =
"Linked Server? We don't need no stinkin' Linked Server!";
SqlConnection _Connection =
new SqlConnection(_ConnectionString.ConnectionString);
SqlCommand _Command = new SqlCommand();
_Command.CommandType = CommandType.StoredProcedure;
_Command.Connection = _Connection;
_Command.CommandText = #"[dbo].[getcountrylist]";
SqlDataReader _Reader = null;
WindowsImpersonationContext _SecurityContext = null;
try
{
if (UseImpersonation.IsTrue)
{
_SecurityContext = SqlContext.WindowsIdentity.Impersonate();
}
_Connection.Open();
if (_SecurityContext != null)
{
_SecurityContext.Undo();
}
if (!RemoteDatabase.IsNull && RemoteDatabase.Value != String.Empty)
{
// do this here rather than in the Connection String
// to reduce Connection Pool Fragmentation
_Connection.ChangeDatabase(RemoteDatabase.Value);
}
_Reader = _Command.ExecuteReader();
SqlContext.Pipe.Send(_Reader);
}
catch
{
throw;
}
finally
{
if (_Reader != null && !_Reader.IsClosed)
{
_Reader.Close();
}
if (_Connection != null && _Connection.State != ConnectionState.Closed)
{
_Connection.Close();
}
}
return;
}
}

Bug with multiple TADOConnections

I already figured out a display table bug using ADO and SQL Server and different connections to one database. ( see details at BUG #1 )
Another issues come while trying to delete records
The code goes like this
function resettable (tablename, databasename, servername) : Boolean;
var
aADOQuery : TADOQuery;
aADOConnection : TAdoConnection,
begin
/// Create ADO stuff
aADOQuery := TADOQuery.Create;
aADOConnection := TAdoConnection.Create;
/// connect to DB & Table
....
///
aADOQuery.sql.add('delete * from ' + Tablename;
aADOQuery.execsql;
/// free objects after use
....
end;
This code works fine when the ADOConnection is alone on the database. If there has been some activity by any other ADO connection and some modification done, the code fails while the "database is not updated error message"
How to create a solution which will do an update prior to the delete record statement ???

Set database collation in Entity Framework Code-First Initializer

I want to set the default collation for a database, when Entity Framework Code First creates it.
I've tried the following:
public class TestInitializer<T> : DropCreateDatabaseAlways<T> where T: DbContext
{
protected override void Seed(T context)
{
context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] COLLATE Latin1_General_CI_AS");
context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] SET MULTI_USER");
}
}
This appears to run OK when SQL Server is already set to the same default collation Latin1_General_CI_AS.
But if I specify a different collation, say SQL_Latin1_General_CP1_CI_AS this fails with the error,
System.Data.SqlClient.SqlException: Resetting the connection results in a different
state than the initial login. The login fails.
Can anyone advise how I can set the collation please?
Solution with a command interceptor
It is definitely possible, though it's a bit of a hack. You can alter the CREATE DATABASE command with a command interceptor. Il will intercept all the commands sent to the database, recognize the database creation command based on a regex expression, and alter the command text with your collation.
Before database creation
DbInterception.Add(new CreateDatabaseCollationInterceptor("SQL_Romanian_Cp1250_CI_AS_KI_WI"));
The interceptor
public class CreateDatabaseCollationInterceptor : IDbCommandInterceptor
{
private readonly string _collation;
public CreateDatabaseCollationInterceptor(string collation)
{
_collation = collation;
}
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { }
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
// Works for SQL Server
if (Regex.IsMatch(command.CommandText, #"^create database \[.*]$"))
{
command.CommandText += " COLLATE " + _collation;
}
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { }
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { }
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
}
Remarks
Since the database is created with the right collation from the start, all the columns will automatically inherit that collation and you wan't have to ALTER them afterwards.
Be aware that it will impact any later database creation occurring inside the application domain. So you might want to remove the interceptor after the database is created.
I was able to change collation with a custom migration (EF6). I have automatic migrations enabled. You need to delete your DB first.
Create the migration code by typing Add-Migration [YourCustomMigration] in Package Manager Console. (Code First Migrations)
First step should create your migration class with current model creation code in the Up() override. Add your ALTER DATABASE code BEFORE the table creation codes so they are created using the database collation you want. Also, note the suppressTransaction flag:
public override void Up()
{
Sql("ALTER DATABASE [YourDB] COLLATE [YourCollation]", suppressTransaction: true);
[...Your DB Objects Creation codes here...]
}
Each update-database command issued from then on creates a new migration class. All migration codes are executed in order.
My solution with EFCore 2.1 was to derive from the SqlServerMigrationsSqlGenerator and override Generate(SqlServerCreateDatabaseOperation, IModel, MigrationCommandListBuilder)
internal class CustomSqlServerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
internal const string DatabaseCollationName = "SQL_Latin1_General_CP1_CI_AI";
public CustomSqlServerMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies,
IMigrationsAnnotationProvider migrationsAnnotations)
: base(dependencies, migrationsAnnotations)
{
}
protected override void Generate(
SqlServerCreateDatabaseOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
base.Generate(operation, model, builder);
if (DatabaseCollationName != null)
{
builder
.Append("ALTER DATABASE ")
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
.Append(" COLLATE ")
.Append(DatabaseCollationName)
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator)
.EndCommand(suppressTransaction: true);
}
}
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.ReplaceService<IMigrationsSqlGenerator, CustomSqlServerMigrationsSqlGenerator>();
}
then used it in the DbContext by replacing the IMigrationsSqlGenerator service
public class MyDbContext : DbContext
{
//...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.ReplaceService<IMigrationsSqlGenerator, CustomSqlServerMigrationsSqlGenerator>();
}
//...
}
I have had the same problem a while ago. Possible solutions:
It appears that EF creates the database using the server default
collation so one thing you could do is change that.
You cannot change the database collation within the Seed() method
but you can change the collation of individual columns for a table
(NOTE: there is no such thing as table collation, it does relate to
column in a table). You will have to change each column's collation
separately.
If you are using migrations, you could alter the table column
collations within your Up() method.
As you are using the Seed() method, I would suggest the following (modify as appropriate) within the Seed() method:
context.Database.ExecuteSqlCommand(
#"ALTER TABLE MyTable ALTER COLUMN MyColumn NVARCHAR(max) COLLATE MyCollation NOT NULL");
Hope that helps.
I would like to explain why you should not use the seed method for this. If you change your database collation after any columns have been added there is a large risk for collation conflicts like below
Cannot resolve the collation conflict between
"SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_100_CI_AS" in the
equal to operation.
This is due to the fact that if you alter your database with ALTER DATABASE [YourDb] COLLATE [YourCollation] you will only change the databases collation and not previously created columns.
Example in T-SQL:
DECLARE #DBName nvarchar(50), #SQLString nvarchar(200)
SET #DBName = db_name();
SET #SQLString = 'ALTER DATABASE [' + #DBName + '] COLLATE Latin1_General_100_CI_AS'
EXEC(#SQLString)
/* Find Collation of SQL Server Database */
SELECT DATABASEPROPERTYEX(#DBName, 'Collation')
/* Find Collation of SQL Server Database Table Column */
SELECT name, collation_name
FROM sys.columns
WHERE OBJECT_ID IN (SELECT OBJECT_ID
FROM sys.objects
WHERE type = 'U'
AND name = 'AspNetUsers')
AND name = 'FirstName'
Due to this you need to change database collation before any columns are added or change every column separately. Possible solutions:
#MathieuRenda https://stackoverflow.com/a/42576705/3850405
I would put the DbInterception.Add in a class deriving from DbConfiguration or in Application_Start in Global.asax as recommended in the documentation. Note: Wherever you put this code, be careful not to execute DbInterception.Add for the same interceptor more than once, or you'll get additional interceptor instances.
public class ApplicationDbConfiguration: DbConfiguration
{
public ApplicationDbConfiguration()
{
DbInterception.Add(new CreateDatabaseCollationInterceptor("Latin1_General_100_CI_AS"));
}
}
I would also not inherit from the interface but instead use the implementation of DbCommandInterceptor as Microsoft does in their examples.
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;
using System.Text.RegularExpressions;
namespace Application.Repositories.EntityFramework
{
public class CreateDatabaseCollationInterceptor : DbCommandInterceptor
{
private readonly string _collation;
public CreateDatabaseCollationInterceptor(string collation)
{
_collation = collation;
}
public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
// Works for SQL Server
if (Regex.IsMatch(command.CommandText, #"^create database \[.*]$"))
{
command.CommandText += " COLLATE " + _collation;
}
}
}
}
More information here: https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/connection-resiliency-and-command-interception-with-the-entity-framework-in-an-asp-net-mvc-application
#steliosalex: https://stackoverflow.com/a/22895703/3850405. Note that changing every column might not be enough either. You also need to handle metadata and parameters for stored procedure and similar get the collation that the database had when these where created. Changing collation completely requires a create database command with the right collation.
#RahmiAksu https://stackoverflow.com/a/31119371/3850405 NOTE: This is not a good solution in my opinion but if you use it edit the very first migration. Can't be used if the database is already in production. If you have a seed method the exception Resetting the connection results in a different state than the initial login will be thrown.
Your Seed SqlException can be solved by using a plain ADO.Net connection, so the context's connection won't be reset. However as mentioned above this will probably cause a lot of errors later.
using (var conn = new SqlConnection(context.Database.Connection.ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText =
string.Format("ALTER DATABASE [{0}] COLLATE Latin1_General_100_CI_AS",
context.Database.Connection.Database));
conn.Open();
cmd.ExecuteNonQuery();
}
}
SqlException: Resetting the connection results in a different state
than the initial login. The login fails. Login failed for user ''.
Cannot continue the execution because the session is in the kill
state.
Source:
https://stackoverflow.com/a/50400609/3850405
It's simply not possible using current versions of EF (EF6). However, at least EF6+ can now work with already existent database. We've changed our deployment scenario such that the database is already created by our deployment script (incl. the default collation) and let EF6 work with the existing database (using the correct default collation).
If you absolutely have to create the database inside your code and cannot use anything else than EF (e.g. you are not able to create the database using ADO.NET) then you have to go for seliosalex answer. It's the only solution we came up, however, see my comment, it is a lot of work to do it right.
EF 5 now supports creating missing tables in an existing database with Code First, so you can create an empty database and set the collation correct, before running an CF on it.
1):
public class DataBaseContext : System.Data.Entity.DbContext
{
public DataBaseContext() : base("MyDB") { }
static DataBaseContext()
{
System.Data.Entity.Database.SetInitializer(new MyInitializer());
}
....
}
2):
public class MyInitializer : DropCreateDatabaseIfModelChanges<DataBaseContext>
{
public MyInitializer() { }
protected override void Seed(DataBaseContext context)
{
base.Seed(context);
using (var conn = new SqlConnection(context.Database.Connection.ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText =
string.Format("ALTER DATABASE [{0}] COLLATE Persian_100_CI_AS",
context.Database.Connection.Database);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
context.Database.Connection.Close();
}
}
}

EntLib and SQLCLR?

I have quite a few SQLCLR functions and sprocs in my MSSQL 2008 DB. Debugging and logging is always a problem. I have used Microsoft Enterprise Library Logging Application Block a lot in straight C# apps, and was wondering if it was (1) crazy or (2) impossible to layer that into SQLCLR stuff. I really like using a config file to define a rolling text log, Event Log, and SMTP output for different events, so if there is another way to do so, I'm all ears...
Thanks.
It seems like it is possible. I don't know if it's advisable.
One alternative would be defining a trace listener in code, which of course can read the configuration from the database. Another would simple be logging messages to a SQL table and using triggers to enable notifications.
I also do have to question whether the real mistake is that you have CLR stored procs in your database that have so much business logic that they require logging. I'm a big fan of business logic in my database, but I'm wary of CLR stored procs.
If you wanted to use the enterprise application blocks, the config file to edit woulds be C:\Program Files\Microsoft SQL Server\MSSQL10.INSTANCE_NAME\MSSQL\Binn\sqlservr.config. However, sql server does not seem to write values to this file when you restart it. I arrived at this conclusion with the following CLF PROC and UDFs:
using System;
using System.Configuration;
using Microsoft.SqlServer.Server;
namespace LoggedClr
{
public static class AppDomainInfo
{
[SqlFunction]
public static string GetConfigFileName()
{
return AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
}
[SqlFunction]
public static string GetAppConfigValue(string key)
{
return ConfigurationManager.AppSettings[key];
}
[SqlProcedure]
public static void SetAppConfigValue(string key, string value)
{
ConfigurationManager.AppSettings[key] = value;
}
}
}
Which I loaded and ran using the following T-SQL:
CREATE DATABASE TestAssembly
GO
USE TestAssembly
GO
ALTER DATABASE TestAssembly SET TRUSTWORTHY ON;
GO
ALTER AUTHORIZATION ON DATABASE::TestAssembly TO test
GO
DROP ASSEMBLY LoggedClr
GO
CREATE ASSEMBLY LoggedClr
from 'C:\justin''s projects\TestClr\LoggedClr\LoggedClr\bin\Debug\LoggedClr.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS
GO
CREATE FUNCTION GetConfigFileName () RETURNS NVARCHAR(MAX) AS
EXTERNAL NAME LoggedClr.[LoggedClr.AppDomainInfo].GetConfigFileName
GO
CREATE FUNCTION GetAppConfigValue (#key nvarchar(max)) RETURNS nvarchar(max) AS
EXTERNAL NAME LoggedClr.[LoggedClr.AppDomainInfo].GetAppConfigValue
GO
CREATE PROCEDURE SetAppConfigValue (#key nvarchar(max), #value nvarchar(max)) AS
EXTERNAL NAME LoggedClr.[LoggedClr.AppDomainInfo].SetAppConfigValue
GO
SELECT dbo.GetConfigFileName()
EXEC dbo.SetAppConfigValue 'justin', 'is a developer'
SELECT dbo.GetAppConfigValue('justin')
This gave me the following results:
------------------------------------------------------------------------------
C:\Program Files\Microsoft SQL Server\MSSQL10.INSTANCE_NAME\MSSQL\Binn\sqlservr.config
(1 row(s) affected)
-------------------------------------------------------------------------
is a developer
(1 row(s) affected)

Resources