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.
Related
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.
Using fuelphp, is there a way to modify an existing \ORM\Model to convert it to \ORM\Model_Temporal? I started looking into creating a migration, which needs to 1) add the necessary columns to my table (not difficult); 2) update the primary key to include the new columns as part of a compound primary key (difficult). The problem I am having is that I don't see a method in the DBUtil that performs this operation.
What is the right way to do this?
Currently DBUtil does not include a way to alter primary keys because the method of doing so differs between DB systems and DBUtil is not actually that aware what DBMS you are using.
To do this you will have to construct the query manually in your migration and use DB::query() to execute it.
In case anyone else is faced with the same task, here is what I ended up doing. My database is MySQL, the syntax may have to be changed if you're using a different DBMS. My original primary key was an auto-incrementing column named id.
class Modify_TableName_To_Temporal
{
public function up()
{
\DBUtil::add_fields('tablename',array(
'temporal_start'=>array('constraint'=>11, 'type'=>'int','default'=>0),
'temporal_end'=>array('constraint'=>11, 'type'=>'int','default'=>2147483647),
));
\DB::query('ALTER TABLE tablename MODIFY id int, DROP PRIMARY KEY')->execute();
\DB::query('ALTER TABLE tablename ADD PRIMARY KEY (id, temporal_start, temporal_end), MODIFY id int auto_increment')->execute();
}
public function down()
{
\DB::query('ALTER TABLE tablename MODIFY id int, DROP CONSTRAINT primary')->execute();
\DB::query('ALTER TABLE tablename ADD PRIMARY KEY (id), MODIFY id int auto_increment')->execute();
\DBUtil::drop_fields('tablename',array('temporal_start','temporal_end'));
}
}
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 :-)
}
In our database, every table has two DateTime columns, CreatedOn and ModifiedOn, set via triggers in SQL Server. CreatedOn is set on INSERT, and ModifiedOn is set on INSERT and UPDATE.
I am trying to use Entity Framework 4.1. How should I annotate/configure the two properties?
I think it involves the annotation [DatabaseGenerated(DatabaseGeneratedOption.Computed)], but should I use that annotation for both, or should I set [DatabaseGenerated(DatabaseGeneratedOption.Identity)] on the CreatedOn field?
According to MSDN Identity simply implies that The database generates a value when a row is inserted., which seems true here.
Also, should I use [Timestamp]?
Use Identity for CreatedOn and Computed for ModifiedOn. Identity means that value is set only during insert and returned back to application. Computed is set during each modification (including insert) and value is returned back to the application after each executed insert or update.
Just be aware that neither of these properties can be set in the application. Computed columns also can't be part of primary key or foreign key (it will not be your case).
This will only work with existing database. When using code-first Computed can be set only for timestamp or rowversion.
Timestamp is used for optimistic concurrency. If you mark a column as timestamp each update will contain condition WHERE timestampColum = #lastKnownValue. It will update the record only if last known value is the same as current value. If the value is different you will get an exception. It is usually used with timestamp SQL type. Using it with datatime would require some tests. Value of SQL datatime is not the same as value in .NET.
If I have 2 tables 1 with a composite primary key where one of the keys is also a foreign key in another table:
Table 1:
A (PK, FK - maps to X in Table 2)
B (PK)
C
Table 2:
X (PK)
Y
Because A is both the PK in table 1 and FK in table 2, when I use EF to generate the entity model, I have both a Scalar AND a Navigation property for A in table 1. I cannot seem to remove A as a scalar (I think because it is a primary key).
The problem I am having is that if I create a table1Entity and set A's scalar property to a new value, A's navigation property will not be changed automatically (and vice versa).
Ideally I just want A to expose the navigation property - which is the way it behaves if A was not also part of the composite primary key anyway. Is there any way to achieve this?
Am I correct in assuming that Table1 derives from Table2? If so, I would do it like so:
(I'd also change the PK for both tables to the same name, since they probably have the same meaning - for the instance of this, I'll use the example ID)
First, create the model with the default relationships (I usually just import the two tables from the database)
In the designer, right click the base type, add inheritance, select the derived type.
Delete the one to zero or one association
Then, since the base type already has column ID, delete it from the derived type.
Go to table mapping for the derived type, and map the ID property to the ID of the table.
Well, not really. Create the view with schemabinding and create a clustered index on the view (SQL Server 2008 or later, earlier versions I'm not sure can do that). The clustered index will be recognised as a primary key, thus tricking EF(VS) into believing the view is a real table.
Have you expicity set the Ids of the composite key and referenced these in your configuration?
i.e
public class Table1
{
public Table2 A{get;set}
public int AId {get;set;}
public int BId {get;set;}
}
I assume you'll need something like:
HasKey(pc => new { pc.AId, pc.BId});
HasRequired(x => x.A).WithMany().HasForeignKey(x => x.AId);
Instead of mapping to table 1 directly, add a view to your database that's got all of table 1's fields, plus an extra copy of A (A2).
Then, map the scalar key to A2 and the nav key to A.
(You'll run into a problem where if you use a view, Visual Studio can't find a primary key; fix this by manually editing the XML of the edmx file and adding a <Key><PropertyRef ... /></Key> to the <EntityType> for table A)
I know - it's hacky and horrible... but hey - it works!