Как обрабатывать вложенный текст данных в BL? - PullRequest
7 голосов
/ 29 августа 2009
public class TestBL
{
    public static void AddFolder(string folderName)
    {
        using (var ts = new TransactionScope())
        {
            using (var dc = new TestDataContext())
            {
                var folder = new Folder { FolderName = folderName };

                dc.Folders.InsertOnSubmit(folder);
                dc.SubmitChanges();

                AddFile("test1.xyz", folder.Id);
                AddFile("test2.xyz", folder.Id);
                AddFile("test3.xyz", folder.Id);

                dc.SubmitChanges();
            }

            ts.Complete();
        }
    }

    public static void AddFile(string filename, int folderId)
    {
        using (var dc = new TestDataContext())
        {
            dc.Files.InsertOnSubmit(
                new File { Filename = filename, FolderId = folderId });

            dc.SubmitChanges();
        }
    }
}

Это пример вложенного DataContext (не проверено). Проблема начинается, когда TransactionScope добавляется в наш маленький эксперимент (как показано выше). Первый AddFile в функции AddFolder преобразует транзакцию в DTC (что, во всяком случае, плохо), поскольку AddFile инициализирует новый DataContext, открывая, таким образом, второе соединение с БД.

  1. Как я могу использовать вложенный DataContext, который не будет использовать DTC?
  2. Это все просто неправильно? Должен ли я использовать DataContext по-другому?

Ответы [ 4 ]

3 голосов
/ 30 августа 2009

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

Вы не одиноки в путанице по поводу лучших практик с контекстами данных. Если вы ищете в Интернете для этого, есть ответы по всей карте. В вашем примере вы можете передать контекст данных в метод AddFile. Или вы могли бы преобразовать этот доступ к данным в класс, который поддерживает время жизни контекста данных до тех пор, пока все папки и файлы не будут сохранены. Рик Страл опубликовал статью по нескольким методикам.

Тем не менее, ни один из ответов, которые я видел в LINQ to SQL, не кажется очень удовлетворительным. Рассматривали ли вы возможность избежать управления уровнем данных с помощью ORM? Я использовал NetTiers с большим успехом, но я слышал хорошие новости о PLINQO . Оба они требуют CodeSmith , но есть много альтернатив .

1 голос
/ 16 сентября 2009

Помимо передачи DataContext в AddFiles в качестве параметра, вы также можете передать одно значение DataContext Connection другому DataContext. Это гарантирует, что другой DataContext имеет такое же соединение.

Каждый DataContext также имеет свойство Transaction, которое вы, вероятно, могли бы установить и передать вместо использования объекта TransactionScope.

0 голосов
/ 13 сентября 2009

Для этой транзакции вам не нужно совершать 2 и более обходов. Я считаю, что LINQ-DataContext разумно распознает, что эти файлы принадлежат объекту папки, и сначала вставит строку папки, а затем файлы (все в контексте транзакции, например, BEGIN TRAN / COMMIT). Однако вам нужно сделать:

dc.Files.InsertOnSubmit(
                new File { Filename = filename, Folder = folder });

вместо FolderId. Примерно так:

public class TestBL
{
    public static void AddFolder(string folderName)
    {
        using (var ts = new TransactionScope())
        {
            using (var dc = new TestDataContext())
            {
                var folder = new Folder { FolderName = folderName };

                AddFile(dc, "test1.xyz", folder);
                AddFile(dc, "test2.xyz", folder);
                AddFile(dc, "test3.xyz", folder);

                dc.SubmitChanges();
            }

            ts.Complete();
        }
    }

    private static void AddFile(DataContext dc, string filename, Folder folder)
    {
            dc.Files.InsertOnSubmit(
                new File { Filename = filename, Folder = folder });
    }

    public static void AddFile(string filename, int folderId)
    {
        using (var dc = new TestDataContext())
        {
            var folder = new Folder { FolderId = folderId };
            dc.Attach(folder, false);
            AddFile(dc, filename, folder);

            dc.SubmitChanges();
        }
    }
}

На ваш вопрос относительно DTC: я не думаю, что здесь можно избежать DTC из-за 2 открытых соединений. Я полагаю, что они недавно внесли некоторые изменения в эту область, см. здесь , но описанный здесь сценарий немного отличается (2 подключения открываются и закрываются одно за другим, в отличие от 2 открытых одновременно).

0 голосов
/ 06 сентября 2009

Я придумал способ справиться с такими ситуациями.

Пример базового класса для сущности BL (сущность наследует этот класс)

abstract public class TestControllerBase : IDisposable
{
    public TestDataContext CurrentDataContext { get; private set; }

    protected TestControllerBase()
    {
        CurrentDataContext = new TestDataContext();
    }

    protected TestControllerBase(TestDataContext dataContext)
    {
        CurrentDataContext = dataContext;
    }

    protected void ClearDataContext()
    {
        CurrentDataContext.Dispose();
        CurrentDataContext = new TestDataContext();
    }

    public void Dispose()
    {
        CurrentDataContext.Dispose();
    }
}

Реализован контроллер

public sealed class BLTestController : TestControllerBase
{
    public BLTestController() { }

    public BLTestController(TestDataContext dataContext)
        : base(dataContext) { }

    //  The entity functions will be implemented here using CurrentDataContext
}

Простое использование встроенного контроллера

var testController = new BLTestControllerA();

testController.DeleteById(1);

Более сложное использование реализованного контроллера (2 контроллера в одном DataContext)

var testControllerA = new BLTestControllerA();
var testControllerB = new BLTestControllerB(testControllerA.CurrentDataContext);

testControllerA.DeleteById(1);
testControllerB.DeleteById(1);

Я хотел бы увидеть больше идей по решению этой загадки и комментарии по поводу кода выше.

...