0
votes

I have function to create/update entity and "instead insert, update trigger". This error occurs when I'm creating new entity, but with updating it's OK and adding directly in database also works perfectly with this trigger.

I'm using:

  • .NET Framework 4.5
  • Entity Framework 6.1.3
  • MS SQL Server Express 2016

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.

What I already tried:

  1. catch (DbUpdateConcurrencyException ex)
    {
        var ctx = ((IObjectContextAdapter)db).ObjectContext;
        ctx.Refresh(RefreshMode.ClientWins, db.Plannings);
        db.SaveChanges();
        return new SaveResult(true);
    }
    
  2. With and without SET NOCOUNT ON;

Function:

public SaveResult Save(PlanningSaveModel model)
    {
        using (var db = new RTPContext())
        {
            try
            {
                var entity = GetOrCreateEntity(db.Plannings, x => x.Id == model.Id).Item2;
                int totalDays = (model.EndDate.Value - model.StartDate.Value).Days + 1;
                if (totalDays <= 0)
                {
                    throw new Exception("Дата начала не может быть больше даты окончания");
                }
                entity.DocumentNumber = model.DocumentNumber;
                entity.DocumentEndDate = model.DocumentEndDate.Value;
                entity.UnitId = model.UnitId;
                entity.UnitMarkId = model.UnitMarkId;
                entity.Value = model.Value;
                entity.MeasureUnitId = model.MeasureUnitId;
                entity.StartDate = model.StartDate.Value;
                entity.EndDate = model.EndDate.Value;
                entity.CalendarWorkId = model.CalendarWorkId;
                //entity.PerDayValue = (int)Math.Round(Decimal.Divide(model.Value, totalDays));

                db.SaveChanges();
                return new SaveResult(true);
            }
            catch (Exception ex)
            {
                var errorMessage = ex.GetBaseException().Message;
                return new SaveResult(false, errorMessage);
            }
        }
    }

Model:

public abstract class PlanningModel
{
    [Key]
    public long? Id { get; set; }
    [Required]
    [Display(Name = "Document_Number", ResourceType = typeof(SharedStrings))]
    public string DocumentNumber { get; set; }
    [Required]
    [Display(Name = "Document_End_Date", ResourceType = typeof(SharedStrings))]
    public DateTime? DocumentEndDate { get; set; }
    [Required]
    [Display(Name = "Planning_Value", ResourceType = typeof(SharedStrings))]
    public int Value { get; set; }
    [Required]
    [Display(Name = "Date_Start", ResourceType = typeof(SharedStrings))]
    public DateTime? StartDate { get; set; }
    [Required]
    [Display(Name = "Date_End", ResourceType = typeof(SharedStrings))]
    public DateTime? EndDate { get; set; }
}

public class PlanningSaveModel : PlanningModel
{
    [Required]
    [Display(Name = "Unit_Name", ResourceType = typeof(SharedStrings))]
    public long UnitId { get; set; }
    [Required]
    [Display(Name = "Unit_Mark_Name", ResourceType = typeof(SharedStrings))]
    public long UnitMarkId { get; set; }
    [Required]
    [Display(Name = "Measure_Unit_Name", ResourceType = typeof(SharedStrings))]
    public long MeasureUnitId { get; set; }
    [Required]
    [Display(Name = "Calendar_Work_Name", ResourceType = typeof(SharedStrings))]
    public long CalendarWorkId { get; set; }
}

Trigger:

ALTER TRIGGER [dbo].[CalculatePerDayValue]
ON  [dbo].[Planning]
INSTEAD OF INSERT, UPDATE
AS 
BEGIN

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

IF EXISTS(SELECT * FROM deleted) BEGIN
    UPDATE dbo.Planning
    SET DocumentNumber = i.DocumentNumber,
        DocumentEndDate = i.DocumentEndDate,
        UnitId = i.UnitId,
        UnitMarkId = i.UnitMarkId,
        MeasureUnitId = i.MeasureUnitId,
        [Value] = i.[Value],
        StartDate = i.StartDate,
        EndDate = i.EndDate,
        CalendarWorkId = i.CalendarWorkId,
        PerDayValue = dbo.WorkDays(i.StartDate, i.EndDate, i.CalendarWorkId, i.Value),
        ModifierId = i.ModifierId,
        ModifyDate = i.ModifyDate,
        IsDeleted = i.IsDeleted,
        HVersion = i.HVersion
    FROM inserted i
    WHERE dbo.Planning.Id = i.Id
END
ELSE BEGIN
    INSERT INTO dbo.Planning
    SELECT [DocumentNumber]
       ,[DocumentEndDate]
       ,[UnitId]
       ,[UnitMarkId]
       ,[MeasureUnitId]
       ,[Value]
       ,[StartDate]
       ,[EndDate]
       ,[CalendarWorkId]
       ,dbo.WorkDays(StartDate, EndDate, CalendarWorkId, Value)
       ,[ModifierId]
       ,[ModifyDate]
       ,[IsDeleted]
       ,[HVersion] FROM inserted
END
END

Scalar function used in trigger:

ALTER FUNCTION [dbo].[WorkDays]
(
    @Start datetime,
    @End datetime,
    @CalendarId bigint,
    @Value int
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE @WorkDays int

-- Add the T-SQL statements to compute the return value here
SELECT @WorkDays = CASE @CalendarId WHEN 1 
        THEN
        (DATEDIFF(dd, @Start, @End) + 1) - (DATEDIFF(wk, @Start, @End) * 2)
            - (case when DATEPART(dw, @Start) IN (6, 7) then 1 else 0 end)
            - (case when DATEPART(dw, @End) IN (6, 7) then 1 else 0 end)
            - (select count(*) from dbo.CalendarHoliday where [Date] between @Start and @End and DATEPART(dw, [Date]) NOT IN (6, 7))
        ELSE
        (DATEDIFF(dd, @Start, @End) + 1)
            - (select count(*) from dbo.CalendarHoliday where [Date] between @Start and @End)
        END

-- Return the result of the function
RETURN @Value/@WorkDays

END
2

2 Answers

0
votes

I found an answer here: Entity Framework, view and instead of insert trigger. Can't insert a row into the view

Just added SELECT Id FROM dbo.Planning where @@ROWCOUNT > 0 AND Id = SCOPE_IDENTITY() into insert statement in the trigger

So Trigger looks like that:

ALTER TRIGGER [dbo].[CalculatePerDayValue]
ON  [dbo].[Planning]
INSTEAD OF INSERT, UPDATE
AS 
BEGIN

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

IF EXISTS(SELECT * FROM deleted) BEGIN
    UPDATE dbo.Planning
    SET DocumentNumber = i.DocumentNumber,
        DocumentEndDate = i.DocumentEndDate,
        UnitId = i.UnitId,
        UnitMarkId = i.UnitMarkId,
        MeasureUnitId = i.MeasureUnitId,
        [Value] = i.[Value],
        StartDate = i.StartDate,
        EndDate = i.EndDate,
        CalendarWorkId = i.CalendarWorkId,
        PerDayValue = dbo.WorkDays(i.StartDate, i.EndDate, i.CalendarWorkId, i.Value),
        ModifierId = i.ModifierId,
        ModifyDate = i.ModifyDate,
        IsDeleted = i.IsDeleted,
        HVersion = i.HVersion
    FROM inserted i
    WHERE dbo.Planning.Id = i.Id
END
ELSE BEGIN
    INSERT INTO dbo.Planning
    SELECT [DocumentNumber]
       ,[DocumentEndDate]
       ,[UnitId]
       ,[UnitMarkId]
       ,[MeasureUnitId]
       ,[Value]
       ,[StartDate]
       ,[EndDate]
       ,[CalendarWorkId]
       ,dbo.WorkDays(StartDate, EndDate, CalendarWorkId, Value)
       ,[ModifierId]
       ,[ModifyDate]
       ,[IsDeleted]
       ,[HVersion] FROM inserted

    -- FOR ENTITY FRAMEWORK CONCURRENCY EXCEPTION HANDLING
    SELECT Id FROM dbo.Planning where @@ROWCOUNT > 0 AND Id = SCOPE_IDENTITY()
END
END
0
votes

I had this error because the table which i tried to insert into in database have no primary key. Added primary key for the table fix this exception for me. I think you have the same problem because your Planning table have no primary key, that why your data model have these lines:

 [Key]
 public long? Id { get; set; }