UnitOfWork с TransactionScope без изменений в базе данных, но с передачей unitTest - PullRequest
0 голосов
/ 01 декабря 2018

При попытке вставить сущность в базу данных, используя unitOfWork с TransactionsScope.На самом деле я получу ожидаемые результаты от пользователя, которого я добавил (int ожидается, Userid = uowFactory.Create (). AddBasicUser (user);).Но после удаления UnitOfWork и TransactionsScope я не вижу никаких изменений в базе данных.Это заставляет меня поверить в то, что я неправильно реализовал транзакцию TransactionsScope в UnitOfWork.

Я также заметил, что при запуске (отладке) следующего unitTest и с точкой останова в следующей строке: 60, я не могу запроситьтаблица базы данных «BasicUser» через mssql mngmnt studio (заблокировано, JustSaying: выполнение запроса ...).

Сказанный UnitTest (ссылка на изображение)

Сам UnitTest проходит.

Вопрос: Почему кажется, что UnitOfWork работаеткак и предполагалось, но я не вижу каких-либо изменений в базе данных?И как получается, что Db блокируется, когда транзакцияcope / UnitOfWork уже удалена.

Большое спасибо!

Код ссылки:

IUnitOfWorkFactory

namespace DomainServices
{
    public interface IUnitOfWorkFactory
    {
        IUnitOfWork Create();
    }
}

UnitOfWorkFactory

using System;
using DomainServices;
using System.Transactions;
namespace DataAccess
{
    public class UnitOfWorkFactory
    {
        private readonly IsolationLevel _isolationLevel;
        private readonly Func<IsolationLevel, IUnitOfWork> CreateUnitOfWork;

        public UnitOfWorkFactory(Func<IsolationLevel, IUnitOfWork> createUnitOfWork, IsolationLevel isolationLevel)
        {
            _isolationLevel = isolationLevel;
            CreateUnitOfWork = createUnitOfWork;
        }

        public IUnitOfWork Create()
        {
            return CreateUnitOfWork.Invoke(_isolationLevel);
        }
    }
}

IUnitOfWork

using System;

namespace DomainServices
{
    public interface IUnitOfWork : IDisposable
    {
        IUserRepository UserRepository { get; }
        void Dispose();
        void Commit();
    }
}

UnitOfWork

using DataAccess.Repository;
using DomainServices;
using System.Transactions;

namespace DataAccess
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly DatabaseConnection _dbConnection;
        private readonly TransactionScope _transactionScope;
        private bool isDisposed;

        public IUserRepository _userRepository;
        public IUserRepository UserRepository
        {
            get
            {
                if (this._userRepository == null)
                {
                    this._userRepository = new UserRepository(this._dbConnection, this._transactionScope);
                }

                return this._userRepository;
            }
        }

        public UnitOfWork(DatabaseConnection dbConnection, IsolationLevel isolationLevel)
        {
            this.isDisposed = false;
            this._dbConnection = dbConnection;
            this._transactionScope = new TransactionScope(
                TransactionScopeOption.Required,
                new TransactionOptions
                {
                    IsolationLevel = isolationLevel,
                    Timeout = TransactionManager.DefaultTimeout
                });
        }

        public void Commit()
        {
            this._transactionScope.Complete();
        }

        public void Dispose()
        {
            this.Dispose(true);
        }

        protected void Dispose(bool disposing)
        {
            if (!this.isDisposed)
            {
                if (disposing)
                {
                    this._transactionScope.Dispose();
                }

                this.isDisposed = true;
            }
        }
    }
}

IUserRepository

using Domain;
using System.Collections.Generic;

namespace DomainServices
{
    public interface IUserRepository
    {
        int AddBasicUser(BasicUser basicUser);
        IEnumerable<BasicUser> GetAllBasicUsers();
    }
}

UserRepository

using System.Collections.Generic;
using Domain;
using DomainServices;
using System.Data.SqlClient;
using System;
using System.Transactions;

namespace DataAccess.Repository
{
    public class UserRepository : IUserRepository
    {
        private readonly DatabaseConnection _dbConnection;
        private readonly TransactionScope _transaction;
        public UserRepository(DatabaseConnection dbConnection, TransactionScope transaction)
        {
            this._dbConnection = dbConnection;
            this._transaction = transaction;
        }

        public int AddBasicUser(BasicUser basicUser)
        {
            string QueryString = "INSERT INTO BasicUser (Username, RegisterDate) OUTPUT Inserted.Id VALUES (@username, @registerDate);";

            TODO MarWolt: Use a Maybe instead of magic values.
            int basicUserId = -1;

            //using (TransactionScope scope = new TransactionScope())
            using (SqlConnection conn = new SqlConnection(_dbConnection.ConnectionString))
            using (SqlCommand cmd = new SqlCommand(QueryString, conn))
            {
                conn.Open();
                cmd.Parameters.AddWithValue("Username", basicUser.UserName.Value);
                cmd.Parameters.AddWithValue("RegisterDate", basicUser.RegisterDate);
                using (var reader = cmd.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        basicUserId = Convert.ToInt32(reader["Id"]);
                    }
                }

                scope.Complete();

            }

            return basicUserId;
        }

        public IEnumerable<BasicUser> GetAllBasicUsers()
        {
            var QueryString = "SELECT Id, Username, LastLogin, RegisterDate FROM BasicUser;";
            var result = new List<BasicUser>();
            using (var conn = new SqlConnection(_dbConnection.ConnectionString))
            using (var cmd = new SqlCommand(QueryString, conn))
            {
                conn.Open();
                using (var reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                        result.Add(CreateBasicUserFromReader(reader));
                }
                return result;
            }
        }

        private BasicUser CreateBasicUserFromReader(SqlDataReader reader)
        {
            return new BasicUser(
                id: Convert.ToInt32(reader["Id"]),
                userName: new UserName(Convert.ToString(reader["Username"])),
                lastLogin: DateTime.Now,  //Convert.ToDateTime(reader["LastLogin"]), //TODO MarWolt This...
                registerDate: Convert.ToDateTime(reader["RegisterDate"]),
                rideTokens: new List<RideToken>()
            );
        }
    }
}

UnitTestProject

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;
using DataAccess;
using DataAccess.Repository;
using Domain;
using DomainServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTestProject
{
    [TestClass]
    public class UserRepositoryTests
    {
        private UnitOfWorkFactory uowFactory;
        private DatabaseConnection testDatabaseConnection;

        public UserRepositoryTests()
        {
            testDatabaseConnection = new DatabaseConnection(
                "Server=.\\SQLEXPRESS;Initial Catalog=TestDb;User Id=DbUser;Password=DbUserPassword;Integrated Security=false;Connect Timeout=5;");
            uowFactory = new UnitOfWorkFactory((IsolationLevel) => new UnitOfWork(testDatabaseConnection, IsolationLevel), IsolationLevel.ReadCommitted);
        }

        [TestInitialize()]
        public void Initialize()
        {
            //TODO MarWolt: delete everything from the database on a clean way.
            using (SqlConnection conn = new SqlConnection(testDatabaseConnection.ConnectionString))
            using (SqlCommand cmd = new SqlCommand(Properties.Resources.ClearDbSqlScript, conn))
            {
                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }

        [TestMethod]
        public void AddNewBasicUser()
        {
            var user = new BasicUser(new UserName("UnitTestBasicUserName"), DateTime.UtcNow, new List<RideToken>() { });

            var users = uowFactory.Create().UserRepository.GetAllBasicUsers();
            Assert.AreEqual(0, users.Count());

            using (IUnitOfWork unitOfWork = uowFactory.Create())
            {
                unitOfWork.UserRepository.AddBasicUser(user);
                unitOfWork.Commit();
            }

            users = uowFactory.Create().UserRepository.GetAllBasicUsers();
            Assert.AreEqual(1, users.Count());

            var recievedUser = users.First();
            Assert.AreEqual(user.UserName.Value, recievedUser.UserName.Value);

            // I'm unable to query the database via mssql server management studio when this breakpoint is active.
            // I would actually expect it already to be done after line 51.
            Console.WriteLine(string.Empty);
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

Я потратил время, чтобы полностью воссоздать ваш вопрос в git-репо: SafariUOW .И для меня это работает просто отлично.

Почему кажется, что UnitOfWork работает как задумано, но я не вижу никаких изменений в базе данных?

Я предполагаю, что тампроблема в вашей базе данных с правами на чтение или запись, если она на самом деле не "Commit".

И как получается, что БД заблокирована, пока транзакцияScope / UnitOfWork уже ликвидирована.

Что ж, доступ к БД должен быть заблокирован, если транзакции или UnitOfWork ликвидированы.Возможно, вы создаете транзакцию с неправильным уровнем изоляции.Вы можете посмотреть в документах IsolationLevel msdn , каков правильный уровень для вашей области.

Пожалуйста, дайте мне знать, достаточно ли этот ответ для вашего вопроса.

С уважениемМарио

0 голосов
/ 01 декабря 2018

Ваш TransactionScope на самом деле не упаковывает код запроса.

Код, который вы закомментировали:

//using (TransactionScope scope = new TransactionScope())

- это то, как вы должны делать это вместе с другими, закомментированнымистрока:

//scope.Complete();
...