Странное поведение службы WCF: Entity Framework выдает исключение при запуске кода, но отлично работает при отладке

#c# #entity-framework #wcf #debugging

#c# #entity-framework #wcf #отладка

Вопрос:

У меня есть служба WCF, которая использует Entity Framework для подключения к базе данных. Я столкнулся с каким-то странным поведением — когда я начинаю отладку службы WCF и вызываю один из ее методов (контракты операций), я получаю два разных сценария в зависимости от способа отладки. Это означает, что когда я просто вызываю метод, не заходя в код, я получаю исключение с указанием: «Нарушение ограничения ПЕРВИЧНОГО КЛЮЧА ‘PK_TagIsUsedInPublication’. Не удается вставить дубликат ключа в object ‘dbo.TagIsUsedInPublication’. Повторяющееся значение ключа равно (16, 54) «. С другой стороны, когда я ставлю точку останова в начале метода и перехожу к коду и просматриваю его построчно, все работает нормально. Как это возможно и что я делаю не так?

Мой метод использует две сущности из базы данных — публикацию и тег. У меня также есть таблица мостов, называемая TagIsUsedInPublication . У него есть два внешних ключа — один указывает на таблицу публикации, а другой — на тег. Он имеет составной первичный ключ, состоящий из двух столбцов (внешних ключей). Конечно, это означает, что публикация и тег имеют отношение «многие ко многим».

Вот мой метод:

 public static bool EditPublication(Interpretum.InterpretumService.SimplePublication publication)
{
    try
    {
        InterpretumDAL.InterpretumEntities interpretumEntities = new InterpretumDAL.InterpretumEntities();

        InterpretumDAL.Publication publicationDbContext =
            (from p in interpretumEntities.Publications
             where p.Id == publication.Id
             select p).SingleOrDefault();

        // This method just gets all the properties of my custom SimplePublication class and maps them to the Publication entity.
        publicationDbContext = publication.ToDbContext(publicationDbContext);

        if (publication.Tags.Count != 0)
        {
            publicationDbContext.Tags = Tag.RenderTagList(publication.Tags, publication.CreationUserId, interpretumEntities);
        }

        interpretumEntities.SaveChanges();
    }
    catch (Exception e)
    {
        // I added this throw line just to see what actually is causing the method fail. 
        throw e;
        return false;
    }

    return true;
}
  

Метод RenderTagList необходим, потому что моя схема требует уникальных значений для столбца Name таблицы тегов. Итак, он проверяет, есть ли уже тег с указанным именем. Если есть, оно добавляется в список тегов. Если это не так, создается новый, и только тогда он добавляется в список.

Вот как выглядит этот метод:

 public static List<InterpretumDAL.Tag> RenderTagList(List<SimpleTag> simpleTags, int creationUserId, InterpretumDAL.InterpretumEntities interpretumEntities)
{
    List<InterpretumDAL.Tag> tags = new List<InterpretumDAL.Tag>();

    foreach (var simpleTag in simpleTags)
    {
        InterpretumDAL.Tag tag = interpretumEntities.Tags.FirstOrDefault(x => x.Name == simpleTag.Name);

        if (tag == null)
        {
            tag = new InterpretumDAL.Tag()
            {
                Name = simpleTag.Name,
                CreationUserId = creationUserId
            };
        }

        tags.Add(tag);
    }

    return tags;
}
  

Как я писал выше, когда я просто вызываю EditPublication, генерируется исключение. Конечно, я каждый раз вызываю его с одними и теми же значениями, и независимо от того, сколько раз я его вызываю, я всегда получаю это исключение. Однако, когда я вхожу в код и перехожу к методу построчно, все работает нормально.

Я должен упомянуть, что я довольно новичок в Entity Framework, поэтому вполне возможно, что я делаю что-то не так. Проблема в том, что я не вижу, что я делаю неправильно, только из-за этого непредсказуемого поведения.

Редактировать 1:

Трассировка стека:

 at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Booleanamp; dataReady)
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Taskamp; task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Taskamp; task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<NonQuery>b__0(DbCommand t, DbCommandInterceptionContext`1 c)
at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand command, DbCommandInterceptionContext interceptionContext)
at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteNonQuery()
at System.Data.Entity.Core.Mapping.Update.Internal.DynamicUpdateCommand.Execute(Dictionary`2 identifierValues, List`1 generatedValues)
at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
  

Комментарии:

1. Пожалуйста, опубликуйте трассировку стека.

2. Я думаю, что вы можете использовать другой источник данных в зависимости от режима, в котором вы работаете. Возможно, что-то вроде #if(DEBUG) использовать источник данных A #else использовать источник данных B.

3. Другое, возможно, более вероятное объяснение заключается в том, что в вашем коде есть условия гонки? Это может объяснить, почему шаг за шагом работает нормально.

4. Я исправил ошибку, и я больше не получаю исключения, но я все еще не могу понять, почему я получил разные результаты в зависимости от способа запуска кода. Нет, у меня нет условной компиляции, как в вашем примере. И, кстати, что такое условие гонки?