– Bài viết dựa trên môi trường làm việc là MacOs. Vui lòng không làm theo nếu bạn dùng window.
– Yêu cầu kĩ năng research cơ bản.
– Biết sơ qua các khái niệm: docker, kitematic, sqlserver, terminal (lol).

Cài đặt môi trường:
B1: Cài docker và sqlserver, 2 thằng này thì khá rõ để làm gì rồi ha. (hướng dẫn)
B2: Cài Kitematic, để quản lý các container (link)
B3: Chạy docker, chạy kitematic và start sql trong docker.
Lưu ý: Bỏ qua bước 2 và bước 3 nếu đã chạy sqlserver từ bước 1. Tuy nhiên khuyến khích cài đủ, chạy theo các step 1-2-3.

Tạo ứng dụng:
B1: Tạo ứng dụng mới ví dụ: asp-net-core-code-first
B1′: Thêm 2 thư viện Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.Design
B2: Tạo class Person

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

B3: Tạo class PersonContext

public class PersonContext : DbContext
{
   DbSet<Person> Persons { get; set; }

   protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
   {
     optionsBuilder.UseSqlServer("Server=localhost\\sql_server_demo,1433;User=sa;Password=reallyStrongPwd123;Database=PersonDb1");
   }
}

B4: Chạy migrate trong terminal dotnet ef migrations add initial
B5: Update database dotnet ef database update
Done!
Tham khảo: ciclosoftware

bài trước mình đã giới thiệu với các bạn về UnitOfWork Design Pattern.

Nhắc lại vể lợi ích của UOW

  • Quản lý danh sách các đối tượng logic nghiệp vụ được thay đổi trong một transaction.
  • Khi một transaction được complete, tất cả thay đổi sẽ được gửi tới database như một big unit of work

Bài toán:

Để hình dung rõ hơn, lần này chúng ta sẽ xem xét bài toán thực tế khi quản trị hệ thống muốn xóa một đơn hàng. Chúng ta cần xóa dữ liệu ở 2 bảng OrdersOrderDetails.

Theo cách làm thông thường, chúng ta sẽ xóa dữ liệu bảng OrderDetails trước, sau đó xóa dữ liệu bảng Orders. Với cách làm này rủi ro xảy ra nếu sau khi xóa dữ liệu bảng OrderDetails xong, một exception xảy ra (ví dụ kinh điển là mất mạng) lúc này dữ liệu bảng Orders chưa được xóa, dẫn tới việc không đảm bảo tính toàn vẹn dữ liệu và có thể gây lỗi hệ thống khi truy xuất đơn hàng.

Để giải quyết bài toán này. Chúng ta sử dụng pattern UnitOfWork, thực hiện việc xóa dữ liệu ở 2 bảng trong cùng 1 transaction. Bất kì ngoại lệ nào xảy ra, dữ liệu sẽ được rollback. Nếu thao tác xóa dữ liệu thành công, transaction sẽ được commit.

Đồ nghề:

Xây dựng hệ thống

Cùng xem xét lại quan điểm:
Simplicity is the ultimate sophistication
Áp dụng thực tế vào bài toán khi làm việc với Database, để tránh việc code thừa ở khắp nơi khi gọi query dữ liệu. Trong ví dụ này chúng ta dùng thêm repository pattern để xử lý tầng kiến trúc làm việc với database.

Với tư tưởng như trên, chúng ta xây dựng lớp Repository chuyên xử lý các thao tác thông thường với database. Lớp OrderRepository xử lý các vấn đề đặc thù của Domain Order

Cấu trúc dự án tổng thể như sau:

Đầu tiên chúng ta xây dựng interface IRepository

public interface IRepository<TEntity> where TEntity : class
    {
        TEntity Get(int id);
        IEnumerable<TEntity> GetAll();
        IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);

        TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate);

        void Add(TEntity entity);
        void AddRange(IEnumerable<TEntity> entities);

        void Remove(TEntity entity);
        void RemoveRange(IEnumerable<TEntity> entities);
    }

Và lớp Repository implement interface IRepository

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected readonly DbContext Context;
        protected readonly DbSet<TEntity> DbSetEntity;

        public Repository(DbContext context)
        {
            Context = context;
            DbSetEntity = Context.Set<TEntity>();
        }

        public void Add(TEntity entity)
        {
            DbSetEntity.Add(entity);
        }

        public void AddRange(IEnumerable<TEntity> entities)
        {
            DbSetEntity.AddRange(entities);
        }

        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSetEntity.Where(predicate);
        }

        public TEntity Get(int id)
        {
            return DbSetEntity.Find(id);
        }

        public IEnumerable<TEntity> GetAll()
        {
            return DbSetEntity.ToList();
        }

        public void Remove(TEntity entity)
        {
            DbSetEntity.Remove(entity);
        }

        public void RemoveRange(IEnumerable<TEntity> entities)
        {
            DbSetEntity.RemoveRange(entities);
        }

        public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSetEntity.SingleOrDefault(predicate);
        }
    }

Interface IOrderRepository kế thừa từ IRepository

public interface IOrderRepository : IRepository<Orders>
{
    IEnumerable<Orders> GetTopOrders(int count);
    IEnumerable<Orders> GetOrdersWithCustomers(int pageIndex, int pageSize, string customerId);
}

Lớp OrderRepository kế thừa từ IOrderRepositoryRepository

public class OrderRepository : Repository<Orders>, IOrderRepository
{

    public OrderRepository(NorthwindContext context) : base(context)
    {
    }

    public IEnumerable<Orders> GetOrdersWithCustomers(int pageIndex, int pageSize, string customerId)
    {
        return NorthwindContext.Orders
            .Where(o => o.CustomerId == customerId)
            .OrderBy(c => c.OrderId)
            .Skip((pageIndex - 1) * pageSize)
            .Take(pageSize)
            .ToList();
    }

    public IEnumerable<Orders> GetTopOrders(int count)
    {
        return NorthwindContext.Orders.OrderByDescending(c => c.OrderId).Take(count).ToList();
    }

    public NorthwindContext NorthwindContext
    {
        get { return Context as NorthwindContext; }
    }
}

Tiếp theo chúng ta tạo interface IUnitOfWork

public interface IUnitOfWork : IDisposable
{
    IOrderRepository Orders { get; }
    ICustomerRepository Customers { get; }
    IOrderDetailRepository OrderDetails { get; }
    int Complete();
}

Và implement của nó là class UnitOfWork

public class UnitOfWork : IUnitOfWork
{
    private readonly NorthwindContext _context;

    public UnitOfWork(NorthwindContext context)
    {
        _context = context;
        Orders = new OrderRepository(_context);
        Customers = new CustomerRepository(_context);
        OrderDetails = new OrderDetailRepository(_context);
    }

    public IOrderRepository Orders { get; set; }

    public ICustomerRepository Customers { get; set; }

    public IOrderDetailRepository OrderDetails { get; set; }

    public int Complete()
    {
        return _context.SaveChanges();
    }

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

Vậy là chúng ta đã implement xong mô hình của hệ thống, áp dụng 2 partern là RepositoryUnitOfWork.

Tiếp theo chúng ta sẽ tới lớp Program và viết mã test hoạt động của hệ thống trong hàm Main

Class program sẽ trông như sau:

class Program
{
    static void Main(string[] args)
    {
        using (var unitOfWork = new UnitOfWork(new NorthwindContext()))
        {

            // Example1
            var customersByCity = unitOfWork.Customers.GetCustomerByCity("London");

            // Example2
            var customers = unitOfWork.Customers.GetAll();

            // Example3
            var order = unitOfWork.Orders.Get(10250);
            var orderDetails = unitOfWork.OrderDetails.GetByOrderId(order.OrderId);
            unitOfWork.OrderDetails.RemoveRange(orderDetails);
            unitOfWork.Orders.Remove(order);
            unitOfWork.Complete();

            Console.WriteLine("Done!");
        }
    }
}

Source code 

Trong dự án asp net core, khi các bạn sử dụng Generic Repository Pattern, làm cách nào để register generic interface và implementation generic interface bằng cách sử dụng ASP.NET Core DI container?

Ok, với một interface đơn giản và implementation của nó, ta sử dụng như sau

serviceCollection.AddSingleton<IService, MyService>();

Vậy còn kiểu Generic thì sao?

Chúng ta cùng xem lại phát biểu về phương thức AddSingleton

public static IServiceCollection AddSingleton<TService, TImplementation>(this IServiceCollection serviceswhere TService : class where TImplementation : class, Tservice

Phương pháp này sử dụng được với hầu hết các trường hợp, ngoại trừ các service generic..

Xét một ví dụ đơn giản, chúng ta có interface

public interface IThing<T>
{
    string GetName { get; }
}

và một implementation của nó như sau

public class GenericThing<T> : IThing<T>
{
    public GenericThing()
    {
        GetName = typeof(T).Name;
    }

    public string GetName { get; }
}

Câu hỏi đặt ra là khi sử dụng IThing<SomeType> làm sao chúng ta lấy được chính xác GenericThing<SomeType> đã được injected vào?

Trong trường hợp này chúng ta sử dụng phương thức mở rộng khác trong ServiceCollection, bằng cách như sau

serviceCollection.AddSingleton(typeof(IThing<>), typeof(GenericThing<>));

Ok, giờ chúng ta có thể sử dụng DI bất cứ đâu cần Injected

public class ValuesController : Controller
{
    private readonly IThing<ValuesController> _thing;

    public ValuesController(IThing<ValuesController> thing)
    {
        _thing = thing;
    }
}

Trông cũng dễ hiểu phải không 🙂

Hãy comment đặt câu hỏi nếu các bạn cần trao đổi thêm về asp net core hoặc DI nhé!

Lang thang trên mạng tình cờ vớ được bí kíp. Đôi khi trong các dự án các bạn cần tạo đối tượng với dữ liệu từ chuỗi string dạng JSON.

Đơn giản rồi, với 3 bước nho nhỏ sau công việc của các bạn sẽ đỡ hơn rất nhiều:

  1. Vào link http://json2csharp.com/ (lạy giời nó đừng chết sau n times mình dùng)
  2. Paste chuỗi JSON vào (hoặc bạn có thể paste thẳng URL của API vào)
  3. Click Generate và xem kết quả.

Các bạn có thể thử với link này nhé: http://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22

Awesome!!! Tưởng không phê mà phê không tưởng.

Ra luôn Class

Good luck and happy coding.

Dạo gần đây mới làm việc thêm với wordpress, thấy nó cũng thú vị nên muốn làm cái tutorials vừa để chia sẻ vừa để note lại những gì mình thu lượm được.

Do series này sẽ tập trung vào việc lập trình một theme WordPress nên các kiến thức cơ bản như HTML và CSS mình sẽ không nói qua. Khuyến khích các bạn nên có các kiến thức như:Continue reading

Virtual Host (Vhost) là một cấu hình trong Apache để cho phép nhiều domain cùng chạy trên một máy chủ. Để dễ hình dung hơn khi các bạn chạy webapps ở môi trường phát triển sẽ có url localhost:1234. Giờ ta có thể làm url trở nên thân thiện hơn bằng các chuyển nó thành dạng giống domain: thaotrinh.dev:1234
Trong bài viết này mình chỉ muốn chia sẻ với các bạn còn lạ lẫm về Vhost cách để cấu hình một Virtual Host trong Windows, ở đây mình sử dụng XAMPP làm môi trường phát triển.Continue reading