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

Jun*_*ior 6 c# wpf entity-framework entity-framework-core

在我使用c#编写的新WPF项目中,我想尝试使用Entity Framework Core与我的SQL Server数据库进行交互。

每次尝试将模型添加到上下文中时,都会出现以下错误

当IDENTITY_INSERT设置为OFF时,无法为表“订单”中的标识列插入显式值。

我正在使用RepositoryUnitOfWork包装实体框架核心方法来执行所需的工作。

但最简单地说,我正在执行以下代码

var order = new Order();
order.Title = "some";
....
Context.Orders.Add(order);
Context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

这是我的模特

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; }
}
Run Code Online (Sandbox Code Playgroud)

是什么导致此问题?

更新

这是我的表格的创建方式

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]
Run Code Online (Sandbox Code Playgroud)

这是创建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;
}
Run Code Online (Sandbox Code Playgroud)

这是我的存储库实现

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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,此问题并非对Entity Framework错误的重复:无法在表中为Identity列插入显式值,因为我正在用I装饰Id属性[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]。此外,我使用数据库优先方法,因此我使用MSSMS手动创建了数据库

Maj*_*yek 5

此问题可能是以下两种情况之一

首先,您可能无意中Id使用了自动映射工具来设置属性。如果您使用的是自动映射实用程序,例如AutoMapperValueInjecterOoMapper确保将映射配置为Id当目标对象是Order模型时忽略该属性。例如,对于自动映射器,使用以下语法将自动映射器配置为该Id属性不能包含任何值。

expression.CreateMap<OrderViewModel, Order>()
          .ForMember(src => src.Id, opts => opts.Ignore());
Run Code Online (Sandbox Code Playgroud)

第二种情况,您所看到的可能与EntityFrameworkCore Bug有关。代替使用数据注释(即[DatabaseGenerated(DatabaseGeneratedOption.Identity)]),尝试使用OnModelCreating您上下文类的fluent将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;
}
Run Code Online (Sandbox Code Playgroud)

如果确实是您的问题,以上代码也应解决第一种情况。Metadata.BeforeSaveBehavior = PropertySaveBehavior.Ignore;上面的行告诉EntityCore根本不将Id列包括在insert语句中。因此,即使您是映射器,一个值映射到该Id属性,该错误映射的值也将从插入语句中排除。

此外,您可以使用反射在所有dbset上设置“ Id”属性,以使工作流更加健壮,或者如果您有许多dbset,则不必一次添加一个。这是一个如何使用反射来配置模型以创建称为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"]);
    }
}
Run Code Online (Sandbox Code Playgroud)


Sac*_*har 2

就我而言,我必须添加entity.Property(e => e.TranId).ValueGeneratedOnAdd();到我的上下文文件中。

modelBuilder.Entity<PersonalTrades>(entity =>
{
     entity.Property(e => e.TranId).ValueGeneratedOnAdd();
}
Run Code Online (Sandbox Code Playgroud)

我没有从 Automapper 更新身份字段(TranId)。最后,上述更改对我有用,正如名称所示,它在插入记录时生成身份字段的值。