Как задать для свойства значение «Идентичность» или автоматически увеличиваемый столбец с помощью Entity Framework Core? - PullRequest
0 голосов
/ 21 мая 2018

В новом проекте WPF, который я пишу с использованием c #, я хочу попытаться использовать Entity Framework Core для взаимодействия с моей базой данных SQL Server.

Каждый раз, когда я пытаюсь добавитьВ зависимости от модели, я получаю следующую ошибку

Невозможно вставить явное значение для столбца идентификаторов в таблице «Заказы», ​​если для параметра IDENTITY_INSERT установлено значение OFF.

Я используюRepository и UnitOfWork, которые оборачивают методы Entity Framework Core для выполнения необходимой работы.

Но, проще всего, я выполняю следующий код

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

Вот моя модель

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; }
}

Что может быть причиной этой проблемы?

Обновлено

Вот как создается моя таблица

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]

Вот метод, который создает 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;
}

Вот моя реализация репозитория

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)
    {
    }
}

Кроме того, этот вопрос не является дубликатом для Ошибка Entity Framework: Невозможно вставить явное значение для столбца идентификаторов в таблице , поскольку я украшаю свое свойство Idс [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)].Более того, я использую базу данных в первую очередь, поэтому я создал свою базу данных вручную, используя MSSMS

Ответы [ 2 ]

0 голосов
/ 01 апреля 2019

В моем случае мне пришлось добавить entity.Property(e => e.TranId).ValueGeneratedOnAdd(); в мой файл контекста.

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

Я не обновлял поле идентификации (TranId) из Automapper.Наконец, указанное выше изменение сработало для меня, которое, как следует из названия, генерирует значение для поля идентификатора при вставке записи.

0 голосов
/ 21 мая 2018

Это может быть один из следующих двух сценариев

Во-первых, вы можете непреднамеренно установить свойство Id с помощью инструмента автоматического сопоставления.Если вы используете утилиту автоматического сопоставления, такую ​​как AutoMapper, ValueInjecter или OoMapper, убедитесь, что вы настроили свое сопоставление так, чтобы оно игнорировало свойство Id, если вашим целевым объектом является модель Order.Например, в AutoMapper используйте следующий синтаксис, чтобы настроить автоматическое сопоставление, чтобы оно не могло иметь никакого значения для свойства Id.

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

Второй сценарий, который вы видите, может быть связан с EntityFrameworkCore Bug .Вместо использования аннотации данных (т. Е. [DatabaseGenerated(DatabaseGeneratedOption.Identity)]), попробуйте определить свойство id как идентификатор, используя свободный язык в OnModelCreating вашего контекстного класса, например:

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;
}

Приведенный выше код также должен решитьПервый сценарий, если это действительно ваша проблема.строка Metadata.BeforeSaveBehavior = PropertySaveBehavior.Ignore; сверху говорит EntityCore просто не включать столбец Id в оператор вставки.Таким образом, даже если вы сопоставляете значение со свойством Id, это неверно сопоставленное значение будет исключено из оператора вставки.

Кроме того, вы можете использовать отражение, чтобы установить свойство "Id" на всех вашихdbsets, чтобы сделать ваш рабочий процесс немного более устойчивым, или если у вас много dbsets, вам не нужно будет добавлять их по одному.Вот пример того, как использовать отражение, чтобы настроить ваши модели для создания столбца, который называется Id identity.

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"]);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...