#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. Я исправил ошибку, и я больше не получаю исключения, но я все еще не могу понять, почему я получил разные результаты в зависимости от способа запуска кода. Нет, у меня нет условной компиляции, как в вашем примере. И, кстати, что такое условие гонки?