Insert postgres row using npgsql - entity-framework

In my postgres database, the keys for my tables are serial data types that the postgres database autoincrements. Using C#, Npgsql, Entity Framework 6, WPF, linq and EF dbsets (any extension methods?), is there a way to implement a repository method such as:
public virtual void Add(TEntity entity, string addrow)
{NpgsqlCommand cmd = new NpgsqlCommand(addrow, DbConnect);
cmd.ExecuteNonQuery();}
The viewmodel would pass in the string argument something like:
_addRow = string.Format("insert into mytable ('descrip') values ( " + DescripProperty + ") returning 'mytable_id' into mytableID");
Is this the right way to insert a postgres row / record that has an autoincrement key? Or do I need to utilize a PL/pgSQL - SQL Procedural Language function to first lock the table and do the insert from that server side? Thank you in advance.
EDIT ---------------------------
For clarification, I pass the EF DbContext into the genericrepository constructor. This code is in genericrepository class:
private readonly DbSet<TEntity> _aquery;
public DbSet<TEntity> AQuery;
// CTOR - inject dbcontext/entities
public GenericRepository(MyContextClass context)
{
if (context == null)
throw new ArgumentNullException("context");
_theDbContext = context;
_aquery = _theDbContext.Set<TEntity>();
AQuery = _aquery;
}
So I can do AQuery.AsQueryable, etc.

Your SQL query is correct in the sense that the serial key will get autogenerated by PostgreSQL and then return the new ID - there's no need for pl/pgsql or any explicit locking - PostgreSQL's serial mechanism is atomic and will work. However, several comments on your code:
You're passing Entity Framework entirely and doing things with raw SQL. The whole point of an ORM is to generate this kind of statement for you so you don't have to, and EF6 is perfectly capable of doing so.
Concatenating a parameter into your SQL query is a bad idea and opens the door to SQL injection. You should use a parameter instead.
You don't seem to be actually using the returned ID, so there's little use for the returning clause (although you may have omitted the relevant code)

Related

Locking issue when trying to alter column data in migration up

I am trying to add an sql statement to an up-method of a migration for my current project. The database is an Ms Access database. The migrations get applied during run-time.
The situation is as follows:
I have a base Initial-create migration, which in my case is assumed to be already applied. Due to the nature of this application we have a table A which contains some kind of a foreign key, but without any sql-constraints defined. This means the foreign-key relationship is designed via program code and not in sql means an foreign key relationship. The key is an string and if there is no foreign-element the value is empty.
Now we want to add a new migration which enforces this relationship via sql-constraints. This works just fine via standard ef-core migration code, but the problem comes when the migration gets applied to a non empty database. The sql foreign key would need all the empty strings in table A to be null (otherwise we get an exception)
The seemingly easy solution was to add the following statement in the up-method of the new migration:
UPDATE A SET ForeignKeyColumn = NULL WHERE ForeignKeyColumn & \"\" = \"\""
But this results in the following exception:
System.Data.OleDb.OleDbException (0x80040E14): The database engine could not lock table 'A' because it is already in use by another person or process.
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextErrorHandling(OleDbHResult hr)
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextForSingleResult(tagDBPARAMS dbParams, Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteCommandText(Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteCommand(CommandBehavior behavior, Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(CommandBehavior behavior, String method)
at System.Data.OleDb.OleDbCommand.ExecuteNonQuery()
at EntityFrameworkCore.Jet.Data.JetCommand.ExecuteNonQueryCore()
at EntityFrameworkCore.Jet.Data.JetCommand.<>c.<ExecuteNonQuery>b__40_0(Int32 _, JetCommand command)
at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable`1 source, TAccumulate seed, Func`3 func)
at EntityFrameworkCore.Jet.Data.JetCommand.ExecuteNonQuery()
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
at X.Infrastructure.Setup.Migrate(IFactory`1 pDatabaseContextFactory, String pDatabasePath)
at X.COM.XCOMWrapper.Setup(ISettingsProvider pSettingsProvider)
However if we remove this sql statement from the migraition code and execute it as follows, before the call of context.Database.Migrate():
var dbConnection = context.Database.GetDbConnection();
dbConnection.Open();
using (var transaction = dbConnection.BeginTransaction())
{
var updateForeignKeyReferences= dbConnection.CreateCommand();
updateForeignKeyReferences.CommandText = "UPDATE A SET ForeignKeyColumn = NULL WHERE ForeignKeyColumn & \"\" = \"\"";
updateForeignKeyReferences.ExecuteNonQuery();
transaction.Commit();
}
dbConnection.Close();
It works just fine.
Is my approach of using the sql code in the up-method completly wrong? What are possible reasons for this? And most important, how can I fix this? The second approach is my current work-around for this problem but I fear that this means in the long run I can not use the migrations mechanism and have to go for a custom solution (or another framework). I would prefer to just stick with ef core.
Important:
This application works with an legacy application and we have to insert the application history via sql code on the inital startup. For this we create an transaction and simple create the history table and insert the initially created table. This works just fine, and the transactions as well as the commands should all be closed. The table A is never touched by this functions.
Using migrationBuilder.Sql("UPDATE `A` SET `ForeignKeyColumn` = NULL WHERE `ForeignKeyColumn` = ''") is the correct procedure.
It should execute fine.
Unfortunately, there seems to be an issue, where Jet still holds a lock on the table used in the UPDATE command, when the CREATE INDEX statement is executed (that has been generated for your new navigation property and is part of the Up() migration method).
This is only an issue, if both statements are executed inside the same transaction (which is the case for migrations by default). Otherwise, no lock is held and the CREATE INDEX statement succeeds.
The simplest way to fix this issue, is to set the migrationBuilder.Sql() parameter suppressTransaction to true.
This will execute the statement outside of the rest of the transaction, and not lock the table:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
"UPDATE `A` SET `ForeignKeyColumn` = NULL WHERE `ForeignKeyColumn` = ''",
suppressTransaction: true);
migrationBuilder.CreateIndex(/* ... */);
migrationBuilder.AddForeignKey(/* ... */);
}
The other way, which is able to execute the UPDATE statement inside a transaction, is to execute the command in its own dedicated migration:
Add an empty migration. Add your migrationBuilder.Sql() call to the empty Up() method of this migration.
Add the actual migration (containing the CreateIndex() and AddForeignKey() operations).
Apply both migrations to your database.

Schema-less table models with EF6 code first from database + Dynamic schema

I have a situation with multiple Dev/Test/Prod databases + Dev has a different schema name, with structure largely the same as test and prod.
I'd like to create code first DbContext with table classes void of schema name(in the Table attribute) and supply the schema at run time.
I have no code sample since the whole DbContext is generated by wizard, but this is what I'm trying to avoid:
[Table("LOC.MY_TABLE")]
public partial class MY_TABLE

mvc Entity Framwork, insert using select max() from table

I am developing an App using MV5 and Entity Frameowrk to connect to SQL Server Database
I need to do an Insert where the is getting ID like this.
Select max(id)+1 from table where field_id = #ID.
The idea is to do it in one transaction.
Is that Posible to do it in Entity Framework or I should use a Store Procedure?
EF compiles the C# expressions you provide it into SQL, and then executes the resulting SQL. So you can write such a query in LINQ, which will in-fact be computed during a single "call" to the database:
long id = MyDbContext.MyDbSet.Max(entity => entity.Id) + 1;
Do note, that if you are doing this so that you can assign a non existing ID to a new entry- there's no need. EF takes care of this for you, so long as the field is named Id and it is of type long. Just do not assign it any value.

EF 6 Migration: How to execute sql SELECT?

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

How to set database column as "Sparse" when using EF Code First Fluent API?

I am working with a system that is using EF code first and I would like to use a number of SQL Server sparse columns on a table. My current solution is to drop the table created by EF and re-add via a script during the database initialization. Is this something that can be configured with Fluent API in a class inherited from EntityTypeConfiguration or other means?
If you use Entity Frameworks migrations, you can issue a SQL statement like this in the Up method for the migration that adds the sparse column:
Sql("alter table TableName alter column ColumnName int sparse");
Even if you don't use migrations, any one-time execution of dbContext.Database.ExecuteSqlCommand with the same SQL will work.
Neither way is as nice as if you could explicitly configure the type through EF, but they're still better than dropping and replacing the entire table.
This is now supported in EF Core 6.0 as per the documentation here
This can now be configured using IsSparse in OnModelCreating. For example:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<ForumModerator>()
.Property(e => e.ForumName)
.IsSparse();
}