беспорядок о транзакциях и msdtc

При предназначении для Платформы.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();
9
задан Randy supports Monica 16 October 2009 в 04:45
поделиться

3 ответа

Если вы используете 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 и пул соединений для получения дополнительных сведений.

11
ответ дан 4 December 2019 в 11:06
поделиться

Чтобы расширить объяснение @ 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 также могут помочь вам более эффективно обрабатывать ваши соединения и транзакции.

8
ответ дан 4 December 2019 в 11:06
поделиться

Обновление: я нашел статью, которая объясняет, почему транзакции переходят из 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=&quot;TEST MSDTC&quot;;
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
0
ответ дан 4 December 2019 в 11:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: