Использование интерфейся для Unit of Work

371
13 февраля 2017, 13:20

GitHub-ссылка на проект

К собеседованию было дано тестовое задание:

Create a web application that will monitor changes on a SQL table using SignalR Requirements

1) The SQL table should have the following definition: CREATE TABLE DevTest( [ID] int IDENTITY(1, 1) NOT NULL, [CampaignName] varchar(255) NULL, [Date] datetime NULL, [Clicks} int NULL, [Conversions] int NULL, [Impressions] int NULL, [AffiliateName] varchar(255) NULL )

2) All data access should adhere to the repository and unit of work design patterns

3) There should be a view that implements SignalR and displays the changes to the SQL table in real time.

4) There should be a view that allows the user to make changes to the DevTest table (insert, update, delete)

, где как видите, одним из условий было использование паттерна Unit of Work.

Вот часть моего решения (с полным решением вы можете ознакомиться по ссылке выше/в конце этого поста) касаемо UoW:

IRepository.cs

namespace MonitDbTable.Interfaces
{
    public interface IRepository<T> where T : class
    {
        IEnumerable<T> GetAll();
        T Get(int id);
        void Add(T item);
        void Update(T item);
        void Delete(int id);
    }
}

DevTest.cs

namespace MonitDbTable.Models
{
    public class DevTest
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public string CompanyName { get; set; }
        [DataType(DataType.Date)]        
        public DateTime? Date { get; set; }
        public int? Clicks { get; set; }
        public int? Conversions { get; set; }
        public int? Impressions { get; set; }
        public string AffiliateName { get; set; }
    }
}

DevTestRepository.cs

namespace MonitDbTable.Repositories
{
    public class DevTestRepository : IRepository<DevTest>
    {
        private readonly string _connString = ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString;
        private DevTestContext db;
        public DevTestRepository(DevTestContext db)
        {
            this.db = db;
        }
        public void Add(DevTest item)
        {
            db.DevTests.Add(item);
        }
        public void Delete(int id)
        {
            var item = db.DevTests.Find(id);
            db.DevTests.Remove(item);
        }
        public DevTest Get(int id)
        {
            return db.DevTests.Find(id);
        }
        public IEnumerable<DevTest> GetAll()
        {
            var devTests = new List<DevTest>();
            using (var connection = new SqlConnection(_connString))
            {
                connection.Open();
                using (var command = new SqlCommand(@"SELECT [Id], [CompanyName], [Date], [Clicks], [Conversions], [Impressions], [AffiliateName] FROM [dbo].[DevTests]", connection))
                {
                    command.Notification = null;
                    var dependency = new SqlDependency(command);
                    dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
                    if (connection.State == ConnectionState.Closed)
                        connection.Open();
                    var reader = command.ExecuteReader();
                    while (reader.Read())
                    {                        
                        devTests.Add(item: new DevTest
                        {
                            Id = (int)reader["Id"],
                            CompanyName = reader["CompanyName"] != DBNull.Value ? (string)reader["CompanyName"] : "",
                            Date = reader["Date"] != DBNull.Value ? Convert.ToDateTime(reader["Date"]) : (DateTime?)null,
                            Clicks = reader["Clicks"] != DBNull.Value ? (int)reader["Clicks"] : 0,
                            Conversions = reader["Conversions"] != DBNull.Value ? (int)reader["Conversions"] : 0,
                            Impressions = reader["Impressions"] != DBNull.Value ? (int)reader["Impressions"] : 0,
                            AffiliateName = reader["AffiliateName"] != DBNull.Value ? (string)reader["AffiliateName"] : ""
                        });
                    }
                }
            }
            return devTests;
        }
        private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            if (e.Type == SqlNotificationType.Change)
            {
                DevTestsHub.SendMessages();
            }
        }
        public void Update(DevTest item)
        {
            db.Entry<DevTest>(item).State = System.Data.Entity.EntityState.Modified;
        }
    }
}

UnitOfWork.cs

namespace MonitDbTable.Repositories
{
    public class UnitOfWork : IDisposable
    {
        private DevTestContext db = new DevTestContext();
        private DevTestRepository dtRepository;
        public DevTestRepository DevTests
        {
            get
            {
                if (dtRepository == null)
                    dtRepository = new DevTestRepository(db);
                return dtRepository;
            }
        }
        public void Save()
        {
            db.SaveChanges();
        }
        private bool disposed = false;
        public virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    db.Dispose();
                }
                this.disposed = true;
            }
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}

ЗАМЕЧАНИЕ ИНТЕРВЬЮЕРА

Интервьюер поставил мне два плюса (2+) и 4- по этому заданию. И один из минусов гласит:

Didn't use interface for unit of work

Собственно именно по этому "минусу" я и хочу чтобы вы меня проконсультировали и помогли разобраться в том как правильно использовать UoW.

Мои знания и попытки

  • в тех статьях, где я знакомился с этим паттерном, как-то не делался акцент на использовании интерфейсов или я не обратил внимание;
  • я и не до конца понял, что именно означает эта строчка замечания: имеется ввиду необходимость объявления интерфейса IUnitOfWork или автор имел ввиду в класс UnitOfWork внедрить зависимость от репозитория DevTestRepository?
  • в Интернете не так-то и много статей где можно встретить IUnitOfWork. К примеру, есть один пост на Code Review. Но думаю этот случай у парня очень специфичный, так как у него там IUnitOfWork UnitOfWork { get; } в качестве свойства у interface IRepository<T>.

Но одно я исправил - внедрил зависимость от контекста:

public UnitOfWork(DevTestContext db)
{
    this.db = db;
}

Иначе "не возможно" было тестировать WebApi-методы, которые обращаются к БД через UoW.

GitHub-ссылка на проект

READ ALSO
Есть проблема с \t

Есть проблема с \t

Здравствуйте, уважаемыеС помощью ответов из предыдущего вопроса я немного усовершенствовал свой код

284
Xamarin Android совет новичку

Xamarin Android совет новичку

Вопросов несколько:

356
Как отправить ответ в командную строку сразу после команды?

Как отправить ответ в командную строку сразу после команды?

В примере показано как работает код, но нужно чтобы ответ приходил не следующей командой, а продолжением запроса

339
Привязка к IsEnabled

Привязка к IsEnabled

XAML часть:

258