SonarQube: Code Quality and Security

Trong quá trình lập trình & phát triển phần mềm chúng ta thường hay gặp vấn đề về quản lý chất lượng code, code smell, dirty code hay technical debt, thậm chí tồn tại lỗ hổng bảo mật. Nhất là khi dự án có sự tham gia của nhiều member, với trình độ kinh nghiệm khác nhau. Hoặc khi dự án chưa có rules, coding conventions, coding styles rõ ràng. Đến một ngày nào đó cần maintainance dự án hay develop thêm feature mới, chúng ta mới giật mình nhìn lại đống code cũ, tại sao nó lại tệ hại như vậy.

SonarQube là một open source platform, được phát triển bởi SonarSource dành cho việc kiểm tra liên tục chất lượng code (code quality), review code một cách tự động để phát hiện ra các bugs, code smell, lỗ hổng bảo mật cho 25+ ngôn ngữ lập trình khác nhau. SonarQube hỗ trợ báo cáo duplicated code, coding standards, unit tests, code coverage, code complexity, comments, bugs, and security vulnerabilities.

Cài đặt SonarQube trên Windows

Hướng dẫn cài đặt và bắt đầu với SonarQube. Bạn có thể sử dụng Docker hoặc download file về chạy bình thường. Yêu cầu máy có Java JDK 11 trở lên. Cấu hình máy tối thiểu 2GB RAM. https://docs.sonarqube.org/latest/requirements/requirements/

Đầu tiên vào https://www.sonarqube.org/downloads/ chọn bản Community (miễn phí). Giải nén và tìm thư mục bin để chạy. Đối với Windows có thể cài thành service để tiện sử dụng.

Nếu gặp lỗi liên quan JAVA, các bạn chú ý cài lại Java JDK. Sau đó tìm conf/wrapper.conf sửa lại dòng sau:

wrapper.java.command=C:\Program Files\Java\jdk-12.0.2\bin\java

Truy cập: http://localhost:9000 username/password: admin/admin để kiểm tra. Nếu thành công thì chúng ta xong bước cài đặt. Tiếp theo tích hợp dự án vào SonarQube để phân tích.

Tích hợp dự án của bạn vào SonarQube

Sau khi đăng nhập thành công, click vào http://localhost:9000/projects/create để tạo dự án mới. Nhập key & tên dự án, sau đó chuyển sang màn hình nhập key, chọn loại dự án của bạn, và download file scanner của nó về, giải nén và thêm vào biến môi trường %PATH%. Ví dụ mình làm về Windows, dot net core và reactjs thì cần

sonar-scanner-4.0.0.1744-windows
sonar-scanner-msbuild-4.6.2.2108-net46
sonar-scanner-msbuild-4.6.2.2108-netcoreapp2.0

Sau đó mở project của bạn và chạy câu lệnh theo hướng dẫn để sonarqube phân tích.

SonarScanner.MSBuild.exe begin /k:"KEY_CUA_BAN" /d:sonar.host.url="http://localhost:9000" /d:sonar.login="API_KEY_CUA_BAN"

MsBuild.exe /t:Rebuild

SonarScanner.MSBuild.exe end /d:sonar.login="API_KEY_CUA_BAN"

Kết quả sau khi áp dụng

Chúc các bạn thành công.

Working with XML in C# and SQL Server

Đôi khi một lập trình viên có thể nhận một task nhất định liên quan đến việc thao tác dữ liệu XML trên SQL Server, hoặc đơn giản cần insert một list data từ C# xuống data SQL. Khi đó chúng ta có thể chuyển list data thành một XML rồi thao tác dễ dàng. Bài viết này sẽ hữu ích trong những tình huống như vậy.

Thực hiện convert một object, list object trong C# thành XML

Chúng ta có một đối tượng là Student cần chuyển đổi.

public class Student     
{        
        public int Id { get; set; }         
        public string Name { get; set; }   
        public int Age { get; set; }           public string Phone { get; set; }           public string Email { get; set; }           public double? Score { get; set; }   
}

Danh sách các Students

 var students = new List
            {
                new Student{Id =1, Name="An", Age=18, Email="[email protected]", Phone="123456789", Score=6.5 },
                new Student{Id =2, Name="Binh", Age=19, Email="[email protected]", Phone="64641313", Score=9.5 },
                new Student{Id =3, Name="Canh", Age=20, Email="[email protected]", Phone="525252525", Score=8 },
                new Student{Id =4, Name="Dat", Age=21, Email="[email protected]", Phone="525252525", Score=5 },
                new Student{Id =5, Name="Em", Age=22, Email="[email protected]", Phone="252525256879", Score=6 },
            };

Tạo một class ConvertToXmlExtension để viết các function convert list to xml

public static class ConvertToXmlExtension
{
	public static string XmlElementToString<T>(this IEnumerable<T> items)
	{
		var xEle = new XElement("Root",
			  from item in items
			  select new XElement("Row", ToXElement(item)));
		return xEle.ToString(SaveOptions.None);
	}

	public static string XmlAttributeToString<T>(this IEnumerable<T> items)
	{
		var xEle = new XElement("Root",
			  from item in items
			  select new XElement("Row", ToXAttribute(item)));
		return xEle.ToString(SaveOptions.None);
	}

	private static IEnumerable<XElement> ToXElement<T>(T item)
	{
		var elements = new List<XElement>();
		var listProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => !Attribute.IsDefined(x, typeof(XmlIgnoreAttribute)));

		foreach (var prop in listProperties)
		{
			var value = prop.GetValue(item, null);
                        if(value != null)
                        {
			    elements.Add(new XElement(prop.Name, value));
                        }
		}
		return elements;
	}

	private static IEnumerable<XAttribute> ToXAttribute<T>(T item)
	{
		var attributes = new List<XAttribute>();
		var listProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => !Attribute.IsDefined(x, typeof(XmlIgnoreAttribute)));

		foreach (var prop in listProperties)
		{
			var value = prop.GetValue(item, null);
                        if(value != null) 
                        {
			   attributes.Add(new XAttribute(prop.Name, value));
                        }
		}
		return attributes;
	}
}

Kết quả sau khi gọi function XmlElementToString

var xml = students.XmlElementToString();
Console.WriteLine(xml);
<Root>
  <Row>
    <Id>1</Id>
    <Name>An</Name>
    <Age>18</Age>
    <Phone>123456789</Phone>
    <Email>[email protected]</Email>
    <Score>6.5</Score>
  </Row>
  <Row>
    <Id>2</Id>
    <Name>Binh</Name>
    <Age>19</Age>
    <Phone>64641313</Phone>
    <Email>[email protected]</Email>
    <Score>9.5</Score>
  </Row>
  <Row>
    <Id>3</Id>
    <Name>Canh</Name>
    <Age>20</Age>
    <Phone>525252525</Phone>
    <Email>[email protected]</Email>
    <Score>8</Score>
  </Row>
  <Row>
    <Id>4</Id>
    <Name>Dat</Name>
    <Age>21</Age>
    <Phone>525252525</Phone>
    <Email>[email protected]</Email>
    <Score>5</Score>
  </Row>
  <Row>
    <Id>5</Id>
    <Name>Em</Name>
    <Age>22</Age>
    <Phone>252525256879</Phone>
    <Email>[email protected]</Email>
    <Score>6</Score>
  </Row>
</Root>
var xml = students.XmlAttributeToString();
Console.WriteLine(xml); 
<Root>
  <Row Id="1" Name="An" Age="18" Phone="123456789" Email="[email protected]" Score="6.5" />
  <Row Id="2" Name="Binh" Age="19" Phone="64641313" Email="[email protected]" Score="9.5" />
  <Row Id="3" Name="Canh" Age="20" Phone="525252525" Email="[email protected]" Score="8" />
  <Row Id="4" Name="Dat" Age="21" Phone="525252525" Email="[email protected]" Score="5" />
  <Row Id="5" Name="Em" Age="22" Phone="252525256879" Email="[email protected]" Score="6" />
</Root>

Thực hiện thao tác với XML trong SQL Server

Đối với XML Element


DECLARE @XMLData XML
SET @XMLData ='
<Root>
  <Row>
    <Id>1</Id>
    <Name>An</Name>
    <Age>18</Age>
    <Phone>123456789</Phone>
    <Email>[email protected]</Email>
    <Score>6.5</Score>
  </Row>
  <Row>
    <Id>2</Id>
    <Name>Binh</Name>
    <Age>19</Age>
    <Phone>64641313</Phone>
    <Email>[email protected]</Email>
    <Score>9.5</Score>
  </Row>
  <Row>
    <Id>3</Id>
    <Name>Canh</Name>
    <Age>20</Age>
    <Phone>525252525</Phone>
    <Email>[email protected]</Email>
    <Score>8</Score>
  </Row>
  <Row>
    <Id>4</Id>
    <Name>Dat</Name>
    <Age>21</Age>
    <Phone>525252525</Phone>
    <Email>[email protected]</Email>
    <Score>5</Score>
  </Row>
  <Row>
    <Id>5</Id>
    <Name>Em</Name>
    <Age>22</Age>
    <Phone>252525256879</Phone>
    <Email>[email protected]</Email>
    <Score>6</Score>
  </Row>
</Root>'
SELECT Id = Node.Data.value('(Id)[1]', 'INT')
        , [Name] = Node.Data.value('(Name)[1]', 'VARCHAR(MAX)')
        , [Age] = Node.Data.value('(Age)[1]', 'INT')
		, [Email] = Node.Data.value('(Email)[1]', 'VARCHAR(MAX)')
		, [Phone] = Node.Data.value('(Phone)[1]', 'VARCHAR(MAX)')
		, [Score] = Node.Data.value('(Score)[1]', 'FLOAT')
FROM    @XMLData.nodes('/Root/Row') Node(Data)

Đối với XML Attribute

DECLARE @XMLData XML

SET @XMLData = '<Root>
  <Row Id="1" Name="An" Age="18" Phone="123456789" Email="[email protected]" Score="6.5" />
  <Row Id="2" Name="Binh" Age="19" Phone="64641313" Email="[email protected]" Score="9.5" />
  <Row Id="3" Name="Canh" Age="20" Phone="525252525" Email="[email protected]" Score="8" />
  <Row Id="4" Name="Dat" Age="21" Phone="525252525" Email="[email protected]" Score="5" />
  <Row Id="5" Name="Em" Age="22" Phone="252525256879" Email="[email protected]" Score="6" />
</Root>'

DECLARE @idoc INT

EXEC sp_xml_preparedocument @idoc OUTPUT
	,@XMLData

SELECT data.Id
	,data.Name
	,data.Age
	,data.Phone
	,data.Email
	,data.Score
FROM OPENXML(@idoc, '/Root/Row', 1) WITH (
		Id INT
		,Name NVARCHAR(50)
		,Age INT
		,Phone NVARCHAR(50)
		,Email NVARCHAR(50)
		,Score FLOAT
		) AS data

EXEC sp_xml_removedocument @idoc

Kết quả

Chúc các bạn thành công.

Dãy số Fibonacci trong C#

Quy luật của dãy số Fibonacci: số tiếp theo bằng tổng của 2 số trước, 2 số đầu tiên của dãy số là 0, 1. Ví dụ: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …

Sử dụng yield trong C# để trả về dãy số Fibonacci

static void Main()
{
 foreach(var value in Fibonacci())
 {
 Console.Write(value + " ");
 if(value > 1000) 
 {
 break;
 }
 }
}

static IEnumerable<int>  Fibonacci()
{
 int current = 0;
 int next = 1;
 while(true)
 {
 yield return current;
 int oldCurrent = current;
 current = next;
 next = next + oldCurrent;
 } 
}

Kết quả:

0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Entity framework update modified fields only

Coding

 public virtual int Update(T entity, params Expression>[] properties)
        {
            if (entity.Id < 1)
            {
                return Insert(entity).Id;
            }
            if (properties?.Any() == true)
            {
                _dbContext.Attach(entity);
                foreach (var prop in properties)
                {
                    _dbContext.Entry(entity).Property(prop).IsModified = true;
                }
            }
            else
            {
                _dbContext.Entry(entity).State = EntityState.Modified;
            }

            return _dbContext.SaveChanges();
        }

        public virtual int Update(T entity, object replaceBy)
        {
            if (entity.Id < 1)
            {
                return Insert(entity).Id;
            }
            if (replaceBy == null)
            {
                return _dbContext.SaveChanges();
            }

            var properties = replaceBy.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);            
            if (properties?.Any() == true)
            {
                foreach (var prop in properties)
                {
                    var valueOfProp = prop.GetValue(replaceBy, null);
                    var propOfEntity = entity.GetType().GetProperty(prop.Name);
                    propOfEntity.SetValue(entity, Convert.ChangeType(valueOfProp, propOfEntity.PropertyType), null);

                    _dbContext.Entry(entity).Property(propOfEntity.Name).IsModified = true;
                }
            }
            else
            {
                _dbContext.Entry(entity).State = EntityState.Modified;
            }

            return _dbContext.SaveChanges();
        }

Using

var contact = new Contact { Id = 1, FullName = "Le Thanh Tuan" };

var rs1 = contactService.Update(contact, x => x.FullName);

var rs2 = contactService.Update(contact, new
{
	Id = 1,
	Phone = "0976060432",
	UserName = "tuanitpro",                
	FullName = "Le Thanh Tuan"                             
});

Logging with Serilog, Sentry, ASP.NET Core and ReactJs

Khi phát triển dự án, chúng ta luôn mong muốn sản phẩm làm ra chạy thật ngon, không lỗi, không phàn nàn từ khách hàng.

Tuy nhiên đời không như mơ, đôi khi sự cố vẫn xảy ra. Chính vì vậy logging là việc cần thiết, và cũng có nhiều công cụ hỗ trợ cho việc đó. Trong phạm vi bài viết này, các bạn sẽ được biết thêm một công cụ log ngon lành đơn giản dành cho .NET Core & ReactJs. Đó là https://serilog.net, http://sentry.io. Nếu bạn quan tâm thêm thì có thể tìm hiểu về log4net, nlog…. Có bài viết so sánh tại https://stackify.com/nlog-vs-log4net-vs-serilog/

Bản thân mình đánh giá thằng serilog ngon, mạnh mẽ, dễ áp dụng vào dự án. Nó hỗ trợ rất nhiều loại log: file, console, sentry, elasticsearch

Sentry.io hiểu đơn giản như một database lưu trữ & cung cấp theo dõi tất cả log của chúng ta. Có bản free, gửi email khi có issue. Đánh giá ngon. Muốn ngon hơn nữa thì dùng bản trả phí. Tuy nhiên bản miễn phí cũng đủ dùng cho dự án nhỏ rồi.

Continue reading Logging with Serilog, Sentry, ASP.NET Core and ReactJs

Handle errors in ASP.NET Core

if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
if (env.IsProduction() || env.IsStaging())
{
app.UseExceptionHandler(options =>
{
options.Run(async context =>
{
context.Response.StatusCode = 500;
context.Response.ContentType = “text/plain”;
await context.Response.WriteAsync(“CONTACT: 097 6060 432”);

var exceptionHandlerPathFeature =
context.Features.Get();
var now = System.DateTime.Now;
var eventLog = new EventLog
{
DateCreated = now,
RequestPath = exceptionHandlerPathFeature?.Path,
IPAddress = context.Request.HttpContext.Connection.RemoteIpAddress.ToString(),
Message = exceptionHandlerPathFeature?.Error.Source
};
string eventLogString = JsonConvert.SerializeObject(eventLog);

eventLogString += exceptionHandlerPathFeature?.Error.StackTrace + “\n\r”;

File.AppendAllText(“log.txt”, eventLogString);

ColorLife.Core.Helpers.IEmailProvider emailProvider = new ColorLife.Core.Helpers.EmailProvider(“[Urgent] Bug “, “[email protected]”);
emailProvider.Send(eventLogString, null);
});
});
app.UseHsts();
}

Sử dụng cache trong ASP.NET MVC với Redis Cache, Memory Cache

Khi lập trình ứng dụng chúng ta hay gặp vấn đề làm sao cho tối ưu tài nguyên của hệ thống, tăng tốc độ trải nghiệm từ phía end user. Đặc biệt là các ứng dụng web.

Cache là gì ? 
Lý thuyết: Cache là tên gọi của bộ nhớ đệm – nơi lưu trữ các dữ liệu nằm chờ các ứng dụng hay phần cứng xử lý. Mục đích của nó  để tăng tốc độ xử lý (có sẵn xài liền không cần tốn thời gian đi lùng sục tìm kéo về).

Thực tế: Cache là các dữ liệu trong phiên làm việc trước của các ứng dụng, chương trình mà hệ điều hành lưu lại nhằm giúp việc tải data trong các phiên làm việc sau được nhanh hơn.

Tip này hướng dẫn các bạn cách implement cache vào ứng dụng, sử dụng thư viện đã được wrap lại từ Memery Cache & Redis Cache

Đầu tiên tải về thư viện tại Nuget: https://www.nuget.org/packages/colorlife.tuanitpro.com/

Hoặc cài đặt trực tiếp trong Visual Studio

Tạo class Customer để test

public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

Sử dụng Memory Cache

  private static void MemoryCache_Customer_Test()
        {
            ICacheProviderFactory cacheProvideractory = new CacheProviderFactory();
            IDataCacheProvider dataCacheProvider = cacheProvideractory.CreateFactory();
            Customer customer = new Customer
            {
                FirstName = "Tuan",
                LastName = "Le"
            };
            string cacheKey = "Memory_Cache_Customer";
            dataCacheProvider.Set(cacheKey, customer);
            Console.WriteLine("Before cache: " + customer.FirstName + " " + customer.LastName);
             
            Console.WriteLine("After Update: " + customer.FirstName + " " + customer.LastName);

            var customerInCache = dataCacheProvider.Get(cacheKey);

            Console.WriteLine("Get Customer In MemoryCache: " + customerInCache.FirstName + " " + customerInCache.LastName);
        }

Sử dụng Radis Cache
Đầu tiên tải Redis cho Windows & cài đặt tại: https://github.com/MicrosoftArchive/redis/releases

Code:

  private static void RedisCache_Customer_Test()
        {
            ICacheProviderFactory cacheProvideractory = new CacheProviderFactory(new CacheProviderOptions
            {
                SlidingExpiration = TimeSpan.FromSeconds(30),
                ConnectionString = "localhost:6379" // mặc định
            }, CacheType.Redis);
            IDataCacheProvider dataCacheProvider = cacheProvideractory.CreateFactory();
            Customer customer = new Customer
            {
                FirstName = "Tuan",
                LastName = "Le"
            };
            string cacheKey = "RedisCache_Customer";
            dataCacheProvider.Set(cacheKey, customer);
            Console.WriteLine("Before cache: " + customer.FirstName + " " + customer.LastName);
            Thread.Sleep(5000);
            customer.LastName = "Le_Updated";
            Console.WriteLine("After Update: " + customer.FirstName + " " + customer.LastName);

            var customerInCache = dataCacheProvider.Get(cacheKey);

            Console.WriteLine("Customer In RedisCache: " + customerInCache.FirstName + " " + customerInCache.LastName);
        }

Thư viện còn hỗ trợ một số chức năng nho nhỏ khác, sẽ nói trong bài viết khác. Chúc các bạn thành công 🙂

binance price alert, coin watchlists using c#

Ứng dụng theo dõi giá của coin trên sàn Binance & thông báo khi đạt mức giá quy định.

Lấy danh sách coin trên Binance

var listSymbol = new List();
            using (var client = new BinanceClient())
            {
                var rs = await client.GetAllPricesAsync();
                if (rs.Success && rs.Data != null)
                {
                    foreach (var item in rs.Data)
                    {
                        listSymbol.Add(new SelectListItem
                        {
                            Value = item.Symbol,
                            Text = item.Symbol + " " + item.Price
                        });
                    }
                }
            }

 

Tham khảo: https://github.com/JKorf/Binance.Net

Source code: https://github.com/tuanitpro/binance-price-alert

Convert Image to Base64 String and Base64 String to Image

This article will help you to learn how we can convert an image into a base64 string and base64 string back to image.

Image to Base64 String

public string ImageToBase64(Image image, System.Drawing.Imaging.ImageFormat format)
{
  using (MemoryStream ms = new MemoryStream())
  {
    // Convert Image to byte[]
    image.Save(ms, format);
    byte[] imageBytes = ms.ToArray();

    // Convert byte[] to Base64 String
    string base64String = Convert.ToBase64String(imageBytes);
    return base64String;
  }
}

Base64 String to Image

public Image Base64ToImage(string base64String)
{
  // Convert Base64 String to byte[]
  byte[] imageBytes = Convert.FromBase64String(base64String);
  MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);

  // Convert byte[] to Image
  ms.Write(imageBytes, 0, imageBytes.Length);
  Image image = Image.FromStream(ms, true);
  return image;
}

Coding in real world

 


Result

Download: https://github.com/tuanitpro/base64toimage

Online test: http://codebeautify.org/base64-to-image-converter

Happy coding 🙂