Using Entity Framework 4.3.1 CodeFirst and having no luck getting the migrations or scripts to respect the schema that I want my tables to end up in.
It seems the default behavior (the one that I'm seeing regardless of what I do) is to omit the schema completely from the SQL that actually runs causes tables to be created in the default schema for the user running the migration or script.
My DBAs are telling me that they cannot change my default schema due to the fact that I'm part of an AD group and not a local user, so changing the default schema for the user running (an often recommended workaround) the script is not an option at all.
I've tried using the annotations like this:
[Table("MyTable", Schema = "dbo")]
public class MyTable
{
public int Id { get; set; }
public string MyProp1 { get; set; }
public string MyProp2 { get; set; }
}
And I've also tried using the fluent variant of the same thing:
modelBuilder.Entity<YourType>().ToTable("MyTable", "dbo");
The resultant script (and migrations) ignore the fact that I tried to specify a schema. The script looks like this:
CREATE TABLE [MyTable] (
[Id] [int] NOT NULL IDENTITY,
[MyProp1] [nvarchar](max),
[MyProp2] [nvarchar](max),
CONSTRAINT [PK_MyTable] PRIMARY KEY ([Id])
)
When there should be a [dbo] tucked in there like this:
CREATE TABLE [dbo].[MyTable] (
[Id] [int] NOT NULL IDENTITY,
[MyProp1] [nvarchar](max),
[MyProp2] [nvarchar](max),
CONSTRAINT [PK_MyTable] PRIMARY KEY ([Id])
)
Has anyone else had luck in getting Entity Framework to respect the schema? This behavior pretty much kills our ability to use codefirst at all in our enterprise environment.
Reminder: Changing my user to have a different default schema is not an option at all.
As my comment seems to have answered the quandary, I am recreating it as an answer.
It seems that because the SQL Server provider uses "dbo" as the default schema, it will not explicitly add "dbo" to the TSQL that creates the tables even if you specify that in your configuration.
This answers the basic problem. But now I am curious if dbo is the default, do you (Bob) still have a reason to specify it? It doesn't hurt to have it in the configuration if you just want the default to be obvious to someone reading the code. But is the behavior creating another side-effect?
ADDED: Aha! FIXED IN EF5! :) (I just updated my test project to use EF5RC (against .NET 4.0) and I got "dbo" explicitly in the TSQL for create table.)
I tried all of the stuff that Bob Bland tried with a similar lack of success (I was also using Entity Framework 4.3.1 CodeFirst). Then I changed the generated migration to look like this and it worked. Maybe this will save somebody a few minutes of pain?
So my solution was to generate the migration as normal, then hack it by hand to include dbo. as shown below.
public override void Up()
{
CreateTable(
"dbo.UploadedFiles", // See? I have added dbo. to the front of my table name :-)
c => new
{
UploadedFileId = c.Guid(nullable: false, identity: true),
// other columns omitted for brevity...
FileData = c.Binary(),
})
.PrimaryKey(t => t.UploadedFileId);
}
and the Down bit looks like this
public override void Down()
{
DropTable("dbo.UploadedFiles"); // See? I have added dbo. to the front of my table name here too :-)
}
Related
I have recently been using PostgreSQL rather than SQL, so finding a lot of little nuances between the two.
I want to be able to make a string value unique in a table, so using the EF code first fluent API, I have this code.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyTable>()
.HasIndex(u => u.UniqueValue)
.IsUnique();
base.OnModelCreating(modelBuilder);
}
When creating a migration, it will generate this.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_MyTable_UniqueValue",
table: "MyTable",
column: "UniqueValue",
unique: true);
}
This will then add the index to the PostgreSQL table, and work when the word is of the same case.
e.g. Try and insert "Hello" twice, and it will not work.
It does allow for variations of the word though, so I can insert "Hello", "HEllo", "HELlo", etc...
It looks like it is possible to force the case on the index in PostgreSQL using something like
CREATE UNIQUE INDEX UniqueValue ON MyTable (UPPER(UniqueValue));
However I am not sure how to do this via EF Fluent API and create the migration from that?
It seems like for now you'll have to set up a raw SQL migration. Support for that still hasn't been added (yet). You could also set up a generated (computed) column that holds the result of upper(UniqueValue), then add a unique index on that.
There's no way to add an expression-based unique constraint or to add the expression to an existing unique index key. You can build a new index using your definition as is:
CREATE UNIQUE INDEX UniqueValue ON MyTable (UPPER(UniqueValue));
Or add an exclusion constraint which ends up doing pretty much the same (as of now, quite far from being supported in EF):
create table test(txt text);
alter table test
add constraint case_insensitive_unique
exclude using gist(upper(txt) with =);
insert into test(txt) select 'hello';
--INSERT 0 1
insert into test(txt) select 'Hello';
--ERROR: conflicting key value violates exclusion constraint "case_insensitive_unique"
--DETAIL: Key (upper(txt))=(HELLO) conflicts with existing key (upper(txt))=(HELLO).
Demo
We've started using EF6 as part of rewriting our application suite. There are many perfectly reasonable tables in the existing suite and we're reusing them using a database-first approach. My problem is that EF6 seems to be enforcing what I think are code-first conventions on my database-first model.
Consider this minimal example with two tables defined thusly and appropriately populated with a few rows:
CREATE TABLE [dbo].[Table1] (
[Id] INT NOT NULL PRIMARY KEY,
[Table2Reference] INT NOT NULL REFERENCES [dbo].[Table2](Id) )
CREATE TABLE [dbo].[Table2] (
[Id] INT NOT NULL PRIMARY KEY,
[SomeColumn] NVARCHAR(25) )
After running Update Model From Database we get this model:
(Oops. Not enough reputation to post images. It's what you would imagine.)
So far so good, but when you write code to access the Table1 entity, like so...
var q = _context.Table1.ToList();
foreach (var item in q)
Debug.WriteLine("{0}", item.Table2Reference);
... it compiles fine but will throw on the ToList() line. This is because the SQL generated contains a request for a column that doesn't even exist:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Table2Reference] AS [Table2Reference],
[Extent1].[Table2_Id] AS [Table2_Id] <-- this one doesn't exist
FROM [dbo].[Table1] AS [Extent1]
I gather this has something to do with a code-first naming convention for foreign keys. I know I can rename Table2's Id column to Table2Id and rename Table2Reference to Table2Id and it will work. However, this is supposed to be database-first. Is there some way to tell EF to get out of the way and just go with what is actually in the pre-defined database? I did discover early on that I had to turn off the name pluralizing convention, but I can't seem to identify a convention to turn off that fixes this problem. I tried removing these:
modelBuilder.Conventions.Remove<PrimaryKeyNameForeignKeyDiscoveryConvention>();
modelBuilder.Conventions.Remove<TypeNameForeignKeyDiscoveryConvention>();
modelBuilder.Conventions.Remove<NavigationPropertyNameForeignKeyDiscoveryConvention>();
Anyway, I'm stumped. Is there an easy workaround that doesn't involve modifying the existing database?
Thanks for reading.
You can use data annotations attributes or fluent API to configure EF mapping to actual database tables. Here is how it can be done with attributes:
[Table("Table1")]
public class Table1
{
public int Id { get; set; }
public int Table2Reference { get; set; }
[ForeignKey("Table2Reference")]
public Table2 Table2 { get; set; }
}
It turns out that there is a very important piece to a database-first approach besides having an EDMX file. That is, your connection string must contain the following section:
metadata=res:///IPE.csdl|res:///IPE.ssdl|res://*/IPE.msl; (replacing IPE with the base name of your EDMX)
Otherwise, EF will be unable to locate the EDMX information in the assembly and code-first conventions can come into play. Mostly things just work, until they don't.
Somehow either, my C# code, or entity framework 6 is dropping and creating a database when I run
PM> update-database -verbose
from an explicit migration with an up() method that looks like this...
public override void Up()
{
AddColumn("dbo.Term", "Term_Id", c => c.Int());
CreateIndex("dbo.Term", "Term_Id");
AddForeignKey("dbo.Term", "Term_Id", "dbo.Term", "Id");
}
-verbose generates this...
Origin: Configuration).
Applying explicit migrations: [201410271927053_addTermsTable2].
Applying explicit migration: 201410271927053_addTermsTable2.
ALTER TABLE [dbo].[Term] ADD [Term_Id] [int]
CREATE INDEX [IX_Term_Id] ON [dbo].[Term]([Term_Id])
ALTER TABLE [dbo].[Term] ADD CONSTRAINT [FK_dbo.Term_dbo.Term_Term_Id] FOREIGN KEY ([Term_Id]) REFERENCES [dbo].[Term] ([Id])
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
(plus the migration insert statement)
None of which indicates (to me) the db is about to be dropped.
Can someone confirm this does not drop?
Does it matter that
this is an Azure SQL database?
that the columns were added in response to adding this.. to the class?
public virtual ICollection<Term> urls { get; set; }
Any pointers on how to make sure I know when a drop create is going to occur?
Thanks.
In our project we have necessity of adding some predefined data to DB. I think the best way and concept is using for that EF Migrations (not Seed method).
But we have a big troubles with adding related data to DB:
For Example:
Suppose we have 2 tables:
Users:
Id (PK auto increment)
Name
RoleId
Roles:
Id (PK auto increment)
Name
Let's suppose that we need to add User(Name = 'John', RoleId = (Id of role that name is 'Admin')).
How can we do it? It would be great if we find a solution that allows us to execute pure SQL SELECT script which not uses Entities of Code First because they can be modified or removed.
For DELETE, INSERT, UPDATE can be used Sql(...) method but what about SELECT?
You cannot have a context into the migration.
Logically first are ran the migrations to Update the DB Schema, then you can have a context to work with the data via it. If your DB does not match the model, or even the table is still not there, you cannot use it in EF.
I had to look into the EF code (and also because was curious). Practically the Sql() method in the DbMigration class in several levels below just adds the SQL string into a list of queries that should be executed into the transaction and moves on. It does not executes it when it is called. So in short EF just fills in a list of codes lines that should be executed in the end at once. And it seems correct if you try to walk in all paths of what you can do with the C# code in the migration code.
The question is quite good actually, unfortunately still I didn't found any better solution rather than using pure ADO.
Another option is to generate more custom SQL queries, and use T-SQL more widely.
For your case as you want to insert the user and set the groupId looking by the name, it can be used with inner select:
INSERT INTO Users (Name, GroupId)
VALUES ('John', RoleId = (SELECT Id FROM Roles WHERE Name = 'Admin')).
For my issue, I had to a bit do more sophisticated execution - the following does the same as the AddOrUpdate method of the DbSet, using the IF statement:
IF EXISTS (SELECT * FROM Table1 WHERE Column1='SomeValue')
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
ELSE
INSERT INTO Table1 VALUES (...)
I found it here: http://blogs.msdn.com/b/miah/archive/2008/02/17/sql-if-exists-update-else-insert.aspx
I'm using good old LINQ for this:
public override void Up()
{
using (var dc = new DbContext("your connection string or name"))
{
var ids = dc.Database.SqlQuery<int>("SELECT id FROM sometable WHERE somefield={0}", 42).ToArray();
...
}
}
Using LINQ is better, even for usual migrations, because, there is a bug in DbMigration.Sql method, it ignores arguments: How to pass parameters to DbMigration.Sql() Method
Is there a way to have entity framework use a SQL default value on an insert and yet allow updating to the field. We have an instance where a SQL table has an identity column "id" and another column which is set to ident_current("table"). The only way that I know of to get the field inserted with the default value is to set the field as DatabaseGenerated(DatabaseGeneratedOption.Computed) so that it is ignored on the insert. However by having that attribute then we cannot perform an update to the column. Also it's a self referencing foreign key so we can't do an insert then immediate update to get around the issue. Don't ask me why the table is designed this way - just the way it was set up before so we're kind of stuck with it for now. A simple diagram of our setup is below:
DomainClass:
Class1 {
public int id {get;set;}
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public int id2 {get;set;}
public string Name {get;set;}
}
SQL (pseudo):
Table (
id INT which is an identity(1,1) column,
id2 INT NOT NULL with a default value of ident_current("table")
Name nvarchar(50)
)
We would want the insert statement generated by EF to be:
INSERT INTO Table(Name) VALUES('Name')
and the update to be:
UPDATE table
SET id2 = *somenumber*, name = 'Name'
Thanks a lot for all the help. We are using EF 4.3.1.0 if that's needed as well.
There is no way AFAIK. See this and that.
The first link points to a suggestion about using sequences as primary keys, which seems like something you might want to do instead given your example code.
The second link points to a suggestion about generic handling of default values, which is currently not supported either, but would be another potential starting point toward adding support for what you need.