Транзакции через несколько методов DAL из одного метода в BLL - PullRequest
6 голосов
/ 30 января 2009

Как бы вы вызвали несколько методов на уровне доступа к данным из одного метода на уровне бизнес-логики, чтобы все команды SQL жили в одной транзакции SQL?

Каждый из методов DAL может вызываться индивидуально из других мест в BLL, поэтому нет гарантии, что методы уровня данных всегда являются частью транзакции. Нам нужна эта функциональность, поэтому, если база данных переходит в автономный режим в середине длительного процесса, фиксации не происходит. Бизнес-уровень управляет различными вызовами методов уровня данных на основе результатов каждого из предыдущих вызовов. Мы хотим зафиксировать (с бизнес-уровня) только в самом конце всего процесса.

Ответы [ 4 ]

8 голосов
/ 30 января 2009

ну, во-первых, вам нужно будет придерживаться атомарной единицы работы, которую вы указываете как отдельный метод в своем BLL. Это (например) создаст клиента, заказ и позиции заказа. Затем вы бы аккуратно завернули все это в TransactionScope с помощью оператора. TransactionScope - секретное оружие здесь. ниже приведен код, над которым, к счастью, я сейчас работаю :):

public static int InsertArtist(Artist artist)
{
    if (artist == null)
        throw new ArgumentNullException("artist");

    int artistid = 0;
    using (TransactionScope scope = new TransactionScope())
    {
        // insert the master Artist
        /* 
           we plug the artistid variable into 
           any child instance where ArtistID is required
        */
        artistid = SiteProvider.Artist.InsertArtist(new ArtistDetails(
        0,
        artist.BandName,
        artist.DateAdded));

        // insert the child ArtistArtistGenre
        artist.ArtistArtistGenres.ForEach(item =>
        {
            var artistartistgenre = new ArtistArtistGenreDetails(
                0,
                artistid,
                item.ArtistGenreID);
            SiteProvider.Artist.InsertArtistArtistGenre(artistartistgenre);
        });

        // insert the child ArtistLink
        artist.ArtistLinks.ForEach(item =>
        {
            var artistlink = new ArtistLinkDetails(
                0,
                artistid,
                item.LinkURL);
            SiteProvider.Artist.InsertArtistLink(artistlink);
        });

        // insert the child ArtistProfile
        artist.ArtistProfiles.ForEach(item =>
        {
            var artistprofile = new ArtistProfileDetails(
                0,
                artistid,
                item.Profile);
            SiteProvider.Artist.InsertArtistProfile(artistprofile);
        });

        // insert the child FestivalArtist
        artist.FestivalArtists.ForEach(item =>
        {
            var festivalartist = new FestivalArtistDetails(
                0,
                item.FestivalID,
                artistid,
                item.AvailableFromDate,
                item.AvailableToDate,
                item.DateAdded);
            SiteProvider.Festival.InsertFestivalArtist(festivalartist);
        });
        BizObject.PurgeCacheItems(String.Format(ARTISTARTISTGENRE_ALL_KEY, String.Empty, String.Empty));
        BizObject.PurgeCacheItems(String.Format(ARTISTLINK_ALL_KEY, String.Empty, String.Empty));
        BizObject.PurgeCacheItems(String.Format(ARTISTPROFILE_ALL_KEY, String.Empty, String.Empty));
        BizObject.PurgeCacheItems(String.Format(FESTIVALARTIST_ALL_KEY, String.Empty, String.Empty));
        BizObject.PurgeCacheItems(String.Format(ARTIST_ALL_KEY, String.Empty, String.Empty));

        // commit the entire transaction - all or nothing
        scope.Complete();
    }
    return artistid;
}

надеюсь, вы получите суть. в основном, это все успешное или неудачное задание, независимо от каких-либо разнородных баз данных (т.е. в приведенном выше примере Artist и Artistartistgenre могут быть размещены в двух отдельных хранилищах БД, но TransactionScope меньше заботится об этом, он работает на уровне COM + и управляет атомарностью из области, которую он может «увидеть»)

надеюсь, это поможет

РЕДАКТИРОВАТЬ: вы, возможно, обнаружите, что первоначальный вызов TransactionScope (при запуске приложения) может быть слегка заметным (т.е. в примере выше, если вызывается в первый раз, может потребоваться 2 -3 секунды для завершения), однако последующие вызовы почти мгновенные (то есть обычно 250-750 мс). компромисс между простой транзакцией точки контакта и (громоздкими) альтернативами уменьшает (для меня и моих клиентов) эту первоначальную задержку загрузки.

просто хотел продемонстрировать, что легкость не приходит без компромиссов (хотя и на начальных этапах)

4 голосов
/ 30 января 2009

То, что вы описываете, является «определением» длинной транзакции.

Каждый метод DAL может просто предоставлять операции (без каких-либо конкретных фиксаций). Ваш BLL (который действует в любом случае, когда вы координируете любые вызовы в DAL) - это то место, где вы можете выбрать фиксацию или выполнение «точки сохранения». Точка сохранения - это необязательный элемент, который можно использовать, чтобы разрешить «откат» внутри длительной транзакции.

Так, например, если у моего DAL есть методы DAL1, DAL2, DAL3, все они изменчивы, они просто «выполнят» операции изменения данных (то есть некоторый тип Create, Update, Delete). Из моего BLL, давайте предположим, что у меня есть методы BL1 и BL2 (BL1 долго работает). BL1 вызывает все вышеупомянутые методы DAL (т.е. DAL1 ... DAL3), в то время как BL2 только вызывает DAL3.

Следовательно, при выполнении каждого метода бизнес-логики у вас может быть следующее:

BL1 (длинная транзакция) -> {точка сохранения} DAL1 -> {точка сохранения} DAL2 -> DAL3 {commit / end}

BL2 -> DAL3 {commit / end}

Идея «точки сохранения» состоит в том, что она может позволить откату BL1 в любой момент, если в операциях с данными возникают проблемы. Длинная транзакция принимается ТОЛЬКО в случае успешного завершения всех трех операций. BL2 по-прежнему может вызывать любой метод в DAL, и он отвечает за управление фиксацией. ПРИМЕЧАНИЕ: вы также можете использовать «точки сохранения» в коротких / обычных транзакциях.

2 голосов
/ 30 января 2009

Хороший вопрос. Это становится причиной несоответствия импеданса.

Это один из самых сильных аргументов в пользу использования хранимых процедур. Причина: они предназначены для инкапсуляции нескольких операторов SQL в транзакции.

То же самое можно сделать процедурным образом в DAL, но это приводит к тому, что код становится менее четким, но обычно приводит к перемещению баланса сцепления / сцепления в неправильном направлении.

По этой причине я реализую DAL на более высоком уровне абстракции, чем просто инкапсуляция таблиц.

1 голос
/ 30 января 2009

на всякий случай, если мой комментарий в оригинальной статье не «прилипает», вот что я добавил в качестве дополнительной информации:

<----- по совпадению, только что заметил еще одну похожую ссылку на эту публикацию через несколько часов после вашего запроса. использует аналогичную стратегию и, возможно, стоит того, чтобы вы на нее посмотрели: http://stackoverflow.com/questions/494550/how-does-transactionscope-roll-back-transactions ----->

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...