简体   繁体   English

如何使用Entity Framework Core将属性设置为“ Identity”或自动递增的列?

[英]How to set a property to be “Identity” or auto-incremented column with Entity Framework Core?

In a new WPF project that I am writing using c#, I want to attempt on using Entity Framework Core to interact with my SQL Server database. 在我使用c#编写的新WPF项目中,我想尝试使用Entity Framework Core与我的SQL Server数据库进行交互。

Every time I try to add model to my context, I get the following error 每次尝试将模型添加到上下文中时,都会出现以下错误

Cannot insert explicit value for identity column in table 'Orders' when IDENTITY_INSERT is set to OFF. 当IDENTITY_INSERT设置为OFF时,无法为表“订单”中的标识列插入显式值。

I am using a Repository and UnitOfWork which wraps the Entity Framework Core methods to perform the work needed. 我使用的是RepositoryUnitOfWork ,它包装了Entity Framework Core方法来执行所需的工作。

But at its simplest, I am executing the following code 但最简单地说,我正在执行以下代码

var order = new Order();
order.Title = "some";
....
Context.Orders.Add(order);
Context.SaveChanges();

Here is my model 这是我的模特

public class Order
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Int64 Id { get; set; }

    public string Status { get; set; }
    public int? CustomerId { get; set; }
    public DateTime? Birthdate { get; set; }
    public int UtcOffset { get; set; }\
    public DateTime CreatedAt { get; set; }
    public DateTime? UpdatedAt { get; set; }
    public int? UpdatedBy { get; set; }

    [ForeignKey(nameof(Creator))]
    public int CreatedBy { get; set; }

    public Order()
    {
        CreatedAt = DateTime.UtcNow;
    }

    public virtual User Creator { get; set; }
    public virtual Customer Customer { get; set; }
}

What could be causing this problem? 是什么导致此问题?

Updated 更新

Here is how my table is created 这是我的表格的创建方式

CREATE TABLE [dbo].[Orders](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Status] [varchar](50) NOT NULL,
    [CustomerId] [int] NULL,
    [Birthdate] [datetime] NULL,
    [CreatedBy] [int] NOT NULL,
    [CreatedAt] [datetime] NOT NULL,
    [UpdatedBy] [int] NULL,
    [UpdatedAt] [datetime] NULL,
    [UtcOffset] [int] NOT NULL,
 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Here is the method that creates the Order model 这是创建Order模型的方法

public Order Create(int? customerId, DateTime? birthdate)
{
    var order = new Order();
    order.CustomerId = customerId;
    order.Birthdate = birthdate;
    order.Status = OrderStatus.Sold.ToString();
    order.CreatedBy = Passport.Identity.Id;

    var updatedOrder = Orders.Add(order);
    Orders.Save();

    return updatedOrder;
}

Here is my repository implementation 这是我的存储库实现

public class EntityRepository<TEntity, TKeyType> : IRepository<TEntity, TKeyType>
    where TEntity : class
    where TKeyType : struct
{
    protected readonly DbContext Context;
    protected readonly DbSet<TEntity> DbSet;

    public EntityRepository(DbContext context)
    {
        Context = context;
        DbSet = context.Set<TEntity>();
    }

    public TEntity Get(TKeyType id)
    {
        return DbSet.Find(id);
    }

    public IEnumerable<TEntity> GetAll()
    {
        return DbSet.ToList();
    }

    public bool Any(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.Any(predicate);
    }

    public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.Where(predicate);
    }

    public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.SingleOrDefault(predicate);
    }

    public virtual TEntity Add(TEntity entity)
    {
        DbSet.Add(entity);

        return entity;
    }

    public virtual IEnumerable<TEntity> AddRange(IEnumerable<TEntity> entities)
    {
        DbSet.AddRange(entities);

        return entities;
    }

    public void Remove(TEntity entity)
    {
        DbSet.Remove(entity);
    }

    public void RemoveRange(IEnumerable<TEntity> entities)
    {
        DbSet.RemoveRange(entities);
    }

    public void Update(TEntity entity)
    {
        DbSet.Attach(entity);
        var record = Context.Entry(entity);
        record.State = EntityState.Modified;
    }

    public IQueryable<TEntity> Query()
    {
        return DbSet;
    }

    public void Save()
    {
        Context.SaveChanges();
    }
}

public class EntityRepository<TEntity> : EntityRepository<TEntity, int>
   where TEntity : class
{
    public EntityRepository(DbContext context)
        : base(context)
    {
    }
}

Additionally, this question is not a duplicate on Entity Framework error: Cannot insert explicit value for identity column in table because I am decorating my Id property with the [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] . 此外,此问题不是有关Entity Framework错误的重复项:无法为表中的Identity列插入显式值,因为我正在使用[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]装饰我的Id属性。 Moreover, I am using database first approach, so I created my database manually using MSSMS 此外,我使用数据库优先方法,因此我使用MSSMS手动创建了数据库

This issue could be one of the following two scenarios 此问题可能是以下两种情况之一

First, you could be unintentionally setting the Id property using auto-mapping tool. 首先,您可能会使用自动映射工具无意中设置Id属性。 If your using auto-mapping utility like AutoMapper , ValueInjecter or OoMapper make sure you configure your mapping to ignore the the Id property when your destination object is the Order model. 如果使用诸如AutoMapperValueInjecterOoMapper类的自动映射实用程序, ValueInjecter确保将目标对象配置为Order模型时,将映射配置为忽略Id属性。 For Example, with AutoMapper, use the following syntax to configure auto-mapper to not may any value to the Id property. 例如,对于AutoMapper,使用以下语法将auto-mapper配置为对Id属性不施加任何值。

expression.CreateMap<OrderViewModel, Order>()
          .ForMember(src => src.Id, opts => opts.Ignore());

Second scenario, what you are seeing may be related to an EntityFrameworkCore Bug . 第二种情况,您所看到的可能与EntityFrameworkCore Bug有关 Instead on using the data-annotation (ie [DatabaseGenerated(DatabaseGeneratedOption.Identity)] ), try defining the id property as identity using fluent in the OnModelCreating of you context class like so 代替使用数据注释(即[DatabaseGenerated(DatabaseGeneratedOption.Identity)] ),尝试使用Context类的OnModelCreating的流利方法将id属性定义为标识

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    // Here we identify the Id property to be set to Identity
    // Also, we use change the PropertySaveBehavior on the same
    // property to ignore the values 
    modelBuilder.Entity(modelType)
                .Property(key.Name)
                .UseSqlServerIdentityColumn()
                .Metadata.BeforeSaveBehavior = PropertySaveBehavior.Ignore;
}

The above code should also solve the first scenario if that is indeed your problem. 如果确实是您的问题,以上代码也应解决第一种情况。 the line Metadata.BeforeSaveBehavior = PropertySaveBehavior.Ignore; Metadata.BeforeSaveBehavior = PropertySaveBehavior.Ignore; from above is telling EntityCore to simply not include the Id column in the insert statement. 从上面开始告诉EntityCore在插入语句中根本不包括Id列。 So even if you're mapper maps a value to the Id property that incorrectly mapped value would be excluded from the insert statement. 因此,即使您是映射器,也将一个值映射到Id属性,但错误映射的值将从插入语句中排除。

Furthermore, you could use reflection to set the "Id" property on all your dbsets to make your workflow little more robust or if you have many dbsets you won't have to add them one at a time. 此外,您可以使用反射在所有dbset上设置“ Id”属性,以使工作流更加健壮,或者如果您有许多dbset,则不必一次添加一个。 Here is an example of how to use reflection to configure your models to make the column that is called Id identity. 这是一个如何使用反射来配置模型以创建称为Id身份的列的示例。

public class DataContext : DbContext
{
    public DbSet<Order> Orders { get; set; }
    public DbSet<User> Users { get; set; }
    // Here list any other DbSet...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // First we identify the model-types by examining the properties in the DbContext class
        // Here, I am assuming that your DbContext class is called "DataContext"
        var modelTypes = typeof(DataContext).GetProperties()
                         .Where(x => x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
                         .Select(x => x.PropertyType.GetGenericArguments().First())
                         .ToList();

        // Feel free to add any other possible types you may have defined your "Id" property with
        // Here I am assuming that only short, int, and bigint would be considered identity
        var identityTypes = new List<Type> { typeof(Int16), typeof(Int32), typeof(Int64) };

        foreach (Type modelType in modelTypes)
        {
            // Find the first property that is named "id" with the types defined in identityTypes collection
            var key = modelType.GetProperties()
                               .FirstOrDefault(x => x.Name.Equals("Id", StringComparison.CurrentCultureIgnoreCase) && identityTypes.Contains(x.PropertyType));

            // Once we know a matching property is found
            // We set the propery as Identity using UseSqlServerIdentityColumn() method
            if (key == null)
            {
                continue;
            }

            // Here we identify the Id property to be set to Identity
            // Also, we use change the PropertySaveBehavior on the same
            // property to ignore the values 
            modelBuilder.Entity(modelType)
                        .Property(key.Name)
                        .UseSqlServerIdentityColumn()
                        .Metadata.BeforeSaveBehavior = PropertySaveBehavior.Ignore;
        }
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (optionsBuilder.IsConfigured)
        {
            return;
        }

        optionsBuilder.UseSqlServer(ConfigurationManager.ConnectionStrings["ConnectionName"]);
    }
}

In my case I had to add entity.Property(e => e.TranId).ValueGeneratedOnAdd(); 在我的情况下,我必须添加entity.Property(e => e.TranId).ValueGeneratedOnAdd(); to my context file. 到我的上下文文件。

modelBuilder.Entity<PersonalTrades>(entity =>
{
     entity.Property(e => e.TranId).ValueGeneratedOnAdd();
}

I was not updating the identity field(TranId) from Automapper. 我没有从Automapper更新身份字段(TranId)。 Finally the above change worked for me which, as the name specifies, generates the value for the identity field while inserting the record. 最终,上面的更改对我有用,正如名称所指,它在插入记录时为标识字段生成了值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 尽管将标识列设置为自动递增,但在向datagridview插入数据时出现NoNullAllowedException错误 - NoNullAllowedException error when inserting data to a datagridview despite identity column is set to be auto-incremented 如何设置自动递增属性的初始值(DatabaseGeneratedOption.Identity) - How to set initial value for auto incremented property (DatabaseGeneratedOption.Identity) 实体框架是否接受/添加未自动递增的int PK的数据? - Does Entity framework accept/add data of an int PK that is not auto-incremented? 如何在Entity Framework代码优先创建模型时隐藏自动递增的属性 - How to hide auto incremented property while creating model in Entity Framework code-first 如何使用 Entity Framework Core 配置标识列? - How to configure an Identity column using Entity Framework Core? 将 object 映射为实体框架核心中的标识列 - Mapping object as identity column in entity framework core 当 IDENTITY_INSERT 设置为 OFF 时,无法为标识列插入显式值。 (实体框架核心) - Cannot insert explicit value for identity column when IDENTITY_INSERT is set to OFF. (Entity Framework Core) 实体框架中的自动递增列 - Automatically incremented column in Entity Framework 如何首先获取实体框架代码中的自动递增值 - How to get the auto incremented value in entity framework code first 带有 Prism MVVM 的实体框架:如何将自动递增的 id 获取到 CollectionViewSource? - Entity Framework with Prism MVVM: How to get an auto incremented id to a CollectionViewSource?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM