Нужен совет по сложному сценарию вставки с использованием LinqToSql с шаблоном репозитория - PullRequest
2 голосов
/ 21 февраля 2010

У меня есть таблица User и таблица ClubMember в моей базе данных. Существует взаимно-однозначное сопоставление между пользователями и членами клуба, поэтому каждый раз, когда я вставляю ClubMember, мне нужно сначала вставить User. Это реализовано с помощью внешнего ключа на ClubMember (UserId REFERENCES User (Id)).

В моем приложении ASP.NET MVC я использую LinqToSql и шаблон репозитория для обработки логики персистентности. То, как я сейчас это реализовал, мои транзакции User и ClubMember обрабатываются отдельными классами репозитория, каждый из которых использует свой собственный экземпляр DataContext.

Это прекрасно работает, если нет ошибок в базе данных, но я обеспокоен тем, что у меня останутся осиротевшие User записи, если любая вставка ClubMember не удастся.

Чтобы решить эту проблему, я планирую переключиться на один DataContext, который я мог бы загрузить с обеими вставками, а затем вызвать DataContext.SubmitChanges() только один раз. Однако проблема заключается в том, что Id для User не назначается до тех пор, пока User не будет вставлен в базу данных, и я не могу вставить ClubMember, пока не узнаю UserId.

Вопросы:

  1. Можно ли вставить User в базу данных, получить Id, а затем вставить ClubMember, все как одну транзакцию (которую можно откатить, если что-то пойдет не так с какой-либо деталью) сделки)? Если да, то как?

  2. Если нет, то есть ли у меня единственный способ вручную удалить осиротевшие User записи, которые были созданы? Или есть лучший способ?

Ответы [ 2 ]

1 голос
/ 21 февраля 2010

Вы можете использовать System.Transactions.TransactionScope для выполнения всего этого в атомарной транзакции, но если вы используете разные экземпляры DataContext, это приведет к распределенной транзакции, что, вероятно, не то, что вы действительно хотите.

Судя по всему, вы не совсем правильно реализуете шаблон репозитория. Репозиторий не должен создавать свой собственный DataContext (или объект соединения, или что-либо еще) - эти зависимости должны передаваться через конструктор или открытое свойство. Если вы сделаете это, у вас не возникнет проблем с разделением DataContext:

public class UserRepository
{
    private MyDataContext context;

    public UserRepository(MyDataContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        this.context = context;
    }

    public void Save(User user) { ... }
}

Используйте тот же шаблон для ClubMemberRepository (или как вы его называете), и это становится тривиальным:

using (MyDataContext context = new MyDataContext())
{
    UserRepository userRep = new UserRepository(context);
    userRep.Save(user);
    ClubMemberRepository memberRep = new ClubMemberRepository(context);
    memberRep.Save(member);
    context.SubmitChanges();
}

Конечно, даже этот немного ненадежен. Если у вас есть внешний ключ в вашей базе данных, вам даже не понадобятся два репозитория, потому что Linq to SQL управляет отношениями. Код для создания должен просто выглядеть следующим образом:

using (MyDataContext context = new MyDataContext())
{
    User user = new User();
    user.Name = "Bob";
    user.ClubMember = new ClubMember();
    user.ClubMember.Club = "Studio 54";

    UserRepository userRep = new UserRepository(context);
    userRep.Save(user);
    context.SubmitChanges();
}

Не возитесь с несколькими репозиториями - пусть Linq to SQL обрабатывает отношения для вас, для этого и нужны ORM.

0 голосов
/ 21 февраля 2010

Да, вы можете сделать это за одну транзакцию. Используйте объект TransactionScope, чтобы начать и зафиксировать транзакцию (и выполнить откат, если, конечно, есть ошибка)

...