I'm building a Web API in F# to test some things out. I come from a C# background and I've structured things how I do there.
You can find the repo here: https://github.com/MrGodlike6/WebApiTest
I've referenced the WebApiTest.Repositories.SqlServer project in WebApiTest.WebApi but for some reason Visual studio says it can't find the WebApiTest.Repositories.SqlServer namespace.
The thing is that the solution compiles fine and I can run the application, it's just that I don't have IntelliSense for anything coming from the SqlServer project (e.g. in the CompositionRoot module).
I've tried a lot of things, from different DBs, to making a .NET Framework 4.7.2 project for the DB project. So far nothing works. Tried both Nuget & Paket and no difference. Tried VS 2019. In VS 2022 it gives some compile error that is unrelated.
I used dotPeek from JetBrains to look at the resulting .dll and the namespace & module are there as expected. Don't know what could be causing this.
Also can you indicate if the project/folder structure that I use is good for F#? Any other suggestions are also welcome.
EDIT: The SQL part is based of https://github.com/isaacabraham/get-programming-fsharp/tree/master/src/code-listings/lesson-35. Here everything works, although the SQL code is not put in a separate project.
I've commented the SqlEnumProvider and I no longer have problems in the composition root. I'll look a little closer at this provider and see if I can come up with something.
EDIT2: Created a issue on GitHub: https://github.com/fsprojects/FSharp.Data.SqlClient/issues/410
I've reproduced what you're seeing. The problem appears to be caused by a design-time error in the FSharp.Data type provider. In particular, SqlEnumProvider seems to be the source. When I remove the DbOperations type from AccountRepository.fs (using plain strings instead of enum values), I'm able to get Intellisense for WebApiTest.Repositories.SqlServer in CompositionRoot.fs.
Here's my kludgy workaround:
[<AutoOpen>]
module private DB =
let [<Literal>] Conn = #"Data Source=.;Database=BankAccountDb;Integrated Security=True;Connect Timeout=60"
type AccountsDb = SqlProgrammabilityProvider<Conn>
type GetAccountId = SqlCommandProvider<"SELECT TOP 1 AccountId FROM dbo.Account WHERE Owner = #owner", Conn, SingleRow = true>
type FindTransactions = SqlCommandProvider<"SELECT Timestamp, OperationId, Amount FROM dbo.AccountTransaction WHERE AccountId = #accountId", Conn>
type FindTransactionsByOwner = SqlCommandProvider<"SELECT a.AccountId, at.Timestamp, at.OperationId, at.Amount FROM dbo.Account a LEFT JOIN dbo.AccountTransaction at on a.AccountId = at.AccountId WHERE Owner = #owner", Conn>
// type DbOperations = SqlEnumProvider<"SELECT Description, OperationId FROM dbo.Operation", Conn>
let toBankOperation operationId =
match operationId with
| "Deposit" -> Deposit
| "Withdraw" -> Withdraw
| _ -> failwith "Unknown operation!"
let fromBankOperation bankOperation =
match bankOperation with
| Deposit -> "Deposit"
| Withdraw -> "Withdraw"
I searched for an existing issue on GitHub, but didn't find one, so you might want to open a new one yourself.
If anyone else wants to try this, here's the dummy database schema I used to get things to compile:
USE [BankAccountDb]
GO
/****** Object: Table [dbo].[Account] Script Date: 9/16/2021 11:40:48 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Account](
[Owner] [nchar](10) NOT NULL,
[AccountId] [uniqueidentifier] NOT NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[AccountTransaction] Script Date: 9/16/2021 11:40:48 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AccountTransaction](
[AccountId] [uniqueidentifier] NOT NULL,
[Timestamp] [datetime] NOT NULL,
[OperationId] [nchar](10) NOT NULL,
[Amount] [money] NOT NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Operation] Script Date: 9/16/2021 11:40:48 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Operation](
[Description] [nvarchar](100) NULL,
[OperationId] [nvarchar](100) NULL
) ON [PRIMARY]
GO
Thank you Brian for the script.
As the documentation says,
"An enumeration type (or enum type) is a value type defined by a set of named constants of the underlying integral numeric type."
I changed the OperationId column data type to int in dbo.Operation and dbo.AccountTransaction and populated the table dbo.Operation, because the SqlEnumProvider complained about lack of data during the compilation.
CREATE TABLE [dbo].[Operation] (
[Description] NVARCHAR (100) NULL,
[OperationId] INT NOT NULL
);
CREATE TABLE [dbo].[AccountTransaction] (
[AccountId] UNIQUEIDENTIFIER NOT NULL,
[Timestamp] DATETIME NOT NULL,
[OperationId] INT NOT NULL,
[Amount] MONEY NOT NULL
);
insert dbo.Operation (Description, OperationId) values ('Deposit', 1), ('Withdraw', 2)
Now I can compile the solution.
Related
I'm using SQLProvider for making a query in F# project. In my DB I have a column which is storing GUID - 'Id' char(36) NOT NULL. And when query is made I got an error:
Unhandled exception. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.InvalidCastException: Unable to cast object of type 'System.Guid' to type 'System.String'.
I'm using MySqlConnector as a driver.
Here is how table looks in DB
CREATE TABLE `Comics` (
`Id` char(36) NOT NULL,
`Price` double DEFAULT NULL,
`Title` char(255) DEFAULT NULL,
`Image` char(255) DEFAULT NULL,
`Description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Here is types which was generated based on DB
Here is a query
let ctx = sql.GetDataContext()
let ComicsTable = ctx.ComicsShop.Comics;
let getById (id: string) =
query {
for c in ComicsTable do
where (c.Id = id)
select c.Id
} |> Seq.head
And usage
let result =
getById "0e49d94e-76c4-44be-b2ea-38382b297c78"
Console.WriteLine(result)
Thanks for your help.
After quite some time of investigation the issue was with DB.
My Id field has type as char(36) which is converted into string. But since that column contains GUID, SQLProvider will make a query assuming that field is Guid, and it will throw an error in Runtime.
The solutions is simple enough Id columns which is containing Guid should be typed as varchar(36) then all types will be converted correctly.
-=-=-=-= Latest update
In my catch block, I brought up the exception variable in Quick Watch, and drilling down the inner exceptions showed me this error:
Unable to load DLL 'Microsoft.Data.SqlClient.SNI.x86.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
Problem is, in my bin\debug directory, that file is there!!! So why can it not be found? I tried but was not able to reference it directly.
-=-=-= Update
A suggested answer below had some links which I re-examined. I picked out useful code, such as assigning my log object to the static logger, followed by a call to CloseAndFlush.
That showed me Microsoft.Data.SqlClient.dll errors when the CloseAndFlush call was executed. I used NuGet to update SQLClient, but still getting those errors:
Exception thrown: 'System.DllNotFoundException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'System.TypeInitializationException' in Microsoft.Data.SqlClient.dll
I am now investigating those SQL.Client errors.
-=-=-=-=-=-=
Working with Asp.NET, not core. Trying to get a basic sql server sink to work, having no luck. Something very simple must be the problem, but I can't see it.
Below is the table to write to, it already exists. With the seriuser account I can connect via SSMS, and perform all CRUD operations. I also wrote a method that uses SQL data objects that connects to the db and I can perform all CRUD operations on it that way also. But with Serilog, the log statement logs nothing. What could I be missing? The database is local on my machine. Breakpoint shows a valid logger object. Tried it also by passing in schemaname in sinkoptions, no luck. And no errors, had a try catch block around logger code previously.
A solution with a console app and a class library. Can't get much simpler than this.
The console app has this in program.cs:
class Program
{
static void Main(string[] args)
{
SLogger logger = new SLogger();
}
}
The class file and sql table:
public class SLogger
{
public SLogger()
{
var columnOption = new ColumnOptions();
columnOption.Store.Remove(StandardColumn.MessageTemplate);
ILogger log = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.MSSqlServer(#"Data Source=LW39\QA;Initial Catalog=DEV;user id=seriuser;Password=SERIUSER1;",
sinkOptions: new MSSqlServerSinkOptions { TableName = "Logs" },
columnOptions: columnOption)
.CreateLogger();
log.Information("Logger created.");
.....
.....
CREATE TABLE [dbo].[Logs](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Message] [nvarchar](max) NULL,
[MessageTemplate] [nvarchar](max) NULL,
[Level] [nvarchar](128) NULL,
[TimeStamp] [datetime] NOT NULL,
[Exception] [nvarchar](max) NULL,
[Properties] [nvarchar](max) NULL,
CONSTRAINT [PK_Logs] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
Mentioned in my last update that I was receiving this error, which I beleived was the root cause of my problem:
Unable to load DLL 'Microsoft.Data.SqlClient.SNI.x86.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
and my confusion because said file was in the class library directory.
As I also mentioned, I had two projects in my solution: the class library and the test console app. Turns out the dll need to be in the bin directory of the console app!! Specifically in the x86 directory, which I had to create.
Once I placed the file there, the problem was solved. Thanks for all who helped!
This is a very common question. Lots of answers here on SO:
Serilog MSSqlServer sink not writing to table
Serilog ms sql sink not writing to my table
I'm trying to change my code first ID column from 'int' to 'Guid', and when trying to run the migration, I get the message:
Identity column 'CustomFieldId' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, and constrained to be nonnullable.
I'm defining the column like this:
public partial class CustomField : BaseEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid CustomFieldId { get; set; }
Mapping it in CustomFieldMapping.cs like this:
public CustomFieldMapping()
{
//Primary key
HasKey(t => t.CustomFieldId);
//Constraints
Property(t => t.CustomFieldId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
And the migration that's generated is trying to do this:
public override void Up()
{
DropForeignKey("dbo.CustomField", "CustomFormId", "dbo.CustomForm");
DropForeignKey("dbo.CustomData", "CustomFieldId", "dbo.CustomField");
DropForeignKey("dbo.CustomForm", "ParentFormId", "dbo.CustomForm");
DropIndex("dbo.CustomField", new[] { "CustomFormId" });
DropIndex("dbo.CustomForm", new[] { "ParentFormId" });
DropIndex("dbo.CustomData", new[] { "CustomFieldId" });
DropPrimaryKey("dbo.CustomField");
DropPrimaryKey("dbo.CustomForm");
AlterColumn("dbo.CustomField", "CustomFieldId", c => c.Guid(nullable: false));
AlterColumn("dbo.CustomField", "SortOrder", c => c.Int(nullable: false));
AlterColumn("dbo.CustomForm", "CustomFormId", c => c.Guid(nullable: false));
AlterColumn("dbo.CustomForm", "ParentFormId", c => c.Guid());
AddPrimaryKey("dbo.CustomField", "CustomFieldId");
AddPrimaryKey("dbo.CustomForm", "CustomFormId");
CreateIndex("dbo.CustomField", "CustomForm_CustomFormId");
CreateIndex("dbo.CustomForm", "ParentFormId");
CreateIndex("dbo.CustomData", "CustomField_CustomFieldId");
AddForeignKey("dbo.CustomField", "CustomForm_CustomFormId", "dbo.CustomForm", "CustomFormId");
AddForeignKey("dbo.CustomData", "CustomField_CustomFieldId", "dbo.CustomField", "CustomFieldId");
AddForeignKey("dbo.CustomForm", "ParentFormId", "dbo.CustomForm", "CustomFormId");
I would like it to be a sequentially incremented Guid. What am I doing wrong?
To solve this problem I used a Sql() method in the Up() and Down() methods of the migration class. The SQL command string in the Up() method removes the primary key constraint on the ID column, drops the ID column of type int and then adds a new ID column with of type Guid. The Down() method does the same thing but drops the Guid column and adds a new int column.
I found a few solutions on Stack Overflow that resolve the "change column type" by running a SQL command in a query window. To address your comment:
We're just trying to keep a clean/clear migration path to trace when
we did what which is not always easy with SQL.
I used SQL commands within the Up() and Down() migration methods. For me this solution works well in my projects.
The solution at the bottom of this answer was constructed from several Stack Overflow questions/answers. Skip to that for just the code. Here are the long-winded details.
Using SQL commands in a migration class
I couldn't find a solution that used Entity Framework migration methods like AlterColumn() and DropColumn() ONLY.
Rather than using a mix of migration methods and commands in the Sql() method, I used all SQL commands within a string in the Sql() migration method. Using all SQL commands made it easier to test in a query window in Visual Studio or SQL Server Management Studio.
The answer by 'Uchitha' gave the starting steps for adding the Sql() "method within the desired migration class."
Generate migration class using Add-Migration
Alter the class using code similar to above
Run the migration using Update-Database
The Sql() method sample in the answer looks like:
Sql("UPDATE dbo.YourTable SET Column1 = 'VALUE1' ");
Changing the column type - generic steps
I used the answer by 'JustAnotherUserYouMayKnow' to get started on the steps to change the column type. I didn't follow this explicitly but it provided just the basic framework of the need to drop a column and recreating it.
Add a new column with your new type
Use Sql() to take over the data from the original column using an update statement
Remove the old column
Rename the new column
Sequential GUIDs
The answer from 'Icarus' provided the ALTER TABLE statement with the use of newsequentialid() to generate sequential GUIDs as per your statement:
I would like it to be a sequentially incremented Guid.
ALTER TABLE your_table
ADD your_column UNIQUEIDENTIFIER DEFAULT newsequentialid() NOT null
Take note of privacy concerns by 'Johan' in the comment section of the answer by 'Icarus':
If privacy is a concern, do not use newsequentialid(). It is possible to guess the value of the next generated GUID and, therefore, access data associated with that GUID
Alter primary key
The column you want to change is an ID column and you've set it as the primary key. Therefore, before dropping the existing ID column you'll need to remove the primary key using another ALTER TABLE SQL command.
See the selected answer from 'darnir' for "How can I alter a primary key constraint using SQL syntax?"
ALTER TABLE <Table_Name>
DROP CONSTRAINT <constraint_name>
ALTER TABLE <Table_Name>
ADD CONSTRAINT <constraint_name> PRIMARY KEY (<Column1>,<Column2>)
See the note by 'Oleg' to determine if this will be a factor:
PRIMARY KEY CONSTRAINT cannot be altered, you may only drop it and create again. For big datasets it can cause a long run time and thus - table inavailability.
I had problems when the command with DROP CONSTRAINT above was executed. The results pane listed a constraint that was auto-generated even though I'd used a specific constraint name in the ALTER TABLE ... ADD COLUMN command. See this question "Why does SQL keep creating a DF constraint?" and this question if you experience something similar.
To fix the problem with dropping the constraint I used the answer by 'ScubaSteve' from this question: "How to drop SQL default constraint without knowing its name?" With the addition of the note by 'Seven' here are the SQL commands:
DECLARE #ObjectName NVARCHAR(100)
SELECT #ObjectName = OBJECT_NAME([default_object_id]) FROM SYS.COLUMNS
WHERE [object_id] = OBJECT_ID('[tableSchema].[tableName]') AND [name] = 'columnName';
IF #ObjectName IS NOT NULL EXEC('ALTER TABLE [tableSchema].[tableName] DROP CONSTRAINT ' + #ObjectName)
The comment by 'Seven' in 'ScubaSteve's answer. I added the 'if' condition as at times the EXEC would fail when no constraint was found.
To make this script idempotent add IF #ObjectName IS NOT NULL before EXEC command
The final solution
Make sure to replace MyTableName, MyColumnName, and dbo in the code below to your table name, column name (e.g. set column name to Id) and table schema respectively.
public override void Up()
{
Sql(#"
DECLARE #ObjectName NVARCHAR(100)
SELECT #ObjectName = OBJECT_NAME([default_object_id]) FROM SYS.COLUMNS
WHERE [object_id] = OBJECT_ID('[dbo].[MyTableName]') AND [name] = 'MyColumnName';
IF #ObjectName IS NOT NULL EXEC('ALTER TABLE [dbo].[MyTableName] DROP CONSTRAINT ' + #ObjectName)
ALTER TABLE dbo.MyTableName DROP CONSTRAINT PK_MyTableName, COLUMN MyColumnName
ALTER TABLE dbo.MyTableName
ADD Id UNIQUEIDENTIFIER DEFAULT (newsequentialid()) NOT NULL
CONSTRAINT PK_MyTableName
PRIMARY KEY CLUSTERED ([MyColumnName])
");
}
public override void Down()
{
Sql(#"
DECLARE #ObjectName NVARCHAR(100)
SELECT #ObjectName = OBJECT_NAME([default_object_id]) FROM SYS.COLUMNS
WHERE [object_id] = OBJECT_ID('[dbo].[MyTableName]') AND [name] = 'MyColumnName';
IF #ObjectName IS NOT NULL EXEC('ALTER TABLE [dbo].[MyTableName] DROP CONSTRAINT ' + #ObjectName)
ALTER TABLE dbo.MyTableName DROP CONSTRAINT PK_MyTableName, COLUMN Id
ALTER TABLE MyTableName
ADD MyColumnName int IDENTITY(1, 1) NOT NULL
CONSTRAINT PK_MyTableName
PRIMARY KEY CLUSTERED ([MyColumnName] ASC)
");
}
I found a simple solution for the problem. You just need to drop the column CustomFieldId then add it back as a Guid column. That way there won't be any error message and the migration will pass:
public override void Up()
{
DropForeignKey("dbo.CustomField", "CustomFormId", "dbo.CustomForm");
DropForeignKey("dbo.CustomData", "CustomFieldId", "dbo.CustomField");
DropForeignKey("dbo.CustomForm", "ParentFormId", "dbo.CustomForm");
DropIndex("dbo.CustomField", new[] { "CustomFormId" });
DropIndex("dbo.CustomForm", new[] { "ParentFormId" });
DropIndex("dbo.CustomData", new[] { "CustomFieldId" });
DropPrimaryKey("dbo.CustomField");
DropPrimaryKey("dbo.CustomForm");
DropColumn("dbo.CustomField", "CustomFieldId")
AddColumn("dbo.CustomField", "CustomFieldId", c => c.Guid(nullable: false));
AlterColumn("dbo.CustomField", "SortOrder", c => c.Int(nullable: false));
AlterColumn("dbo.CustomForm", "CustomFormId", c => c.Guid(nullable: false));
AlterColumn("dbo.CustomForm", "ParentFormId", c => c.Guid());
AddPrimaryKey("dbo.CustomField", "CustomFieldId");
AddPrimaryKey("dbo.CustomForm", "CustomFormId");
CreateIndex("dbo.CustomField", "CustomForm_CustomFormId");
CreateIndex("dbo.CustomForm", "ParentFormId");
CreateIndex("dbo.CustomData", "CustomField_CustomFieldId");
AddForeignKey("dbo.CustomField", "CustomForm_CustomFormId", "dbo.CustomForm", "CustomFormId");
AddForeignKey("dbo.CustomData", "CustomField_CustomFieldId", "dbo.CustomField", "CustomFieldId");
AddForeignKey("dbo.CustomForm", "ParentFormId", "dbo.CustomForm", "CustomFormId");
Today I migrated an old application from EF 4.2 to EF 4.3.1.
In my app I was using CodeFirst, but after migration it stopped working, and couldn't find a reason for that.
To clear any other possible problem I decided to create a small console application and I used the Data Migration walk-through published by the ADO team:
http://blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx
I copied exactly the code of the blog, but instead of working correctly (creating the DB, creating the schema, and inserting the blog) I get some errors:
only the DB is created, but no tables
I get this error Conversion failed when converting datetime from character string."
All of this is on SQL Server 2005 express.
I tried the same using SQL Compact, but same result (tho different error):
only the DB is created (in this case a sdf file in the bin folder), but no tables
I get the error The format of the specified date or time datepart is not valid. [ String = 2012-04-19T13.21.04.364 ]
I think in both cases the problem lies in the line that EF wants to enter as first migration:
INSERT INTO [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion])
VALUES ('201204191321184_init', '2012-04-19T13.21.04.364', ...., '4.3.1');
Apparently the format with the . is wrong, at least in my locale, it should be with :
Is this a bug or what? It always worked with other datetime before.
UPDATE
I tried running it as explicit migration, and applying the migration with the -verbose flag set, and here is what I get:
PM> Update-Database -Verbose
Using NuGet project 'ConsoleApplication2'.
Using StartUp project 'ConsoleApplication2'.
Target database is: '|DataDirectory|ConsoleApplication2.ConsoleApplication1.BlogContext.sdf' (DataSource: |DataDirectory|ConsoleApplication2.ConsoleApplication1.BlogContext.sdf, Provider: System.Data.SqlServerCe.4.0, Origin: Convention).
Applying explicit migrations: [201204191356197_Initial].
Applying explicit migration: 201204191356197_Initial.
CREATE TABLE [Blogs] (
[BlogId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](4000),
CONSTRAINT [PK_Blogs] PRIMARY KEY ([BlogId])
)
CREATE TABLE [__MigrationHistory] (
[MigrationId] [nvarchar](255) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[Model] [image] NOT NULL,
[ProductVersion] [nvarchar](32) NOT NULL,
CONSTRAINT [PK___MigrationHistory] PRIMARY KEY ([MigrationId])
)
[Inserting migration history record]
System.Data.SqlServerCe.SqlCeException (0x80004005): The format of the specified date or time datepart is not valid. [ String = 2012-04-19T13.56.45.437 ]
at System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommandText(IntPtr& pCursor, Boolean& isBaseTableCursor)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery()
at System.Data.Entity.Migrations.DbMigrator.ExecuteSql(DbTransaction transaction, MigrationStatement migrationStatement)
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ExecuteSql(DbTransaction transaction, MigrationStatement migrationStatement)
at System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements)
at System.Data.Entity.Migrations.Infrastructure.MigratorBase.ExecuteStatements(IEnumerable`1 migrationStatements)
at System.Data.Entity.Migrations.DbMigrator.ExecuteOperations(String migrationId, XDocument targetModel, IEnumerable`1 operations, Boolean downgrading)
at System.Data.Entity.Migrations.DbMigrator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.RunCore()
at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()
The format of the specified date or time datepart is not valid. [ String = 2012-04-19T13.56.45.437 ]
Update 2
I installed SQL Server Profiler, and profiled what is happening over there.
I executed all the statements one by one via query analyzer and the one that fails is, as already stated above, the insertion of the migration.
INSERT INTO [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201204231416585_InitialCreate', '2012-04-23T14.16.59.038Z', ...., '4.3.1')
When changing the format of the datatime string from 2012-04-23T14.16.59.038Z to 2012-04-23T14:16:59.038Z the command went through, so I guess somehow EF is sending the datatime in format that is not compatible with my locale.
Thank you
Simone
Thanks to the ADO.NET Team, this was a bug in the Migration code.
Apparently they forgot to specify InvariantCulture when they generate code for a DateTime field, so it works on EN locale, but doesn't in other locales.
To fix this problem, waiting for an official fix, you should specify a custom SqlGenerator that overrides the Generate(DateTime defaultValue) method:
class FixedSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override string Generate(DateTime defaultValue)
{
return "'" + defaultValue.ToString("yyyy-MM-ddTHH:mm:ss.fffK", CultureInfo.InvariantCulture) + "'";
}
}
And then specify the new SqlGenerator in the Configuration class:
SetSqlGenerator("System.Data.SqlClient", new FixedSqlGenerator());
If you want to use it just in manual migrations that's enough, if you only need CodeFirst, you have to specify the configuration in the application startup code or in the DbContext.
Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Migrations.Configuration>());
HTH
I had the same problem with a brand new project, in my case I solved it by specifying the right culture in my web.config
<globalization enableClientBasedCulture="false" culture="en-US" />
I'm doing a data migration of a small set of tables. The keys are of course alinged in the source database and I have composed initialization statements similar to this one:
context.Countries.Add(new Country{CountryId=75,CountryName="US"});
context.Countries.Add(new Country{CountryId=89,CountryName="Argentina"});
However, when I look in the database afterwards I find this:
CountryID | CountryName
------------------------
1 | US
2 | Argentina
(It disregards the CountryIds I provide and uses n+1 values for this identity column.)
In an effort to solve the problem I tried this:
context.Database.ExecuteSqlCommand("Set IDENTITY_INSERT COUNTRIES ON");
GetCountries().ForEach(c=>context.Countries.Add(c));
context.Database.ExecuteSqlCommand("Set IDENTITY_INSERT COUNTRIES OFF");
But I see no difference in the keys in the db.
How do I do an Identity Insert from my Initializer's Seed() method?
EDIT:
I'm posting the results of my final attempt for the benefit of anyone else following after:
private void InsertCountries(SodContext context)
{
context.Database.ExecuteSqlCommand("Set IDENTITY_INSERT COUNTRIES ON");
string cmd = "INSERT INTO COUNTRIES (CountryId,Name,IsActive) values ({0},'{1}',{2})";
GetCountries().ForEach(cntry=>context.Database.ExecuteSqlCommand(string.Format(cmd,cntry.CountryId,cntry.Name,cntry.IsActive==false?0:1)));
context.Database.ExecuteSqlCommand("Set IDENTITY_INSERT COUNTRIES OFF");
}
However, I get the error message that I have to turn IDENTITY_INSERT ON.
I'm giving up and switching over to doing this in a TSQL script.
If you have configured your primary key properties as database generated identities(ie: Auto increment) then EF will not include that column in the insert statements that are issued. So EF will request the generated key and update the primary key property.
What you can do is script the necessary data and execute that script directly without using entities.
I had a go at what you were trying in the c# code.
Putting everything in the same sql command statement does work.
const string sqlCmd = "set identity_insert [role] on insert into [role](id, name) values({0}, {1}) set identity_insert [role] off";
context.Database.ExecuteSqlCommand(sqlCmd,(int)SystemRole.SuperAdministrator, superAdminRole);
context.Database.ExecuteSqlCommand(sqlCmd, (int)SystemRole.Administrator, adminRole);
context.Database.ExecuteSqlCommand(sqlCmd, (int)SystemRole.Manager, managerRole);
context.Database.ExecuteSqlCommand(sqlCmd, (int)SystemRole.User, userRole);