0

When saving a new Entity to the DB using EF5 saves the record with ID 0. The database has an identity column starting with 1 and counting 1.

When I recreate the specific table, all problems are gone. But I don't want to recreate tables in my production environment the whole time.

Where as I'm not an SQL expert. Is there some kind of method to solve this problem? We are working with MSSQL2005 (SQL2012 is ordered, but until it's here I can't make use of it and there wil be some time migrating old databases to the new one).

//Database creation script
    /* [Queue].[DirectDebitOrder] */
CREATE TABLE [Queue].[BookingExportTask](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [DirectDebitRequestID] [bigint] NOT NULL,
    [Type] [nvarchar](50) NOT NULL,
    [Step] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Queue_BookingExportTask] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY];


/* Foreign Keys */
ALTER TABLE [Queue].[BookingExportTask]  WITH CHECK ADD CONSTRAINT [FK_Queue_BookingExportTask_Queue_BookingExportTaskStepEnum] FOREIGN KEY([Step]) REFERENCES [Queue].[BookingExportTaskStepEnum] ([Name]);
ALTER TABLE [Queue].[BookingExportTask] CHECK CONSTRAINT [FK_Queue_BookingExportTask_Queue_BookingExportTaskStepEnum];

ALTER TABLE [Queue].[BookingExportTask]  WITH CHECK ADD CONSTRAINT [FK_Queue_BookingExportTask_Queue_BookingExportTaskTypeEnum] FOREIGN KEY([Type]) REFERENCES [Queue].[BookingExportTaskTypeEnum] ([Name]);
ALTER TABLE [Queue].[BookingExportTask] CHECK CONSTRAINT [FK_Queue_BookingExportTask_Queue_BookingExportTaskTypeEnum];

ALTER TABLE [Queue].[BookingExportTask]  WITH CHECK ADD CONSTRAINT [FK_Queue_BookingExportTask_Finance_DirectDebitRequest] FOREIGN KEY([DirectDebitRequestID]) REFERENCES [Finance].[DirectDebitRequest] ([ID])
ALTER TABLE [Queue].[BookingExportTask] CHECK CONSTRAINT [FK_Queue_BookingExportTask_Finance_DirectDebitRequest];



//C# Code for saving Entities
    /// <summary>
    /// Saves the data in the <see cref="ILoadableBookingExportTask"/> to the <paramref name="entityContext"/>
    /// </summary>
    /// <param name="entityContext">The <see cref="DBContext"/> context to save the <see cref="ILoadableBookingExportTask"/> to.</param>
    /// <param name="source">The <see cref="ILoadableBookingExportTask"/> to save.</param>
    /// <returns>The <see cref="BookingExportTask"/> that holds the actually stored values.</returns>
    public static BookingExportTask Save(this DBContext entityContext, ILoadableBookingExportTask source)
    {
        if (source == null) { return null; }
        return source.SaveTo(entityContext.BookingExportTask.EnsureExists(source), entityContext);
    }

    /// <summary>
    /// Helper. Finds existing instance or creates a new instance of <typeparamref name="TEntityType"/> which is immediately added to the <see cref="dbSet{TEntityType}"/> as well. 
    /// </summary>
    /// <typeparam name="TEntityType">The type of the entity to check.</typeparam>
    /// <param name="dbSet">The database collection for the entity.</param>
    /// <param name="entity">The <see cref="IEntity"/> to find.</param>
    /// <returns>The existing or newly created <typeparamref name="TEntityType"/>.</returns>
    public static TEntityType EnsureExists<TEntityType>(this DbSet<TEntityType> dbSet, IEntity entity)
        where TEntityType : class, IEntity, new()
    {
        if (entity == null) { return default(TEntityType); }
        return dbSet.EnsureExists(entity.ID);

    }

    /// <summary>
    /// Helper. Finds existing instance or creates a new instance of <typeparamref name="TEntityType"/> which is immediately added to the <see cref="dbSet{TEntityType}"/> as well. 
    /// </summary>
    /// <typeparam name="TEntityType">The type of the entity to check.</typeparam>
    /// <param name="dbSet">The collection to check for existance of the entity.</param>
    /// <param name="id">The ID of the entity to find.</param>
    /// <returns>The existing or newly created <typeparamref name="TEntityType"/>.</returns>
    public static TEntityType EnsureExists<TEntityType>(this DbSet<TEntityType> dbSet, long id)
        where TEntityType : class, IEntity, new()
    {
        return dbSet.GetByID(id) ?? dbSet.New();
    }


    /// <summary>
    /// Saves the data in the <see cref="ILoadableBookingExportTask"/> to the <paramref name="entityContext"/>
    /// </summary>
    /// <param name="source">The <see cref="ILoadableBookingExportTask"/> to save.</param>
    /// <param name="target">The <see cref="BookingExportTask"/> to save the data to.</param>
    /// <param name="entityContext">The <see cref="DBContext"/> context to save the <see cref="ILoadableBookingExportTask"/> to.</param>
    /// <returns>The <see cref="BookingExportTask"/> that holds the actually stored values.</returns>
    private static BookingExportTask SaveTo(this ILoadableBookingExportTask source, BookingExportTask target, DBContext entityContext)
    {
        if (source == null) { return target; }
        Debug.WriteLine(string.Format("Saving ILoadableBookingExportTask {0} to DBContext.", target.ID));

        /* copy all fields from source to target. Use SyncLinkedObject for all linked entities to ensure the saving of possible changes to the foreign entities.
         * target.Field = target.Field;
         * target.RelatedEntity = entityContext.SyncLinkedObject(source.RelatedEntity, source.RelatedEntity_ForeignKey);
         * 
         * Note: It's useless to overwrite the Identity property - like target.ID. Just skip it.
         */

        return target;
    }

C# Code in short:

  1. Check if entity already exists (it's a new entity so NO)
  2. Create new entity in the specific DBSet in the context
  3. Pass the values of the business entity to the dbset-entity (without the ID)
  4. Save the context

This is al working fine until. The DB is changed or restored. After that tables with Identity columns have to be recreated. If i don't recreate the tables the first NEW entity is saved to ID 0. With the ensureExists method al new entities from that moment on are overwriting the existing entity, which is logical because of the ID 0.

Problem is that the identity columns are not recognized from the EF.

If this problem arises I monitored the DB with the SQL Profiler. I pasted the incomming query found by the profiler in the SQL management studio where I get my ID. So it seems the DB is fine. Code isn't changed and worked before (before the restore of the DB). Only solution I found so far is recreating the tables with identity columns. After that the C# code is working again without adaption.

Luuk Krijnen
  • 1,180
  • 3
  • 14
  • 37
  • code you post please some code? The structure of your table and the code you use to save your entity? Thanks – Christos Nov 15 '13 at 09:25

1 Answers1

0

Are you absolutely sure that the table does have an IDENTITY column? The reason I ask is that if an ID is created without IDENTITY, Entity Framework does not fix it if you then try to switch it on through data annotations or the FluentAPI. Same problem if it starts as IDENTITY (which is the default for integer ID fields) and you want to switch it off.

Dropping the table and recreating it will fix it.

If it's still in development and the table is empty you could just change it in the database.

It depends on your scenario. I would think it would be pretty rare to want to change the column after it's got to production but if you have to, you can do it by Switching Identity On/Off With A Custom Migration Operation

Reference:

Identity problem in Entity Framework

Community
  • 1
  • 1
Colin
  • 22,328
  • 17
  • 103
  • 197