Entity Framework code-first & stored procedure - entity-framework

I am using a code-first approach in my application. I have generated the entities (tables) using the approach. Now I would like to create a stored procedure as well through code. Can someone guide me as I tried migration option and it's failing.
I am using Entity Framework Code First approach.Using this,I have created Customer and some other entities.
Now I want to create a stored procedure 'GetCustomers' using context class and pass parameters and get result set in a collection
it has to return 2 collections as below
create procedure getcustomer #name nvarchar(max),#zipcode int
as
select id,name,zipcode from Customer where name like (#name );
select id,name,zipcode from Customer where zipcode =#zipcode
I want to create a stored procedure 'GetCustomers' using context class and not manually execute in DB.I need to achieve below results:
1.Pass name parameter alone and return first collection
2.Pass zipcode parameter alone and return 2nd collection.
3.Combine result collection of 1 and 2 into a single collection using merge

You can create/generate stored Procedure using CreateStoredProcedure() method using Add-
Migration option in Entity Framework.
Step 1: Generate Migration script using add-migration SP_DO_NOT_DELETE in Package Manager Console. If no Model Changes is there, then the system will generate Empty migration script like below.
public partial class SP_DO_NOT_DELETE : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
Step 2: After generating the Script, Please add your stored procedure inside Up() and down() methods like below. Note: below example, "dbo.GetNextDisplayId" is the Stored Procedure Name which will be used to get the NextAvailableDisplayId using Stored procedure.
public partial class SP_DO_NOT_DELETE : DbMigration
{
public override void Up()
{
CreateStoredProcedure(
"dbo.GetNextDisplayId",
body:
#"DECLARE #requestid INT
SELECT #requestid = NextAvailableDisplayId
FROM [TrackingNumberHistories] WITH (TABLOCKX)
UPDATE [TrackingNumberHistories]
SET NextAvailableDisplayId = #requestid + 1
SELECT #requestid AS 'NextAvailableDisplayId'"
);
}
public override void Down()
{
DropStoredProcedure("dbo.GetNextDisplayId");
}
}
Note: CreateStoredProcedure() in Up() Method will create Stored procedure automatically whenever running migration script. DropStoredProcedure() in Down() will be used to drop stored procedure when ever we roll back/delete the stored procedure automatically in migration script.
Hope this might help you to move forward!!

Related

Entity Framework Intercept Generate Migration Script

I use Entity Framework 6.2 Code First (.net framework 4.6.1) and I map few entities to view via Table Attribute. It works for select operations and I handle Insert/Update/Delete with writing trigger to view at sql server side. It works as expected, however when I add a new migration, Entity Framework generate RenameTable scripts for used Table Attribute (actuallyis expected behavior for EF). But I want to intercept migration generation and change these entities tableName to original name.
my code like;
[MapToView("Users","UsersView")]
public class User
{
...
}
I wrote MapToView Attribute, this attribute inherited by TableAttribute and pass to second parameter to TableAttribute. I create this Attribute because if I intercept migration generation, return original table name with this attribute parameters.
In this case when I run "add-migration migrationName" it creates migration scripts like this;
RenameTable(name: "dbo.Users", newName: "UsersView");
but i want to create empty migration when I run "add-migration migrationName" script.
anyone can help me?
I solve the problem.
First: Problem is; When I Map Entity to View EF Code-first generate migration with ViewName. This is problem because I want to use View Instead of Table. So I solve problem with this instructions;
1- I Create BaseEntityConfiguration that Inherited from EntityTypeConfiguration and all entity configuration classes are inherited by.
for example:
public class UserConfig: BaseEntityConfiguration<User> //Generic Type is Entity
{
public UserConfig()
{
}
}
2- I Create MapToViewAttribute that inherited by TableAttribute
public class MapToViewAttribute : TableAttribute
{
public string TableName { get; }
public string ViewName { get; }
public MapToViewAttribute(string tableName, string viewName) : base(viewName)
{
TableName = tableName;
ViewName = viewName;
}
}
3- I Use MapToViewAttribute for example User Entity to use View.
[MapToView("User","UserView")]
public class User
{
...
}
And in BaseEntityConfiguration's Constructor I Get Generic Type and custom attributes. If any entity has MapToView Attribute, I pass to TableName parameter to ToTable Method. So at runtime EF uses View for these entities but doesn't create migration with RenameTable for these entities.
protected BaseEntityConfiguration()
{
var baseType = typeof(TEntityType);
var attributes = baseType.GetCustomAttributes(true);
foreach (var attr in attributes)
{
if (attr.GetType() == typeof(MapToViewAttribute))
{
var tableName = ((MapToViewAttribute)attr).TableName;
ToTable(tableName);
}
}
}
Last EF don't use your configuration files, so you must tell the EF to use this in DbContext class's InternalModelCreate method.
My implementation like this;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typesToRegister = Assembly.GetExecutingAssembly()
.GetTypes().Where(IsConfigurationType);
foreach (var type in typesToRegister)
{
dynamic configurationInstance = type.BaseType != null
&& type.BaseType.IsGenericType
&& type.BaseType.GetGenericTypeDefinition() == typeof(BaseEntityConfiguration<>)
? Activator.CreateInstance(type, culture)
: Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
modelBuilder.Types().Configure(t => t.ToTable(t.ClrType.Name));
BaseDbContext.InternalModelCreate(modelBuilder);
}
But if you use this approach you must create Insert, Update and Delete Triggers/Rule (if you use SQLServer trigger is an option but if you use postgresql rule is better option) because EF uses this view for insert, update and delete operations.

Working with Schemas in EF Code First Migrations

I suppose this question is a cosmetic one; when you initially create an EF migration, it puts the schema in by default; for example:
public override void Up()
{
DropPrimaryKey("dbo.MyTable");
AddPrimaryKey("dbo.MyTable", "NewField");
This seems fine, unit you see the key name that it generates as a result (it has dbo in the key name).
I realise that one way around this is to specify the key name directly. Are there any other options, for example, can the schema be specified for a block, but not included in the specific modifications? For example:
public override void Up()
{
UseSchema("dbo");
DropPrimaryKey("MyTable");
AddPrimaryKey("MyTable", "NewField");
I realise that you can simply omit the schema name; i.e., this will work:
public override void Up()
{
DropPrimaryKey("MyTable");
AddPrimaryKey("MyTable", "NewField");
But how would I then deal with a situation where there were more than a single schema?
You can specify default schema using HasDefaultSchema method on DbModelBuilder class instance.
modelBuilder.HasDefaultSchema("schemaName");
You can also set schema for each entity using ToTable method on EntityTypeConfiguration<TEntityType> class instance. Which will generate migration scripts with provided schema for desired entity/ies.
modelBuilder.Entity<TEntity>().ToTable("tableName", "schemaName")
You can also use Table attribute to set schema for entity.
[Table("tableName","schemaName")]
Or you can write your own custom convention
public class DynamicSchemaConvention : Convention
{
public CustomSchemaConvention()
{
Types().Configure(c => c.ToTable(c.ClrType.Name, c.ClrType.Namespace.Substring(c.ClrType.Namespace.LastIndexOf('.') + 1)));
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new CustomSchemaConvention());
}
Related links:
DbModelBuilder.HasDefaultSchema Method
EntityTypeConfiguration.ToTable Method
TableAttribute Class
Entity Framework 6 - Code First: table schema from classes' namespace
Entity Framework Custom Code First Conventions (EF6 onwards)

Added Column to the Database is NOT getting retrieved in the View

I am doing a online ASP.net MVC5 course from udemy.I added a new column to my Table using EF migration.
Name Column Added
namespace Vidly.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddNameToMembershipTypes : DbMigration
{
public override void Up()
{
AddColumn("dbo.MembershipTypes","Name",c => c.String(nullable:false));
}
public override void Down()
{
DropColumn("dbo.MembershipTypes","Name");
}
}
}
Then I updated my database for this migration.
Screen Shot for Confirmation
Customer and MembershipTypes Tables have one-to-one relationship and I am able to retrieve MembershipTypes fields from the Customer Table.
The only problem is that The new column "Name" I just added to the MembershipTypes as I described at the very top is NOT getting retrieved from the View.
Following is The Visual confirmation(Although I tried to add Name but got error)
When I forcefully tried to access Name I got following
I am Not getting why I am Not being able to access Name Field as I have added some values too using migration and updated my database.
My feeling is that the link between Customer and MembershipTypes needs to be updated somehow but I donot know how?
The changes you have in your AddNameToMembershipTypes is only for updating your database schema. That will not automatically update your entity class. You still need to add the new property to your MemberShipType class.
public class MembershipType
{
public string Name { set;get;} // This one here
// Your existing properties here
public int DurationInMonths { set;get; }
}

EF6: Create stored procedure. Use Fluent API or DBMigrations?

I'm using EF6 code first to create my database. I understand the syntax, the DbContext, and the modelbuilder. I use LINQ for several exhaustive queries and everything works fine.
But now I need to do something that can't be done in one query using linq. I need to perform a Merge statement using a stored procedure.
I've seen several questions on how to create a stored procedure, like:
Create Stored Procedures using Entity Framework Code First?
Most answers are talking about creating a derived class for DbMigrations and overriding the Up() function. I understand what I should write in the Up function to make sure the stored procedure is created.
But what should I do to make that during database creation this Up function is called?
Should I do something in DbContext.OnModelCreating?
I don't think I should instantiate the subclass of DbMigrations and call Up().
The link mentioned above is talking about "Open the Package Manager Control". What is that? Or do you really use this method when migrating from an older version to a newer one?
After some investigation I found how to make sure that a stored procedure is created whenever the database is created.. I found two methods, each with their own advantages and disadvantages. Hence I describe them both. Sorry if this makse the answer fairly long.
The two methods described here are:
Create a DataBase Initializer, a class that implements IDataBaseInitializer. This will probably be a class derived from DropCreateDatabaseIfModelChanges or similar. Override the Seed function and create in this function the stored procedure using context.Database.ExecuteSqlCommand(...).
Use Entity Framework migrations for the creation of stored procedures.
The first method is simpler. Whenever the database is created, the Seed is called and the stored procedure is created. However this method has the disadvantage that whenever the name or the type of the parameters of the stored procedure change, this is not detected until runtime.
The DbMigration method matches the parameters of the stored procedure using a lambda expression, so whenever the type or the name of the parameter changes, the compiler detects if the definition of the remote procedure matches the parameter.
I'll describe both methods. Both examples have the same simple Hello World! procedure and a big Merge procedure with a lot of parameters.
The definition of the merge statement is not really important. What it
does is that it checks if there is already a record matching several
properties, and if so it adds costs to the existing costs. If not it
creates a record and initializes the costs with costs. This is a
typical example where using linq statement and IQueryable wouldn't suffice.
Using linq, one would have to retrieve the record, update it and call
SaveChanges, with the problems (1) that in
the meantime someone else might have added a value and (2) it needs at
least two roundtrips. Hence the need for a stored procedure.
Method IDatabaseInitializer
In your project you create the entity classes and a class derived form DbContext with DbSet properties for the database tables you want to access.
For example:
public class UsageCosts
{
public int Id {get; set; }
public DateTime InvoicePeriod { get; set; }
public long CustomerContractId { get; set; }
public string TypeA { get; set; }
public string TypeB { get; set; }
public decimal VatValue { get; set; }
// the value to invoice
public decimal PurchaseCosts { get; set; }
public decimal RetailCosts { get; set; }
}
public class DemoContext : DbContext
{
public DemoContext(string nameOrConnectionString) : base(nameOrConnectionString) {}
public DbSet<UsageCosts> UsageCosts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// add entity framework fluent api statements here
}
}
Apart from your database classes, create a database initializer, It has a function Seed that will be called when the database is created.
internal class DataBaseInitializer : DropCreateDatabaseIfModelChanges<DemoContext>
{
protected override void Seed(DemoContext context)
{
base.Seed(context);
// create stored procedures here
this.CreateStoredProcedureHelloWorld(context)
this.CreateStoredProcedureUpdateUsageCosts(context)
}
Simple example that shows how to create a Stored Procedure (Hello World!)
private void CreateStoredProcedureHelloWorld(DemoContext context)
{
context.Database.ExecuteSqlCommand("create procedure HelloWorld as begin Select 'Hello World' end;");
}
Create a stored procedure with input parameters:
private void CreateStoredProcedureUpdateUsageCosts(DemoContext context)
{
var x = new StringBuilder();
x.AppendLine(#"create procedure updateusagecosts");
x.AppendLine(#"#InvoicePeriod datetime,");
x.AppendLine(#"#CustomerContractId bigint,");
x.AppendLine(#"#TypeA nvarChar(80),");
x.AppendLine(#"#TypeB nvarChar(80),");
x.AppendLine(#"#VatValue decimal(18, 2),");
x.AppendLine(#"#PurchaseCosts decimal(18, 2),");
x.AppendLine(#"#RetailCosts decimal(18, 2)");
x.AppendLine(#"as");
x.AppendLine(#"begin");
x.AppendLine(#"Merge [usagecosts]");
x.AppendLine(#"Using (Select #InvoicePeriod as invoicePeriod,");
x.AppendLine(#" #CustomerContractId as customercontractId,");
x.AppendLine(#" #TypeA as typeA,");
x.AppendLine(#" #TypeB as typeB,");
x.AppendLine(#" #VatValue as vatvalue)");
x.AppendLine(#" As tmp ");
x.AppendLine(#"On ([usagecosts].[invoiceperiod] = tmp.invoiceperiod");
x.AppendLine(#"AND [usagecosts].[customercontractId] = tmp.customercontractid");
x.AppendLine(#"AND [usagecosts].[typeA] = tmp.typeA");
x.AppendLine(#"AND [usagecosts].[typeB] = tmp.typeB");
x.AppendLine(#"AND [usagecosts].[vatvalue] = tmp.Vatvalue)");
x.AppendLine(#"When Matched Then ");
x.AppendLine(#" Update Set [usagecosts].[purchasecosts] = [usagecosts].[purchasecosts] + #purchasecosts,");
x.AppendLine(#" [usagecosts].[retailcosts] = [usagecosts].[retailcosts] + #retailcosts");
x.AppendLine(#"When Not Matched Then");
x.AppendLine(#" Insert (InvoicePeriod, CustomerContractId, typea, typeb, vatvalue, purchasecosts, retailcosts)");
x.AppendLine(#" Values (#invoiceperiod, #CustomerContractId, #TypeA, #TypeB, #VatValue, #PurchaseCosts, #RetailCosts);");
x.AppendLine(#"end");
context.Database.ExecuteSqlCommand(x.ToString());
}
}
The hello world example can be found here on StackOverflow
The method with the StringBuilder can also be found somewhere on StackOverflow, but alas I can't find it.
During creation of the database DatabaseInitializer.Seed(...) is called. Here the context is ordered to perform an SQL statement. This statement is a string.
That's why the compiler won't notice changes in the name or the type of the parameters of the functions.
DbMigration methods
For migrations see:
MSDN: Enabling Migrations
Creating and Calling Stored Procedure from Entity Framework 6 Code First
The idea is to let the visual studio package manager create a derived class of DbManager which has an Up() function. This function will be called whenever the database is migrated upwards to the version for the derived class.
Inside the Up() you can call the base class DbMigration.CreateStoredProcedure. The nice thing about this method would be that the translation from Entity type to parameters is done using delegates (with a lambda expression) and thus checked at compile time: do the properties still exist and do they have the correct type?
Alas it is not enough to construct the derived class from DbMigration, and call the Up() functions from within your Seed() function.
To make sure that the Up() function is called it is the easiest to let visual studio do this.
Create your project
Add Nuget package for entity framework
Create your entity classes and your DbContext with DbSet properties for the entity classes
In visual studio start the Nuget Package Manager Console via the Tools menu
Using the Nuget Package Manager Console enable migrations using the command Enable-Migrations
Using the Nuget Package Manager Console add one migration and give if a name, for instance InitialCreation using the command add-Migration InitialCreation
You'll notice that several classes are added to your project.
Configuration derived from DbMigratinConfiguration with a function Seed()
InitialCreation derived from DbMigration with a function Up() (and a function Down(). In this Up you'll see one or more CreateTable functions
If you still have a database seeder class as described in the previous example, and you use DataBase.SetInitializer to initialize it, then whenever the database needs to be re-created the various Up() and Seed() functions are called in the following order:
Constructor of Configuration
InitialCreation.Up()
DatabaseSeeder.Seed()
For some reason Configuration.Seed() is not called.
This gives us the opportunity to create the stored procedures in InitialCraeation.Up()
public override void Up()
{
CreateTable("dbo.UsageCosts",
c => new
{
Id = c.Int(nullable: false, identity: true),
InvoicePeriod = c.DateTime(nullable: false),
CustomerContractId = c.Long(nullable: false),
TypeA = c.String(),
TypeB = c.String(),
VatValue = c.Decimal(nullable: false, precision: 18, scale: 2),
PurchaseCosts = c.Decimal(nullable: false, precision: 18, scale: 2),
RetailCosts = c.Decimal(nullable: false, precision: 18, scale: 2),
})
.PrimaryKey(t => t.Id);
}
The "Hello World" Stored procedure is created as follows:
base.CreateStoredProcedure("dbo.HelloWorld3", "begin Select 'Hello World' end;");
The stored procedure with input parameters:
base.CreateStoredProcedure("dbo.update2", p => new
{
InvoicePeriod = p.DateTime(),
CustomerContractId = p.Long(),
TypeA = p.String(maxLength: 80),
TypeB = p.String(maxLength: 80),
VatValue = p.Decimal(10, 8),
PurchaseCosts = p.Decimal(10, 8),
RetailCosts = p.Decimal(10, 8),
},
#"begin
Merge [usagecosts]
Using (Select
#InvoicePeriod as invoicePeriod,
#CustomerContractId as customercontractId,
#TypeA as typeA,
#TypeB as typeB,
#VatValue as vatvalue)
As tmp
On ([usagecosts].[invoiceperiod] = tmp.invoiceperiod
AND [usagecosts].[customercontractId] = tmp.customercontractid
AND [usagecosts].[typeA] = tmp.typeA
AND [usagecosts].[typeB] = tmp.typeB
AND [usagecosts].[vatvalue] = tmp.Vatvalue)
When Matched Then
Update Set [usagecosts].[purchasecosts] = [usagecosts].[purchasecosts] + #purchasecosts, [usagecosts].[retailcosts] = [usagecosts].[retailcosts] + #retailcosts
When Not Matched Then
Insert (InvoicePeriod, CustomerContractId, typea, typeb, vatvalue, purchasecosts, retailcosts)
Values (#invoiceperiod, #CustomerContractId, #TypeA, #TypeB, #VatValue, #PurchaseCosts, #RetailCosts);
end;");
}
Remember the Down() method:
public override void Down()
{
this.DropStoredProcedure("dbo.update2");
}
For completeness: the remote procedure call
using (var dbContext = new DemoContext())
{
object[] functionParameters = new object[]
{
new SqlParameter(#"InvoicePeriod", usageCosts.InvoicePeriod),
new SqlParameter(#"CustomerContractId", usageCosts.CustomerContractId),
new SqlParameter(#"TypeA", usageCosts.TypeA),
new SqlParameter(#"TypeB", usageCosts.TypeB),
new SqlParameter(#"VatValue", usageCosts.VatValue),
new SqlParameter(#"PurchaseCosts", 20M),
new SqlParameter(#"RetailCosts", 30M),
};
string sqlCommand = String.Format(#"Exec {0} #InvoicePeriod, #CustomerContractId, #TypeA, #TypeB, #VatValue, #PurchaseCosts, #RetailCosts", functionName);
dbContext.Database.ExecuteSqlCommand(sqlCommand, functionParameters);
dbContext.SaveChanges();
}
In my opinion it is best to put this in an extension method of the DbSet. Whenever the UsageCosts changes, the compiler can check for the names and the property types.

Custom logic in code-first EF6 SqlServerMigrationSqlGenerator not working

I am trying to set the default value SQL for a computed column called 'Duration' in a table 'dbo.Table1', in code-first Entity Framework 6 migration through SqlServerMigrationSqlGenerator class.
I tried setting this in Generate methods for AddColumnOperation as well as for CreateTableOperation. While the code under Generate method for column never fires, but the code under Generate table fires and throws an error saying the following. (the column EndTime is a column in table dbo.Table1 and so is StartTime)
The name "EndTime" is not permitted in this context. Valid expressions
are constants, constant expressions, and (in some contexts) variables.
Column names are not permitted.
Question: How could I make this work in either of the Generate methods in code below?
internal class CustomImplForSqlServerMigration: SqlServerMigrationSqlGenerator {
protected override void Generate(AlterColumnOperation alterColumnOperation) {
base.Generate(alterColumnOperation);
}
protected override void Generate(AddColumnOperation addColumnOperation) {
if (addColumnOperation.Table == "dbo.Table1" && addColumnOperation.Column.Name == "Duration") {
addColumnOperation.Column.DefaultValueSql = "(CAST(CAST(EndTime AS DATETIME) - CAST(StartTime AS DATETIME) AS TIME))";
}
base.Generate(addColumnOperation);
}
protected override void Generate(CreateTableOperation createTableOperation) {
if (createTableOperation.Name == "dbo.Table1") {
foreach(ColumnModel cm in createTableOperation.Columns) {
if (cm.Name == "Duration") {
cm.DefaultValueSql = "(CAST(CAST(EndTime AS DATETIME) - CAST(StartTime AS DATETIME) AS TIME))";
}
}
}
base.Generate(createTableOperation);
}
}
UPDATE 1:
I used another simple approach to add my custom logic for modifying database objects using ExecuteSqlCommand. Just follow the steps below to use this in your situation.
come up with the custom script for modifying or creating a database object
execute a command in Seed method for every custom script
make sure that the ExecuteSqlCommand statement is placed at end of Seed method and also context.SaveChanges( ) method is called before the code for custom scripts in case there is a dependency on seed data
protected override void Seed(EfCodeFirst.ShiftsDb context)
{
//Write your seed data statements
//call SaveChanges in case your custom script depends
//on some seed data
context.SaveChanges();
//include your custom scripts like ALTER TABLE
//or CREATE PROCEDURE or anything else
//use a ExecuteSqlCommand for every custom script
context.Database.ExecuteSqlCommand(#"ALTER TABLE ShiftTypes DROP COLUMN Duration;
ALTER TABLE TABLE1 ADD Duration AS (CAST(CAST(EndTime AS DATETIME) -
CAST(StartTime AS DATETIME) AS TIME)); ");
}
This is not a limitation of EF but of the database itself - you cannot refer to other columns within Default value specification. What I would recommend you instead is to write stored procedure for inserting new Table1 entities and then map with fluent api according to article.