При предназначении для Платформы.NET 3.5 самым изящным решением был бы дополнительный метод, по-моему.
public static class ObjectExtensions
{
public static string NullSafeToString(this object obj)
{
return obj != null ? obj.ToString() : String.Empty;
}
}
Затем для использования:
attribs.something = entry.Properties["something"].Value.NullSafeToString();
Если вы используете MSDTC, вам понадобится клиент (ваше приложение) и сервер (база данных) для запуска MSDTC и правильная настройка.
Это может быть источником боли, особенно при работе с межсетевыми экранами. Если у вас возникли проблемы, см. Устранение проблем с MSDTC . В нем говорится о BizTalk, но это относится к MSDTC в целом. DTCPING также ваш друг.
Теперь, если вы используете SQL Server 2005 и выше, обращаетесь только к одной базе данных, используете одно соединение с базой данных и не передаете транзакции между доменами приложений, тогда вам не нужно требовать использование MSDTC. В этих обстоятельствах менеджер транзакций System.Transactions будет управлять вашими транзакциями за вас. Если произойдет какая-либо из предыдущих ситуаций, транзакция будет переведена в распределенную транзакцию (и менеджером транзакций будет MSDTC). Для получения дополнительной информации см. Transaction Management Escalation .
В общем, лучше избегать использования MSDTC, если он вам не нужен. т.е. если вы имеете дело только с одной базой данных SQL Server 2005+, попробуйте спроектировать свой код так, чтобы он не использовал MSDTC. Помимо проблем с настройкой, DTC снижает производительность, потому что все вызовы MSDTC находятся вне процесса в сочетании с накладными расходами двухфазного протокола фиксации (который использует MSDTC).
С точки зрения того, что происходит в вашей конкретной ситуации, это так. сложно сказать. Если ваш код не изменился, тогда возможно правила межсетевого экрана изменились? Я также видел, как обновления Windows меняют конфигурацию DTC (для безопасности), что вызывает проблему.
Обновление на основе комментария:
Для отслеживания продвижения или эскалации транзакций, если вы не используете какие-либо распределенные транзакции, я думаю, вы могли бы использовать некоторые счетчики производительности координатора распределенных транзакций для отслеживания зафиксированных транзакций. При тестировании вы можете отключить MSDTC и посмотреть, не сработает ли ваш код. Другой способ - отслеживать транзакции в SQL Server. С точки зрения кодирования вы можете попытаться обработать событие DistributedTransactionStarted и выполнить некоторое протоколирование (но удалите этот код перед запуском в производство).
Для примера кода с использованием одного соединения перейдите к Страница TransactionScope в MSDN. По сути, создайте TransactionScope, создайте SqlConnection, поработайте с SqlConnection, закройте соединение, вызовите scope.Complete ().
Обратите внимание, что если вы используете методы адаптера данных, они автоматически управляют вашим соединением, поэтому соединение закрывается или возвращается в соединение бассейн. В любом случае, если вызывается другая операция, транзакция будет переведена в транзакцию DTC. См. System.Transactions и пул соединений для получения дополнительных сведений.
Чтобы расширить объяснение @ Tuzo, вот пример команды, которая всегда будет расширяться:
using(var scope = new TransactionScope())
{
using(var conn = new SqlConnection(connString)){
conn.Open();
//...some command, etc.
}
using(var conn = new SqlConnection(connString)){
conn.Open();
//...some command, etc.
}
scope.Complete();
}
На практике соединение и команда будут в другом классе и т. Д., Но вы получить идею. Даже если строка подключения относится к той же базе данных, она будет расширяться с использованием DTC, потому что это два подключения. Транзакция без эскалации будет выглядеть так:
using(var scope = new TransactionScope())
{
using(var conn = new SqlConnection(connString)){
conn.Open();
//...some command, etc.
//...some other command, etc.
}
scope.Complete();
}
Это лучший код в любом случае, потому что вы открываете соединение, делаете то, что вам нужно, и закрываете как можно скорее. Это означает, что вам нужно продумать управление подключением. В зависимости от вашего приложения вы можете реализовать это по-разному. Например:
using(var scope = new TransactionScope())
using(var conn = new SqlConnection(connString))
{
conn.Open();
var myService = new MyService(conn);
var myService2 = new MyService2(conn);
myService.DoSomething();
myService2.DoSomething();
scope.Complete();
}
Есть разные способы реализовать это. Блок приложения доступа к данным корпоративной библиотеки и различные ORM также могут помочь вам более эффективно обрабатывать ваши соединения и транзакции.
Обновление: я нашел статью, которая объясняет, почему транзакции переходят из LTM в MSDTC при использовании только GetData и Update на одном и том же адаптере данных в TransactionScope, а также обходной путь.
Окончательная статья в блоге TableAdapters + Transactions http://blah.winsmarts.com/2006/06/18/the-definitive-tableadapters--transactions-blog-post.aspx
Я понимаю часть о том, что наличие нескольких одновременно открытых соединений приводит к тому, что транзакция становится распределенной. Однако у меня возникает проблема, когда есть только одно соединение и один запрос к базе данных, который его эскалирует. В хранимой процедуре также нет никаких транзакций. Если у кого-то есть подсказка, я бы хотел ее услышать. Из моего примера кода следует, что "adapter.Update(table)" вызовет распределенную транзакцию.
Я вытащил внутренности кода из моего существующего проекта и упростил большую часть того, что происходило, и у меня все еще возникают те же проблемы. По сути, это создание набора данных с адаптером таблицы и настройка его с помощью хранимой процедуры для выбора, вставки и удаления. Я выбираю все связанные записи с определенным пользователем. Затем, в зависимости от того, существует ли "myPPID" для одной из записей, я добавляю его или удаляю. Затем я вызываю метод обновления и вижу, как транзакция перерастает в распределение, наблюдая за статистикой транзакций в службах компонентов.
Я использую windows XP Pro SP3 и .Net Framework 3.5 для клиентской программы. Она подключается к базе данных SQL 2005 по локальной сети к Windows Server 2003 R2 Enterprise Edition SP2.
private void button1_Click(object sender, EventArgs e)
{
int userId = 3;
int myPPId = 881;
using (TransactionScope ts = new TransactionScope())
{
using (DataSet1TableAdapters.AssignedPPTableAdapter adapter
= new MSDTCPromotionTest.DataSet1TableAdapters.AssignedPPTableAdapter())
{
using (DataSet1.AssignedPPDataTable table = adapter.GetData(userId))
{
DataSet1.AssignedPPRow row = table.FindByUserIdmyPPId(
userId, myPPId);
if (row == null)
{
table.AddAssignedPPRow(userId, myPPId, string.Empty,
string.Empty, true);
}
else
{
row.Delete();
}
adapter.Update(table);
}
ts.Complete();
}
}
}
Строка подключения не представляет собой ничего особенного:
<add name="ConnectionString" connectionString="
Data Source=devdb;
Initial Catalog="TEST MSDTC";
Integrated Security=True"
providerName="System.Data.SqlClient" />
Также хранимые процедуры представляют собой простые вызовы crud.
Create:
ALTER procedure [dbo].[p_UserForm_AssignedPP_Insert]
(
@UserId INT,
@myPPId int
)
AS
SET NOCOUNT ON;
INSERT INTO [UsermyPP] ([UserID],[myPPID],[DateCreated])
VALUES (@UserId,@myPPId,GETutcDATE())
Read:
ALTER procedure [dbo].[p_UserForm_AssignedPP_SelectByUserId]
(
@UserId int
)
AS
SELECT
[UserId],
[myPPId],
'' Title,
'' Abbreviation,
0 IsArchived
from
UsermyPP unpp
where
unpp.[userid] = @UserId
Delete:
ALTER procedure [dbo].[p_UserForm_AssignedPP_Delete]
(
@Original_UserId INT,
@Original_MyPPId INT
)
AS
SET NOCOUNT ON;
DELETE FROM usermypp WHERE [UserID] = @Original_UserId
AND [MyPPID] = @Original_MyPPId