简体   繁体   中英

EF6: where to create stored procedures?

I'm using Entity Framework 6 Code First. To use My database I need a stored procedure. Whenever the database is created this stored procedure needs to be created.

Edit: the reason for a stored procedure is written at the end of this post.

From Stackoverflow I know how to create a stored procedure and how to call it. My question is where do I create it?

My first instinct was in DbContext.OnModelCreating . This function is called whenever the model is created. In this function you'll tell the modelbuilder what the model should look like.

Alas, in this function I cannot use the DbContext yet. There are functions to override Insert / Update / Delete procedures, but I need a different procedure.

I consider to add the function in the Seed function of DropCreateDataBaseIfModelChanges, but that would inhibit users of my DbContext to Seed the database with their own desired data.

DbMigrations seems to be developed to migrate an existing database to a newer one. Not to be used when the first version of a database is created.

So where to create the stored procedures?


Edit. The reason I need a stored procedure is the following

My service receives notices that a customer spent money on something today. I need to remember the day totals per customer.

  • If the customer hasn't spent anything on date , which is if no record with (customerId, date) exists, insert a record with the spent value
  • It the customer already spent something on datae , which is if a record with (customerId, date) already exists, add the spent value to the value in the record

Something like this:

public void InsertOrAdd(int customerId, DateTime date, decimal value)
{
    using (var dbContext = CreateContext())
    {
        var retrievedRecord = dbContext.Find(...)
        if (retrievedRecord == null)
        {
            InsertRecord(customerId, date, value);
        }
        else
        {
            retrievedRecord.Value += value;
            dbContext.SaveChanges();
        }
    }
}

The problem is that after my Find, someone else might already have Inserted a record. In that case I should have added the value. Instead there will be two records. Or while I am processing an existing record and adding the value, someone else might process the same existing record. Therefore the adding of the value should be done inside the database with a statement like

update [CustomerSpends]
Set [SpentValue] = [SpentValue] + @Value
Where [CustomerId] = @CustomerId and [SpentDate] = @SpentDate

2nd edit As * Ivan Yuriev* already mentioned the above could be solved using a transaction. However I still would have the problem about adding the spends.

If this would be a 3 step process, like Ivan suggests, it would be like:

  • Fetch the existing record
  • Add spentValue to the total value in the record
  • SaveChanges

This has the disadvantage that I always need two round trips to the database. Besides I get a sequence of customer spends. If any of them fail processing, everything needs to be rolled back. Even when using transaction it is possible there are race conditions

The highest Transaction Isolation level (MSDN) is read committed.

READ COMMITTED Specifies that statements cannot read data that has been modified but not committed by other transactions. This prevents dirty reads. Data can be changed by other transactions between individual statements within the current transaction, resulting in non-repeatable reads or phantom data.

When using the three step procedure defined above, I fetch record X, with a total spent value of $ 5.00. While I am adding $ 3 to the total, someone else can also fetch record X, because it has not been modified yet. The other one receives the record with the total of $ 5. After my addition the total value became $ 8. I update the value using SaveChanges and continue with other records. After a while I commit my changes. The other process who still has the record with $ 5, should add $2, ending with a value of $ 7, while the end result should have been $ 10

Using transactions can't prevent others to read the data. Therefore the fetch - add value - update should be done in one SQL statement

Best way to manage by creating a blank migration and Add your stored procedure in Up method and delete that stored procedure in Down Method .

This will work like.

1) Add blank migration by running command on Package Manger Console

add-migration 'SomeName' -IgnoreChanges

It will create the migration file

2) Now Add your stored procedure in Up and Delete the stored procedure in Down

public partial class SomeName: DbMigration
    {
        public override void Up()
        {
            Sql(@"Add Command For StoredProcedure");

        }

        public override void Down()
        {
            Sql(@"Drop Command for Stored Procedure")
        }
    } 

Adding Drop in Down method is equally important.

Nowhere. I found code first migrations to be extremely limited and something only people that lack a deep understanding of SQL server concepts and capabilities would feel comfortable using.

The way I have gone is:

  • I maintain a master database and sync the schema to a schema project.
  • Regularly generate delta scripts which I then manually maintain.
  • I have a library that applies delta scripts as needed based on a version number of the database which is stored in an extended property.

Reasons not to use migrations are simple - you end up having to submit way too many SQL scripts anyway. Migrations do not even handle trivial cases such as proper fully configured indices (filtered indices), have no idea of stored procedures, triggers and views, are unable to deal with partitioned tables, are unable to deal efficiently with data migrations that may require temporary tables to store data while the table layout is changed. Because I do not deal with trivial databases I require nearly all of those features pretty much daily.

Telling the full truth - I never found code first even an efficient approach. It is way too error prone. The only thing worse is db first with EF tools - mostly because the edmx designer is as rotten as it gets, totally unable to deal with updates. Third party editors are available, though, and they WORK.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM