Не генерируется исключение OptimisticConcurrencyException: возможная причина: UpdateCommand - PullRequest
3 голосов
/ 20 февраля 2011

Я просто пытаюсь реализовать некоторые вещи с базой данных с EF 4.0. Я пытаюсь вызвать исключение OptimisticConcurrencyException, но, похоже, оно не работает. Я предполагаю, что если данные в базе данных изменились непосредственно перед тем, как я вызову context.SaveChanges (), фреймворк должен вызвать исключение OC - но это не так! Я посмотрел в update-command и voilà - команда update не содержит свойств от сущностей, которые я отметил с помощью concurrencyMode FIXED в окне свойств edmx для этого свойства.

Почему изменения в моей таблице базы данных (установите некоторые свойства как FIXED для режима параллелизма) не отражены в моей команде update?

Вот все, что я уже где-то опубликовал, но я думаю, что все проблемы возникают из-за ИСПРАВЛЕННЫХ значений, которые не отражены в моей команде обновления?

Кто-нибудь понял?

USE [MyProject]
GO

/****** Object:  Table [dbo].[History]    Script Date: 02/19/2011 20:14:49 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[History](
    [HistoryId] [bigint] IDENTITY(1,1) NOT NULL,
    [HistoryKey] [varchar](100) NOT NULL,
    [HistoryText] [varchar](max) NULL,
    [RowVersion] [timestamp] NOT NULL,
 CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED
(
    [HistoryId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ССДЛ утверждает это:

 <EntityType Name="History">
    <Key>
      <PropertyRef Name="HistoryId" />
    </Key>
    <Property Name="HistoryId" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
    <Property Name="HistoryKey" Type="varchar" Nullable="false" MaxLength="100" />
    <Property Name="HistoryText" Type="xml" />
    <Property Name="RowVersion" Type="timestamp" Nullable="false" StoreGeneratedPattern="Computed" />
  </EntityType>

MSL это:

<EntitySetMapping Name="Histories" StoreEntitySet="History" TypeName="MyModel.History">
  <ScalarProperty Name="HistoryId" ColumnName="HistoryId" />
  <ScalarProperty Name="HistoryKey" ColumnName="HistoryKey" />
  <ScalarProperty Name="HistoryText" ColumnName="HistoryText" />
  <ScalarProperty Name="RowVersion" ColumnName="RowVersion" />
</EntitySetMapping>

и csdl это:

<EntityType Name="History">
    <Key>
      <PropertyRef Name="HistoryId" />
    </Key>
    <Property Name="HistoryId" Type="Int64" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
    <Property Name="HistoryKey" Type="String" Nullable="false" MaxLength="100" Unicode="false" FixedLength="false" />
    <Property Name="HistoryText" Type="String" MaxLength="Max" Unicode="true" FixedLength="false" />
    <Property Name="RowVersion" Type="Binary" Nullable="false" MaxLength="8" FixedLength="true" annotation:StoreGeneratedPattern="Computed" />
  </EntityType>

«История» POCO была сгенерирована с помощью модифицированного генератора сущностей POCO ADO.NET в VS2010

Это источник:

class Program
{
  static void Main(string[] args)
  {
    using (var context = new MyEntities())
    {
      do
      {
        var query20 = (from p in context.Histories select p).OrderBy(p => p.HistoryId);
        ((ObjectQuery) query20).MergeOption = MergeOption.OverwriteChanges;
        var query2 = query20.ToList();
        History history = query2[0];

        Console.WriteLine("found: " + history.HistoryKey + " ==> " + history.HistoryText + " ==> " +
        Convert.ToBase64String(history.RowVersion));

        var origRowVersion = history.RowVersion;
        Console.WriteLine("Insert new key (q for exit):");
        string newtext = Console.ReadLine();

        if (newtext == "q")
          break;
        history.HistoryText = newtext;

        try
        {
          context.DetectChanges();
          var ose = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
          context.ObjectStateManager.ChangeObjectState(history, EntityState.Modified);
          context.SaveChanges(SaveOptions.DetectChangesBeforeSave);
          var newRowVersion = history.RowVersion;
          if (newRowVersion == origRowVersion)
          {
            Console.WriteLine("rowversion unchanged");
          }
          else
          {
            Console.WriteLine("rowversion changed");
          }
        }
        catch (OptimisticConcurrencyException)
        {
          Console.WriteLine("concurrencyexception occured!");
        }
      } while (true);
    }
  }
}

Что мне делать?

У меня есть процесс A, который получает запись через EF

У меня есть процесс B, который получает ту же запись через EF

Измените значение свойства этого объекта в процессе A и выполняйте до тех пор, пока метод context.SaveChanges () (точка останова в отладчике)

Измените значение того же свойства в процессе B и вызовите context.SaveChanges (). Я мог бы также сделать это непосредственно в базе данных.

Плохо то, что когда я перехожу через метод context.SaveChanges () в процессе A, OC-исключение не генерируется, как я и ожидал, так как свойство RowVersion в базе данных присутствует и оно помечено как исправлено в edmx.

Изменения свойств сохраняются в базе данных.

ТОГДА Я ДУМАЛ, ЧТО Я ПОЛУЧИЛ ИДЕЮ - НО ЭТО СЧИТАЕТСЯ НЕ ПОЛНОСТЬЮ ЛОГИЧЕСКИМ: ПОЧЕМУ СЛЕДУЕТ УСТАНОВИТЬ ИМУЩЕСТВО, ЕСЛИ ЭТО НЕ РАБОТАЕТ?

Я думаю, я думаю, почему исключение не выбрасывается.

Это потому, что значение свойства fixed не обновлено, и EF будет сравнивать исходное значение и измененное значение для этого фиксированного свойства ... и значения одинаковы -> исключение не выдается. Кажется, это правильно со стороны EF, но как я могу получить исключение, не запрашивая значение из базы данных и сравнить вручную? Как я могу заставить EF проверять значение фиксированного свойства по сравнению со значением в базе данных (и это значение фактически изменилось, потому что я делал это вручную в процессе B (соответственно в базе данных))?

ОБНОВЛЕНИЕ ОБНОВЛЕНИЯ с отсутствующими фиксированными свойствами:

=============== BEGIN COMMAND ===============

update [dbo].[History]
set [HistoryText] = @0
where ([HistoryId] = @1)
select [RowVersion]
from [dbo].[History]
where @@ROWCOUNT > 0 and [HistoryId] = @1
@0 = harald
@1 = 1

=============== END COMMAND ===============

Но должны ли быть включены и мои фиксированные свойства? Почему они не в заявлении об обновлении?

ДОЛЖЕН СМОТРЕТЬ ЧТО-ТО, КАК Я ПРИНИМАЮ:

=============== BEGIN COMMAND ===============

update [dbo].[History]
set [HistoryText] = @0
where ([HistoryId = @1) **AND ([ROWVERSION] = @2))**
select [RowVersion]
from [dbo].[History]
where @@ROWCOUNT > 0 and [HistoryId] = @1
@0 = harald
@1 = 1
**@2 = 0x000000045ef21**

=============== END COMMAND ==============

У кого-нибудь есть идеи для меня? Спасибо!

1 Ответ

1 голос
/ 20 февраля 2011

Привет, нашел ответ на мои проблемы:

Я использую cdsl, msdl и ssdl в отдельном каталоге (d: \ webservices, который не является выходным каталогом моей сборки).Эти три файла я создаю с помощью EDMGEN.exe.Подсказка в том, что файл csdl, созданный с помощью EDMGEN.EXE, отличается от csdl-content-section в xml-представлении файла .edmx, а файл csdl из edmgen.exe не содержит атрибут CONCURRENCYMODE = FIXEDтогда как содержимое csdl в .edmx-файле делает.Я не знаю, как я могу сгенерировать 3 файла из моего edmx-файла (metadataoutput установлен в «copytooutputdirectory» - но выходной каталог содержит только edmx-файл, но я хочу, чтобы три файла были там :-(), ноКогда я вручную изменяю файл csdl из edmgen.exe в d: \ webservices, поведение исключений параллелизма работает отлично!

Если кто-нибудь может предложить, как получить три файла, воссозданные без edmxgen.exe ... Я быочень ценю это: -)

...