Apologies for this being so long, but I have seen so many questions that include too little info... If someone sees my error(s) in the first few lines, I will be delighted...
I have an SQL Server 2008 R2 database, and can't get what I believe should be the correct behaviour when connecting through a C# SqlConnection.
I have two C# applications built with Visual C# 2010 Express:
One for data import/export/reporting and browsing
One for doing some complex processing
This is all on Windows 7 with all updates etc...
Some of the tables used by each of these two applications are shared common tables, and others need to be kept separated. Since I need to be able to transform and transfer data between the two sides, I want to keep all of this in one database.
To keep some degree of separation, I have created two schemas, two users, two roles and two logins, like:
CREATE LOGIN [Import_User] WITH PASSWORD=N'*****'
CREATE LOGIN [Engine_User] WITH PASSWORD=N'*****'
CREATE USER [Import_User] FOR LOGIN [Import_User] WITH DEFAULT_SCHEMA=[Import_Schema]
CREATE USER [Engine_User] FOR LOGIN [Engine_User] WITH DEFAULT_SCHEMA=[Engine_Schema]
CREATE ROLE [Import_Role] AUTHORIZATION [dbo]
CREATE ROLE [Engine_Role] AUTHORIZATION [dbo]
EXEC('CREATE SCHEMA [Import_Schema] AUTHORIZATION [Import_User]')
EXEC('CREATE SCHEMA [Engine_Schema] AUTHORIZATION [Engine_User]')
-- Import role permissions on the Import schema
GRANT EXECUTE, DELETE, INSERT, SELECT, UPDATE, REFERENCES ON SCHEMA::[Import_Schema] TO [Import_Role]
-- Engine_Role permissions on the engine schema
GRANT EXECUTE, DELETE, INSERT, SELECT, UPDATE, REFERENCES ON SCHEMA::[Engine_Schema] TO [Engine_Role]
EXEC sp_addrolemember N'Import_Role', N'Import_User'
EXEC sp_addrolemember N'Engine_Role', N'Engine_User'
GRANT CONNECT TO [Import_User]
GRANT CONNECT TO [Engine_User]
Then I create some tables and stored procs in each of the schemas, appropriate to each role. There are maybe 20 tables in the engine schema, 30 or so in the import schema. Some of these are very similar in the two schemas, but not quite the same, like:
CREATE TABLE [Engine_Schema].[Problem](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
[Description] [varchar](max) NULL,
CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED ( [ID] ASC )
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCK = ON) ON [PRIMARY])
ON [PRIMARY]
CREATE TABLE [Import_Schema].[Problem](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
[Client] [varchar](50) NOT NULL,
[Description] [varchar](max) NULL,
CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED ( [ID] ASC )
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCK = ON) ON [PRIMARY])
ON [PRIMARY]
This all seems to work fine when I check using those two logins through SSMS - each login sees exactly the tables and SPs that I expect. In each case, I can run queries and USPs inside SSMS without needing to use the schema prefix because these connections use the default schema that I have set up for each login / user. If I login as 'sa', then of course I can see everything in both schemas.
In my C# code, I connect to the database like this:
SqlConnection dbConnection = new SqlConnection(""server=laptop; database=test; user id=Engine_User; password=*****; Trusted_Connection=yes; connection timeout=30");
dbConnection.Open();
then I try to query the database tables directly like this:
using (SqlCommand cmdSelectProblems = new SqlCommand()) {
cmdSelectProblems.Connection = dbConnection;
cmdSelectProblems.CommandText = "Select ID, Name from Problem order by Name";
DataTable dataTableProblems = new DataTable();
using (SqlDataAdapter dataAdapterProblems = new SqlDataAdapter(cmdSelectProblems)) {
dataAdapterProblems.Fill(dataTableProblems);
...
or I can try to use one of my stored procedures like this:
using (SqlCommand cmd = new SqlCommand()) {
cmd.Connection = dbConnection;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "SelectProblems";
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
da.Fill(_problemsTable);
}
But when I try to use the connection through my C# code like these examples, I get errors like:
Invalid object name 'Problem'
or
Could not find stored procedure 'SelectProblems'
I seem to need to use the explicit schema prefix to get access to those same DB objects in the database. With the schema prefix explicitly included things all work from my C# code as far as I have tested them, so change the direct table query to:
cmdSelectProblems.CommandText = "Select ID, Name from [Engine_Schema].Problem order by Name";
or try to access the USPs with the schema prefix like:
cmd.CommandText = "[Engine_Schema].SelectProblems";
and then everything works fine.
Now I know that using explicit schema names is best practice, but I have a whole shedload of code both in C# and as stored procs that is written without using those schema prefixes. It will be really much simpler if I can make the C# SqlConnection queries respect the default schemas for the logins that I have defined and use. I believe that this should work fine the way I have set it up, but I guess I must have missed something somewhere.
I have wasted two days on this so far, and all that I have read suggests that this should all just work.
server=laptop; database=test; user id=Engine_User; password=*;
Trusted_Connection=yes; connection timeout=30
There's your problem - you're using Windows authentication instead of SQL authentication. Change you connection string to Trusted_Connection=no and everything should work.
Set the users default schema on the rmdbs
USE AdventureWorks;
ALTER USER Engine_User WITH DEFAULT_SCHEMA = Engine_Schema;
GO
Do the same for Import schema
Related
I am working on a C# MVC application. In this application user is uploading data from EXCEL spreadsheet and data is showing to grid.
After it has been showing to grid, user hit 'validate data' button. Application needs to perform UI (data length, empty field, data formats, etc.) validation and additionally SQL validations are also required for eg. record should not already exists already, any constraints, etc.
After validation data is displayed to user for any errors associated with each row, so that he\she can make corrections in pasted data and then save data as a transaction to SQL server database.
One way I am thinking to do this is loop the data in the C# code and perform the validations for each row by calling some stored procedure with return statements then store the same data probably in a dataset and then display to user in the grid. Then when he Submits, perform insert statements in a loop in a transaction.
The problem is that the approach which I am thinking about will double the number of database hits.
So if there are 100 rows in the grid, it will entail 200 database hits.
I am seeking advise if there is another efficient way to do this.
Here is my approach:
You can validate all your UI side validation on client side for example length etc. So that you don't need to travel to application as well as database server.
For data operation here is the approach which I have implemented many times.
Create a table type which must have all the columns which you need to process.
Use that table type variable into stored procedure as input parameter where you can pass n-number of rows in one go so that you do not need to loop at c# to hit database multiple times.
User merge statement inside the stored procedure if record is not matching you can insert it and if matching you can update it if needed. You can also do this action inside the transaction.
Hope this will help you.
EDIT 1: Based on your comment for database level validation.
There would be two types of error at database side.
1. Data itself won't be in the format as is expected by sql table definition like data type conversion failed.
DDL level error like data length exceeding or foreign key constrains etc.
I would recommend that do all possible validation of data you can code at c# level. Like length of data based on your destination column. Type of data before calling to stored procedure and you can filter such records at c# level. At this level you can do maximum validations.
Once you pass the data to sql server you can use try and catch in SQL server where you can implement logic of failed rows. Keep failed rows in temp table which you can return later and insert all successful.
EDIT 2: Here is the possible code.
CREATE TABLE Users
(
Idx BIGINT IDENTITY(1,1),
UserID UNIQUEIDENTIFIER,
FirstName VARCHAR(100),
LastName VARCHAR(100),
Email VARCHAR(100),
UserPassword VARCHAR(100),
InsertDate DATETIME,
UpDateDate DATETIME,
IsActive BIT,
CONSTRAINT [Users_PK] PRIMARY KEY CLUSTERED
(
[UserID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
CREATE TYPE UT_Users AS TABLE
(
Idx INT,
FirstName VARCHAR(100),
LastName VARCHAR(100),
Email VARCHAR(100),
UserPassword VARCHAR(100),
InsertDate DATETIME,
UpDateDate DATETIME,
IsActive BIT
)
GO
CREATE PROCEDURE uspInsertUsers(#user_Details [UT_Users]) READONLY
AS
BEGIN
DECLARE #Counter INT=1
DECLARE #TotalRows INT=0
SELECT #TotalRows = COUNT(1) FROM #user_Details
WHILE #TotalRows>#Counter
BEGIN
TRY
BEGIN
INSERT INTO dbo.Users
SELECT * FROM #user_Details WHERE #Counter = Idx
END
CATCH
BEGIN
--write code for catching the error as per your need. Store row in temp tables and return the temp table at the end
END
SET #Counter = #Counter+1
END
END
GO
DECLARE #user_Details AS [UT_Users];
INSERT #user_Details
SELECT 1,'Rahul','Neekhra','rahul#india.com','12345',GETDATE(),GETDATE(),1 UNION
SELECT 2,'James','Streak','streak#usa.com','12345',GETDATE(),GETDATE(),1
EXEC uspInsertUsers #user_Details
I wrongly assumed that if I deleted a table in the database then EF would re-create an empty table next time I ran update-database.
Now I have a missing table so the application throws an exception any time it hits a reference to it.
Is there a way to tell EF to remake the table without completely deleting the database?
The issue is EF has the table in it's snapshot with the last migration, so if you want EF to recreate it you could do this:
1) Temporarily remove the class by commenting out the DbSet and OnModelCreating (if exists).
2) Run a migration so EF removes the table from the snapshot. You will need to comment out the code in the Up() method that removes the table since it's already deleted.
3) Uncomment step 1 code.
4) Run another migration to add the table back.
https://msdn.microsoft.com/en-US/data/dn481501?f=255&MSPPError=-2147217396
Just comment the table property from DbContext And Update.After That Un Comment the property and Update This will create a new table without deleting the entire database (This works for me While updating through package manager console)
1) Create the database locally in your computer from scratch
2) with SSM open the database and select the desired table
3) right mouse click on the table and select "script Table as" and then "Create To" --> this will generate the table creation as Sql string
4) Excute the generate query with in the application ExecuteSqlCommand
Example from my PC:
string toBeCreatetable = #"
CREATE TABLE [dbo].[Customers](
[CustomerId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED
(
[CustomerId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]";
// Somewhere in code before at the begin do:
using (var ctx = new myDbContext(toBeCreatetable))
{
int executed= myDbContext.Database.ExecuteSqlCommand()
}
This is the best way which guarantee that the migration history will be properly working with the conceptual model.
I have a problem with ADO.NET query. I Create Database successfuly. There is only one table (RegUsers) in this database (I am just testing the work with ADO.NET). EDIT: Databese is based on Microsoft Azure
Create of a my table:
CREATE TABLE [dbo].[RegUsers] (
[Id] INT NOT NULL,
[Login] VARCHAR (50) NOT NULL,
[Password] VARCHAR (50) NOT NULL,
[Name] VARCHAR (50) NOT NULL,
[Surname] VARCHAR (50) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC));
I create SqlConnecion and successfully connect to the database (I hope that successfully if I can Open the connection) and then I'd like to INSERT data into this table with this:
SqlConnectionStringBuilder csBuilder;
csBuilder = new SqlConnectionStringBuilder();
csBuilder.DataSource = "********.database.windows.net";
csBuilder.Encrypt = true;
csBuilder.TrustServerCertificate = false;
csBuilder.UserID = "************".ToString();
csBuilder.Password = "********".ToString();
csBuilder.ConnectTimeout = 30
SqlConnection con = new SqlConnection(csBuilder.ToString());
con.Open();
string PlneniDaty =
#"INSERT INTO [dbo].[RegUsers] (Login,Password,Name,Surname)"+
#" VALUES ('MyLogin','MyPassword','Pavel','Novak')";
SqlCommand NaplDaty = new SqlCommand(PlneniDaty, con);
NaplDaty.ExecuteNonQuery();
con.Close();
Whenever I execute this command It display Error:
Invalid object name 'dbo.RegUsers'
(and yes the table was successfully created I can see it in SQL Server object Explorer)
Where is the problem?
Your entire code looks good.you can post your connectionString.
Following things you need to check.
Connection String ( Data Source Name , Database Name )
Schema of your table.
If I understand correctly, you're executing a insert query in a database dbo.RegUsers and it's giving the error Invalid object name 'dbo.RegUsers'? That simply means that table cannot find an object called "RegUsers ". There are several possible reasons for this:
The object doesn't exist, possibly because the schema and/or database don't exist
The object exists, but the database is case-sensitive and some part of the name doesn't match the name in your code
You'll need to investigate more to find out what the cause is in your case, but as a complete guess, your production server has both the RegUsers and databases?
Finally, when posting questions please always include your SQL Server version (2000/2005/2008) and edition (Express, Standard, Enterprise); they can be very important when talking about schemas and permissions, because features and behaviour can be different.
As far as I can see from your create script, your table's name is RegUzivatele, while you're trying to insert to a table name RegUsers, which of course doesn't exist.
I'm using MySqlBackup.dll (MySqlBackup.NET) which in turn uses MySql.Data.dll to dump the database. I thought MySqlBackup.NET was causing this behavior, so I took it out of the equation. If I run this code in my solution:
Dim cmd = New MySqlCommand()
cmd.Connection = New MySqlConnection(connectionString)
cmd.Connection.Open()
Dim result = QueryExpress.ExecuteScalarStr(cmd, "SHOW CREATE TABLE `airportcodes`;", 1)
cmd.Connection.Close()
I get
CREATE TABLE "airportcodes" (
"AirportCodeId" int(11) NOT NULL AUTO_INCREMENT,
"Code" varchar(4) CHARACTER SET utf8 NOT NULL,
"AirportName" varchar(100) CHARACTER SET utf8 DEFAULT NULL,
"Website" varchar(100) CHARACTER SET utf8 DEFAULT NULL,
"LastUpdate" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY ("AirportCodeId")
)
which I cannot use to restore because it uses double quotes. This happens when I use both the code above and MySqlBackup.NET. If I use the MySqlBackup.NET test application provided with its source code, the result is correct (uses backticks instead of double-quotes).
If I execute this query in the mysql CLI I also get the correct version (with backticks). I am using the same connection string all over.
It feels stupid to search-and-replace after the dump is created. What could be the cause of this?
This has nothing to do with MySqlBackup.NET
It is the behavior of MySql Server which can be configured either as default (GLOBAL) behavior or temporary (SESSION) behavior of MySQL server.
Here is the SQL statements that you can try out yourself:
Example 1: Export table structure with double quotes:
set session sql_mode=ANSI_QUOTES;
show create table `configkey`;
output:
CREATE TABLE "configkey" (
"key" varchar(100) NOT NULL,
"value" text,
PRIMARY KEY ("key")
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Example 2: Export table structure with single quote
set session sql_mode=traditional;
show create table `configkey`;
output:
CREATE TABLE `configkey` (
`key` varchar(100) NOT NULL,
`value` text,
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
For more information, please refer to official MySQL documentation under the topic: SQL-MODE
https://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_sql-mode
https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html
If you see the column names are wrapped with double quotes by default, it's most probably your MySQL server is configured to react in that way by default. You may consult your MySQL server administrator or provider.
Alternatively, you can configure the SQL_MODE each time manually before executing MySqlBackup.NET. Below is an example:
using (MySqlConnection conn = new MySqlConnection(constring))
{
using (MySqlCommand cmd = new MySqlCommand())
{
using (MySqlBackup mb = new MySqlBackup(cmd))
{
cmd.Connection = conn;
conn.Open();
cmd.CommandText = "set session sql_mode=traditional;";
cmd.ExecuteNonQuery();
mb.ExportToFile(file);
conn.Close();
}
}
}
I'm using C# Linq to interact with an MSSQL database. I have a problem where I sometime get the error message:
"Violation of PRIMARY KEY constraint 'PK_clustered_key_name'. Cannot
insert duplicate key in object 'dbo.table_name'. The statement has
been terminated."
When I try to add information to the database. When the error occurs I do an sql query with the values I try to add, in the MSSQL Management Studio and there doesn't seem to be an entry in the table.
So my question is if anyone has a clue about what the issue might be here. Some additional information follows.
The code I run (the error occurs at SaveChanges) is the following:
table_name table_name_instance = new table_name();
...
fill in values of fps
...
context.table_name.AddObject(fps);
context.SaveChanges();
I've also added a check if values of col1, col2, col3, col3 already exist in database so I definetly won't add duplicate entries. Since I'm not sure if the add is delayed and the check return that a duplicate entry already exist I've stored added values in a local container as well just to be sure, but I still get the error when calling SaveChanges().
The definition of my table with the clustered key for which the error is reported follows. Col1 through col4 is all Guid:s that are Foreign keys to other tables.
CREATE TABLE [dbo].[table_name](
[col1] [uniqueidentifier] NOT NULL,
[col2] [uniqueidentifier] NOT NULL,
[col3] [uniqueidentifier] NOT NULL,
[col4] [uniqueidentifier] NOT NULL
CONSTRAINT [PK_forbandsscenariokrav] PRIMARY KEY CLUSTERED
(
[col1] ASC,
[col2] ASC,
[col3] ASC,
[col4] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
I've seen many similar questions but haven't found an answer that seem to apply to my situation.
Additional info, I'm using MSSQL server 2008 R2 under Win 7 64-bit as well as:
Microsoft SQL Server Management Studio 10.50.1600.1
Microsoft Data Access Components (MDAC) 6.1.7601.17514
Microsoft MSXML 3.0 5.0 6.0
Microsoft Internet Explorer 8.0.7601.17514
Microsoft .NET Framework 2.0.50727.5448
Operating System 6.1.7601
Updated
When I instead use the Create function like this:
table_name table_name_instance = Createtable_name(col_val, col2_val,...);
context.table_name.AddObject(fps);
context.SaveChanges();
It seems to work just fine. Does that give any hint on why my first solution with new wasn't working even though I filled in every column value, not accepting null with a value?
See whether this SQL statement works on your SQL Server
INSERT INTO [dbo].[table_name]
([col1]
,[col2]
,[col3]
,[col4])
VALUES
(NEWID(),
NEWID(),
NEWID(),
NEWID())
GO
If it does then that means the Guid you are creating from your app are duplicating. Before inserting data in your database check whether the primary key you want to insert exists in the table. You have to do this for each column as every column is a PK.