简体   繁体   English

在循环内运行时添加实体的 EF 核心错误

[英]EF core errors to add entities when running within a cycle

I have been dealing with some EF core issues that I cannot identify the source and although I can get around them it's resulting in inefficient code in my opinion.我一直在处理一些我无法确定来源的 EF 核心问题,尽管我可以解决它们,但在我看来这会导致代码效率低下。 Let me explain, I am calling the below procedure within a cycle after a POST form submission:让我解释一下,我在 POST 表单提交后的一个周期内调用以下过程:

if (form.Inventory != null && form.Inventory.Any())
            {
                foreach (var pi in form.Inventory)
                {
                    var b = await _branchesActions.GetBranchesByIdNoTrackingAsync(pi.Branchesid).ConfigureAwait(false);
                    await _productActions.ModifyProductInventory(product, user, b, pi.ProductQty, "Producto agregado a sistema",
                        product.Productsid, "Create", "Alta de producto").ConfigureAwait(false);
                }
            }

this is the below code for the functions it is calling:这是它正在调用的函数的以下代码:

public async Task<Branches> GetBranchesByIdNoTrackingAsync(int id)
    {
        return await db.Branches.Where(b => b.IsActive && b.Branchesid == id)
            .AsNoTracking()
            .SingleOrDefaultAsync()
            .ConfigureAwait(false);
    }

ModifyProductInventory:修改产品库存:

public async Task<bool> ModifyProductInventory(Products product, Users user, Branches branch, int qty, string action, int relid, string actionname, string actiontype)
    {
        try
        {
            var inventory = await db.BranchesInventory
                .Where(bi => bi.Branch.Branchesid == branch.Branchesid
                && bi.Product.Productsid == product.Productsid)
                .SingleOrDefaultAsync()
                .ConfigureAwait(false);
            if (inventory == null)
            {
                await InsertBranchInventory(qty, branch, product).ConfigureAwait(false);
            }
            else
            {
                if (!product.IsService)
                {
                    db.BranchesInventory.Update(inventory);
                    inventory.ProductQty += qty;
                    await db.SaveChangesAsync().ConfigureAwait(false);
                    db.Entry(inventory).State = EntityState.Detached;
                    db.Entry(branch).State = EntityState.Detached;
                }
            }
            await AddAuditLogEntry(action, qty, relid, actionname, actiontype, user, branch, product).ConfigureAwait(false);
            return true;
        }
        catch (Exception e)
        {
            await _sewebUtilities.LogError(e, "Error al modificar inventario para producto: " + product.ProductCode).ConfigureAwait(false);
            throw;
        }
    }

This second one is basically where I started having issues with EF core, the same code was on Framework with no issues so I ended up separating the problematic sections into its own functions for InsertBranchInventory:第二个基本上是我开始遇到 EF 核心问题的地方,相同的代码在 Framework 上没有问题,所以我最终将有问题的部分分成了自己的 InsertBranchInventory 函数:

public async Task InsertBranchInventory(int qty, Branches branch, Products product)
    {
        try
        {
            var inventory = new BranchesInventory
            {
                ProductQty = qty
            };
            await db.BranchesInventory.AddAsync(inventory).ConfigureAwait(false);
            await db.SaveChangesAsync().ConfigureAwait(false);
            //This works the first time the cycle runs but the second time but the second time the cycle runs it needs this code otherwise the id is 0 and I get error 1
            if (inventory.BranchesInventoryid <= 0)
            {
                inventory = await db.BranchesInventory
                    .OrderByDescending(i => i.BranchesInventoryid)
                    .Take(1)
                    .SingleOrDefaultAsync()
                    .ConfigureAwait(false);
            }
            db.BranchesInventory.Update(inventory);
            //if I do the below within the inventory declaration I get the error 2
            inventory.Branch = branch;
            inventory.Product = product;
            await db.SaveChangesAsync().ConfigureAwait(false);
            //I tried detaching the entities after the insertion was complete but it does not seem to help
            db.Entry(inventory).State = EntityState.Detached;
            db.Entry(branch).State = EntityState.Detached;
        }
        catch(Exception e)
        {
            await _sewebUtilities.LogError(e, "Error al agregar entrada de inventario para producto: " + product.ProductCode).ConfigureAwait(false);
            throw;
        }
    }
}

and for AddAuditLogEntry:对于 AddAuditLogEntry:

        public async Task AddAuditLogEntry(string actionPerformed, int qty, int relid, string actionname, string actiontype, Users user, Branches branch, Products product)
    {
        try
        {
            var actionTime = _sewebUtilities.GetCurrentTime();
            var audit = new AuditLog
            {
                ActionPerformed = actionPerformed,
                ItemType = "Products",
                Quantity = qty,
                EventDateTime = actionTime,
                Relatedid = relid,
                ActionName = actionname,
                ActionType = actiontype
            };
            await db.AuditLog.AddAsync(audit);
            await db.SaveChangesAsync().ConfigureAwait(false);
            //I'll have to replace the below as in the InsertBranchInventory function as I started getting error 2 on this after the second cycle run
            await db.Entry(audit).GetDatabaseValuesAsync().ConfigureAwait(false);
            db.AuditLog.Update(audit);
            //if I do the below within the inventory declaration I get the error 2, also for branches
            audit.Users = user;
            audit.Branches = branch;
            audit.Products = product;
            await db.SaveChangesAsync().ConfigureAwait(false);
            db.Entry(audit).State = EntityState.Detached;
            db.Entry(branch).State = EntityState.Detached;
        }
        catch (Exception e)
        {
            await _sewebUtilities.LogError(e, "Error al agregar entrada al log de productos");
        }
    }

for the errors I get they are as follow, Error 1:对于我得到的错误如下,错误1:

System.InvalidOperationException: The property 'BranchesInventoryid' on entity type 'BranchesInventory' has a temporary value while attempting to change the entity's state to 'Modified'. Either set a permanent value explicitly or ensure that the database is configured to generate values for this property.
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState)
   at Microsoft.EntityFrameworkCore.DbContext.Update[TEntity](TEntity entity)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.Update(TEntity entity)
   at SeWebERP.Infrastructure.ProductActions.InsertBranchInventory(Int32 qty, Branches branch, Products product) in Infrastructure\ProductActions.cs:line 386
   at SeWebERP.Infrastructure.ProductActions.ModifyProductInventory(Products product, Users user, Branches branch, Int32 qty, String action, Int32 relid, String actionname, String actiontype) in Infrastructure\ProductActions.cs:line 326 Error al modificar inventario para producto: test001

Error 2:错误2:

    Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert explicit value for identity column in table 'Branches' when IDENTITY_INSERT is set to OFF.
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__164_0(Task`1 result)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
ClientConnectionId:ebeb98ae-7c1a-498d-846f-63373ede3b86
Error Number:544,State:1,Class:16 

and no matter all of this patching even after I use the the if to get the last inventory record added it fails on the second cycle run with error:并且无论所有这些修补,即使在我使用 if 来获取添加的最后一个库存记录之后,它在第二个循环运行时都会失败并出现错误:

System.InvalidOperationException: The instance of entity type 'Branches' cannot be tracked because another instance with the key value '{Branchesid: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable`1 forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode`1 node)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode`1 node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, Object oldValue, Object newValue)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, Object oldValue, Object newValue)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectNavigationChange(InternalEntityEntry entry, INavigation navigation)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.LocalDetectChanges(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(IStateManager stateManager)
   at Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.DetectChanges()
   at Microsoft.EntityFrameworkCore.DbContext.TryDetectChanges()
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at SeWebERP.Infrastructure.ProductActions.AddAuditLogEntry(String actionPerformed, Int32 qty, Int32 relid, String actionname, String actiontype, Users user, Branches branch, Products product) in Infrastructure\ProductActions.cs:line 372 Error al agregar entrada al log de productos

and the inventory table end ups as such:库存表最终如下:

库存表

my models are not complicated, for the id I just use the key tag as below:我的模型并不复杂,对于 id 我只使用如下的 key 标签:

    public class BranchesInventory
{
    [Key]
    public int BranchesInventoryid { get; set; }

    public Branches Branch { get; set; }

    public Products Product { get; set; }

    public int ProductQty { get; set; }

    [MaxLength(200)]
    public string ProductLocation { get; set; }

}

but the documentation for ef core does not suggest to use anything else, I hope everything that is included here is enough information and someone can provide some guidance, or let me know if you would like me to include more information.但是 ef core 的文档不建议使用其他任何东西,我希望这里包含的所有信息都是足够的信息,有人可以提供一些指导,或者如果您希望我包含更多信息,请告诉我。

This is not a solution but in order to advance I ended up changing the process to use a SQL query:这不是一个解决方案,但为了推进,我最终更改了使用 SQL 查询的过程:

    public async Task InsertBranchInventory(int qty, Branches branch, Products product)
    {
        try
        {
            var p0 = branch.Branchesid;
            var p1 = qty;
            var p2 = product.Productsid;
            await db.Database.ExecuteSqlInterpolatedAsync($"INSERT INTO[BranchesInventory]([Branchesid], [ProductQty], [Productsid]) VALUES({p0}, {p1}, {p2});");
        }
        catch(Exception e)
        {
            await _sewebUtilities.LogError(e, "Error al agregar entrada de inventario para producto: " + product.ProductCode).ConfigureAwait(false);
            throw;
        }
    }

this is honestly really dumb and again don't consider this a solution but a workaround I guess and of the worst type becaus if I wanted to use SQL queries on my code I would use PHP.老实说,这真的很愚蠢,再次不认为这是一个解决方案,而是一种解决方法,我猜是最糟糕的类型,因为如果我想在我的代码上使用 SQL 查询,我会使用 PHP。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM