9

I have a class called Offer as follows:

public class Offer
{
    public Guid Id { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int OfferNo { get; set; }

    public OfferType OfferType { get; set; }
    public DateTimeOffset DateAdded { get; private set; }
    public DateTimeOffset DateEdited { get; set; }
    public bool IsActive { get; set; }
}

I am using Id property as my PK obviously. But I also need to display the Id of offers (as users will search for offers using this value) and the Guid property is too long for that.

Thus, I tried to use DatabaseGeneratedOption.Identity to auto increment the integer column OfferNo, but I can not set an initial value to increment on. I inserted a dummy entry and then tried to set OfferNo to something else than 1, but received the following exception:

Modifying a column with the 'Identity' pattern is not supported. Column: 'OfferNo'. Table: 'CodeFirstDatabaseSchema.Offer'.

I would like to set the initial value of this auto incremented column using code-first. An alternative solution will also be appreciated.

Ferit Buyukkececi
  • 2,665
  • 2
  • 20
  • 23
  • Might be related: http://stackoverflow.com/questions/11732102/how-do-i-set-identity-seed-on-an-id-column-using-entity-framework-4-code-first-w – David Brabant May 27 '14 at 11:20
  • @DavidBrabant Unfortunately this is not a code-first approach and I know so little about SQL. – Ferit Buyukkececi May 27 '14 at 11:22
  • Why do you need the `Guid` if you're also creating an identity column? – DavidG May 27 '14 at 11:28
  • @DavidG I use Guid as PKs all around in my application. In my application, users should be able to search for offers using an Id and I can't expect them to type GUIDs in a textbox. Using an integer PK will not solve the problem I mentioned above, because I don't want to have Ids with couple of digits. – Ferit Buyukkececi May 27 '14 at 14:40
  • @FeritBuyukkececi I've fully rewritten my answer. Please, let me know if I can make it even more clear – JotaBe May 27 '14 at 16:07

2 Answers2

13

Just found a solution for this matter. You can simply call a Sql() method in your Up() method.

public override void Up()
    {
        CreateTable(
            "Offers",
            c => new
                {
                    OfferNo = c.Int(nullable: false, identity: true),
                    ...
                })
            .PrimaryKey(t => t.OfferNo);
        Sql("DBCC CHECKIDENT ('Offers', RESEED, 100);");
    }
Michael Bar
  • 160
  • 1
  • 6
  • Thanks, I'll try that for sure. But at the moment, I don't have an override for `Up` method in migrations. Do you think I can reseed Offers table right after initialization? – Ferit Buyukkececi Oct 15 '14 at 13:36
  • Seems to be the simplest solution by far – BCA Feb 10 '17 at 21:09
  • I can't seem to make this work in my Azure, only on my `(localdb)\v11.0` – bkwdesign Aug 28 '17 at 20:12
  • @Simon_Weaver- I wanted some entity tables in my Azure SQL Database to start with an `Id` set to `0`. I never found a repeatable way to do this via my "Publish.." button in Visual Studio when refreshing my Web API. I gave up on the idea. It wasn't worth my effort. I vaguely recall that it sometimes worked, but, for some reason wasn't repeatable/reliable – bkwdesign Dec 05 '17 at 17:39
  • Hi, What is the rollback query ? – Adel Mourad Oct 09 '18 at 18:46
  • @AdelMourad - I guess DropTable() is a goog candidate, but it will depend on your implementation. If you created the table in Up you should drop it in Down. If you just need to bump the Autoincrement in Up you can Delete all the records above this Id... Again, it might depend on the scenario you have... – Michael Bar Oct 11 '18 at 12:25
  • Thanks for replying but i was wondering if i can just set the auto increment back to start from 0 when i do not have data in the table, maybe this will work i will test it later DBCC CHECKIDENT ('Offers', RESEED, 0); ... – Adel Mourad Oct 11 '18 at 16:40
11

According to the comment, "but starting from a seed value that I provide instead of 1", you can use an identity column, and customize your database initialization or migration to set the seed of your identity column.

The T-SQL command to do this is:

DBCC CHECKIDENT ('Offer', RESEED, 123);

Note that the next inserted value is not 123, but 123 + increment (124 if default increment of 1).

You can also use a column with the DatabaseGeneratedOption.Computed and a sequence as default value for your field (if you're using a recent SQL server version). When you create a sequence, you can specify the initial value and increment:

CREATE SEQUENCE OfferNoSeq
START WITH 1 -- Initial Value
INCREMENT BY 1 -- Increment

An attach this sequence as a default for the OfferNo column like this:

ALTER TABLE Offer ADD CONSTRAINT OfferNoSeq 
DEFAULT (NEXT VALUE FOR OfferNoSeq)  FOR OfferNo;

There is no direct way to implement this in Code First. So, for using any of these options, you need to

  • customize the DB initialization This is done by implementing your own database initializer class and execute the desired SQL commands from the Seed methods (look for the implementation of public class MyInitializer in the linked article)
  • or to customize a migration: you can execute any SQL Command in the Up() or Down() method of your migration, as shown in the linked SO answer

Please, if you use SEQUENCE, please, read this: EF6 does not work with primary key from sequence.

JotaBe
  • 38,030
  • 8
  • 98
  • 117
  • Thank you for your detailed answer, I learnt more than I requested :) I actually applied my own solution which is using the auto incremented field that starts from 1 and add/subtract a fixed number in the application to produce the seeded value. I also tried the 'customizing the DB initialization' and it worked. Thanks a lot! – Ferit Buyukkececi Jun 01 '14 at 04:41
  • putting a `DBCC CHECKIDENT` in my initializer.. SIMPLE and PERFECT. – bkwdesign Aug 23 '17 at 21:12
  • RE: the note about the next inserted value, I'm finding that if I'm initializing my `(localdb)\v11.0` that the next value is not always incremented. E.g. if I set my `Locations` table to have an `id` seeded with `0`, the initial record that I insert does not have a `1`, but actually starts with a `0` – bkwdesign Aug 28 '17 at 19:49