C# Làm sao thoát khỏi if / switch case quá nhiều?

Câu lệnh điều kiện if hay switch case là thường gặp trong lập trinh. Tuy nhiên khi mà if quá nhiều trong một block code, hay switch case quá nhiều thì nó sẽ gây phức tạp và dễ xảy ra bug. Cũng như khó khăn khi viết Unit Tests. Clean code khuyên rằng không nên dùng quá 4 câu lệnh if trong 1 block code.
Vậy đâu là giải pháp để tránh vấn đề này. Trong .NET có hỗ trợ một kiểu dữ liệu đó là Dictionary. Bạn sẽ gặp đâu đó nói rằng Dictionary còn phức tạp hơn, khó tiếp cận hơn if condition nữa. Điều đó là đúng, tuy nhiên qua các ví dụ dưới đây sẽ giúp bạn thấy dễ dàng làm chủ Dictionary hơn, và tránh phải if else, hay switch case quá nhiều.

Vậy Dictionary là gì?

Dictionary is a data structure for storing a group of objects. Objects are stored as key/value pairs collection which is useful and generic in different object oriented programming languages.

Dictionary can be used for mappings, in-memory cache, tables etc.

Dictionary’s key and value are generic types. In this way delegates can be set as key or value.

Hiểu đơn giản, Dictionary trong C# là một Collections lưu trữ dữ liệu dưới dạng cặp Key – Value. Key đại diện cho 1 khoá giống như chỉ số phần tử của mảng và Value chính là giá trị tương ứng của khoá đó. Ta sẽ dử dụng Key để truy cập đến Value tương ứng.

Chúng ta có bài toán cho phép ứng dụng có thể kết nối đến nhiều loại data source khác nhau. MySql, SqlServer, MongoDb, Redis, Xml…

ConnectionDataHelper.cs

public class ConnectionDataHelper
    {
        public static void ConnectToMySql()
        {
            Console.WriteLine("Connect to MySql");
        }

        public static void ConnectToSqlServer()
        {
            Console.WriteLine("Connect to Sql Server");
        }

        public static void ConnectToMongoDb()
        {
            Console.WriteLine("Connect to MongdoDb");
        }

        public static void ConnectToRedis()
        {
            Console.WriteLine("Connect to Redis");
        }

        public static void ConnectToXml()
        {
            Console.WriteLine("Connect to Xml");
        }
    }

Ví dụ dùng if else.

    
internal class Program
    {
        private static void Main(string[] args)
        {
            DataSource dataSource = DataSource.MySql;
            Console.WriteLine("Connection to DB Source: " + dataSource.ToString());
            if (dataSource == DataSource.MySql)
            {
                ConnectionDataHelper.ConnectToMySql();
            }
            else if (dataSource == DataSource.SqlServer)
            {
                ConnectionDataHelper.ConnectToSqlServer();
            }
            else if (dataSource == DataSource.MongoDb)
            {
                ConnectionDataHelper.ConnectToMongoDb();
            }
            else if (dataSource == DataSource.Redis)
            {
                ConnectionDataHelper.ConnectToRedis();
            }
            else if (dataSource == DataSource.Xml)
            {
                ConnectionDataHelper.ConnectToXml();
            }

            Console.ReadKey();
        }

        private enum DataSource
        {
            MySql,
            SqlServer,
            MongoDb,
            Redis,
            Xml
        }
    }

Cách viết switch case dễ đọc hơn một chút, nhưng vẫn còn dài

internal class Program
    {
        private static void Main(string[] args)
        {
            DataSource dataSource = DataSource.MySql;
            Console.WriteLine("Connection to DB Source: " + dataSource.ToString());
            switch (dataSource)
            {
                case DataSource.MySql:
                    ConnectionDataHelper.ConnectToMySql();
                    break;

                case DataSource.SqlServer:
                    ConnectionDataHelper.ConnectToSqlServer();
                    break;

                case DataSource.MongoDb:
                    ConnectionDataHelper.ConnectToMongoDb();
                    break;

                case DataSource.Redis:
                    ConnectionDataHelper.ConnectToRedis();
                    break;

                case DataSource.Xml:
                    ConnectionDataHelper.ConnectToXml();
                    break;
            }

            Console.ReadKey();
        }

        private enum DataSource
        {
            MySql,
            SqlServer,
            MongoDb,
            Redis,
            Xml
        }
    }

Bây giờ chúng ta thử chuyển sang sử dụng Dictionary.

internal class Program
    {
        private static void Main(string[] args)
        {
            var actionMap = new Dictionary()
            {
                {DataSource.MySql, ConnectionDataHelper.ConnectToMySql},
                {DataSource.SqlServer, ConnectionDataHelper.ConnectToSqlServer},
                {DataSource.MongoDb, ConnectionDataHelper.ConnectToMongoDb},
                {DataSource.Redis, ConnectionDataHelper.ConnectToRedis},
                {DataSource.Xml, ConnectionDataHelper.ConnectToXml},
            };
            DataSource dataSource = DataSource.SqlServer;
            Console.WriteLine("Connection to DB Source: " + dataSource.ToString());
            if (actionMap.ContainsKey(dataSource))
            {
                actionMap[dataSource]();
            }

            Console.ReadKey();
        }

        private enum DataSource
        {
            MySql,
            SqlServer,
            MongoDb,
            Redis,
            Xml
        }
    }

Rõ ràng code đã ngắn gọn hơn rất nhiều. Tất nhiên thực tế các function của chúng ta sẽ phức tạp hơn một chút, có input, output. Bây giờ sửa lại ConnectionDataHelper để nhận vào một connectionString, và return về một trạng thái true/false. IsConnected.

ConnectionDataHelper.cs

    public class ConnectionDataHelper
    {
        public static bool ConnectToMySql(string connectionString)
        {
            Console.WriteLine("Connect to MySql: " + connectionString);
            return true;
        }

        public static bool ConnectToSqlServer(string connectionString)
        {
            Console.WriteLine("Connect to Sql Server: " + connectionString);
            return true;
        }

        public static bool ConnectToMongoDb(string connectionString)
        {
            Console.WriteLine("Connect to MongdoDb: " + connectionString);
            return true;
        }

        public static bool ConnectToRedis(string connectionString)
        {
            Console.WriteLine("Connect to Redis: " + connectionString);
            return true;
        }

        public static bool ConnectToXml(string connectionString)
        {
            Console.WriteLine("Connect to Xml: " + connectionString);
            return true;
        }
    }
internal class Program
    {
        private static void Main(string[] args)
        {
            var actionMap = new Dictionary>()
            {
                {DataSource.MySql, ConnectionDataHelper.ConnectToMySql},
                {DataSource.SqlServer, ConnectionDataHelper.ConnectToSqlServer},
                {DataSource.MongoDb, ConnectionDataHelper.ConnectToMongoDb},
                {DataSource.Redis, ConnectionDataHelper.ConnectToRedis},
                {DataSource.Xml, ConnectionDataHelper.ConnectToXml},
            };
            DataSource dataSource = DataSource.SqlServer;
            Console.WriteLine("Connection to DB Source: " + dataSource.ToString());
            var connectionString = "DataSource=; UserId=; Password=";
            bool isConnected = false;
            if (actionMap.ContainsKey(dataSource))
            {
                isConnected = actionMap[dataSource](connectionString);
            }
            Console.WriteLine("Is Connected" + isConnected);
            Console.ReadKey();
        }

        private enum DataSource
        {
            MySql,
            SqlServer,
            MongoDb,
            Redis,
            Xml
        }
    }

Kết quả:

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

Cấu hình Jenkins Reverse Proxy cho IIS

Bước 1: Cài đặt IIS, thêm sites mới cho Jenkins.

Bước 2: Cài đặt URL Rewrite và IIS ARR

Bước 3: Chọn Application Request Routing Cache -> Server Proxy Settings

Bước 4: Cấu hình IIS site jenkins theo domain

Bước 5: Thêm file web.config vào folder chứa jenkins

Bước 6: Copy nội dung web.config dưới đây vào file của bạn, thay nội dung domain thành của bạn

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<outboundRules>
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1" enabled="true">
<match filterByTags="A, Form, Img" pattern="^http(s)?://localhost:80/(.*)" />
<action type="Rewrite" value="http{R:1}://jenkins.tuanitpro.com/{R:2}" />
</rule>
<rule name="RestorageAccessEncoding" preCondition="RestorageAccessEncoding" enabled="true">
<match serverVariable="HTTP_ACCEPT_ENCODING" pattern="^(.*)" />
<action type="Rewrite" value="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
<preCondition name="RestorageAccessEncoding">
<add input="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" pattern=".+" />
</preCondition>
</preConditions>
</outboundRules>
<rules>
<rule name="ReverseProxyInboundRule1" enabled="true" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://localhost:80/{R:1}" />
<serverVariables>
<set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCONDING}" />
<set name="HTTP_ACCEPT_ENCODING" />

<set name="HTTP_FORWARDED" value="for={REMOTE_ADDR};by={LOCAL_ADDR};host=&quot;{HTTP_HOST}&quot;;proto=&quot;https&quot;" />
</serverVariables>

</rule>
</rules>
</rewrite>
<urlCompression doStaticCompression="false" />
</system.webServer>
</configuration>

Nguồn tham khảo:

– https://wiki.jenkins.io/display/JENKINS/Running+Jenkins+behind+IIS

– https://rlevchenko.com/2018/12/27/using-iis-as-a-reverse-proxy-for-jenkins/

Phần mềm ERP là gì?

Là nhà quản trị, chắc hẳn bạn luôn quan tâm tới những giải pháp công nghệ hữu ích cho quản lý và điều hành doanh nghiệp. ERP là một mô hình phần mềm ra đời với mục đích như vậy. Tuy nhiên, trải qua thời gian và chịu ảnh hưởng bởi làn sóng công nghệ 4.0, phần mềm ERP không còn chiếm thế độc quyền được nữa.

1. Phần mềm ERP là gì?

 

ERP (viết đầy đủ là Enterprise Resource Planning) nghĩa là hoạch định nguồn lực của doanh nghiệp.

 

Phần mềm ERP hiểu đơn giản là một mô hình công nghệ all-in-one, tích hợp nhiều ứng dụng khác nhau thành các module của một gói phần mềm duy nhất, giúp tự động hoá từ A đến Z các hoạt động liên quan tới tài nguyên của doanh nghiệp. Mục đích của phần mềm ERP là tạo ra một hệ thống dữ liệu tự động hợp nhất và xuyên suốt qua các phòng ban và khâu hoạt động như quản lý mua hàng, quản lý sản xuất, quản lý nhân sự,…

 

erp-la-gi-01

Mô hình cấu trúc của ERP với các phân hệ chính (Nguồn ảnh: www.fast.com.vn)

 

Một hệ thống ERP đầy đủ sẽ bao gồm các phân hệ sau:

 

  • Kế toán tài chính (Finance)
  • Lập kế hoạch và quản lý sản xuất (Production Planning and Control)
  • Quản lý mua hàng (Purchase Control)
  • Quản lý bán hàng và phân phối (Sales and Distribution)
  • Quản lý dự án (Project Management)
  • Quản lý nhân sự (Human Resource Management)
  • Quản lý dịch vụ (Service Management)
  • Quản lý hàng tồn kho (Stock Control)
  • Báo cáo thuế (Tax Reports)
  • Báo cáo quản trị (Management Reporting)

 

Một số phần mềm ERP hiện đại còn có thêm các giải pháp liên kết các module cố định với thiết bị hỗ trợ như điện thoại di động, thiết bị quét mã vạch, máy tính cầm tay,…

 

2. Đặc trưng của phần mềm ERP

 

Để phân biệt với các giải pháp quản trị doanh nghiệp khác, phần mềm ERP có 4 đặc điểm chính sau:

 

1. ERP là một hệ thống quản trị sản xuất kinh doanh hợp nhất. Mọi thành viên doanh nghiệp (từ nhà quản lý tới nhân viên), mọi công đoạn và phòng ban chức năng xâu chuỗi thành một quá trình hoạt động sản xuất kinh doanh có trật tự.

 

2. ERP là một hệ thống phần mềm hỗ trợ chứ không phải dây chuyền sản xuất tự động thay thế sức người.

 

3. ERP là một hệ thống quản lý hoạt động theo quy tắc và kế hoạch rõ ràng. Các nhân viên với nhiệm vụ cụ thể cần được xác định từ trước cùng với quy định nhất quán, chặt chẽ; kế hoạch sản xuất kinh doanh phải được lập ra theo định kỳ tuần, tháng, năm.

 

4. ERP là hệ thống liên kết giữa các phòng ban trong công ty để chúng cùng làm việc, trao đổi, cộng tác qua lại với nhau chứ không phải mỗi phòng ban là một cát cứ hoạt động riêng lẻ.

 

3. Lợi ích của ERP đối với doanh nghiệp

 

Bởi lẽ tất cả quy trình làm việc đều được thực hiện trên ERP, nên doanh nghiệp có một cơ sở dữ liệu tập trung duy nhất. Đó là tiền đề đầu tiên cho việc “data mining” – phân tích các dữ liệu theo nhiều góc khác nhau nhằm trợ giúp đưa ra quyết định kinh doanh.

 

ERP giúp kiểm soát thông tin tài chính

 

Thông thường, thông tin tài chính sẽ phải tập hợp số liệu từ nhiều bộ phận khác nhau nên chắc chắn có độ chênh lệch nhất định. Khi sử dụng giải pháp ERP, mọi thứ liên quan đến tài chính được tổng hợp lại ở một nơi – một phiên bản duy nhất xuyên suốt tất cả các phòng ban, cơ sở. Khi một con số được thay đổi, tất cả thông tin liên quan đều được tự động tính toán và hiển thị lại cho trùng khớp, giúp hạn chế tiêu cực trong tài chính doanh nghiệp.

 

Cũng nhờ sự hỗ trợ của phần mềm quản lý, các doanh nghiệp lớn và phức tạp không cần phải đợi đến cuối tháng hoặc cuối quý mới có thể tổng hợp số liệu nữa. Bất cứ khi nào muốn có một báo cáo tài chính chính xác và kịp thời cho lãnh đạo, chỉ cần nhìn vào những con số sau cùng của dòng dữ liệu trên ERP.

 

ERP giúp tăng tốc độ dòng công việc

 

Doanh nghiệp càng lớn thì quy trình làm việc càng nhiều bước, nhiều khâu giống như một mạng lưới phức tạp. Tốc độ dòng công việc phụ thuộc vào hai yếu tố chính: có xác định được đúng nơi cần chuyển dữ liệu đến không, và trong  quá trình chuyển giao đó có gặp phải chướng ngại vật gì.

 

Rõ ràng việc chuyển chứng từ bằng giấy tới tay một nhân viên xác định không thể sánh bằng tốc độ của chứng từ điện tử. Hay trong việc vận chuyển hàng hoá từ kho bãi tới các cơ sở kinh doanh, một quyết định được đồng bộ lên hệ thống ERP sẽ tới được tay người thủ kho rất nhanh chóng. Bằng cách giải quyết các “nút cổ chai” và rút gọn khoảng cách địa lý, phần mềm ERP giúp tăng tốc độ dòng công việc trong doanh nghiệp.

ERP giúp hạn chế sai sót khi nhiều người cùng nhập một dữ liệu

 

Trên thực tế, nhiều khi doanh nghiệp gặp rắc rối bởi nhầm lẫn dữ liệu khi đi qua các bộ phận làm việc khác nhau. Chẳng hạn, con số viết tay “14” thùng hàng rất dễ nhầm thành “19”, hay lỗi gõ Word biến khách hàng ” Phạm Quỳnh” thành khách hàng “Phạm Quyên”. Những sai lầm như vậy ảnh hưởng không nhỏ tới tiến độ làm việc của cả quy trình, làm xấu đi hình ảnh doanh nghiệp, suy giảm tính minh bạch, hơn nữa còn gây mất đoàn kết nội bộ nhân viên.

 

Với ERP, dữ liệu chỉ cần được nhập một lần duy nhất bởi người đầu tiên rồi được lưu trữ nguyên vẹn trên hệ thống. Bất cứ nhân viên nào khác trong doanh nghiệp đều tiếp cận tới dữ liệu gốc này chứ không phải một bản copy “tam sao thất bản”.

ERP giúp dễ dàng kiểm soát quá trình làm việc của nhân viên

 

Một cơ sở dữ liệu tập trung và các quy trình nghiệp vụ được sắp xếp thành dòng cố định sẽ giúp doanh nghiệp dễ dàng áp dụng các cơ chế kiểm soát nội bộ. Chức năng Audit track (tìm vết) của phần mềm ERP cho phép nhanh chóng tìm ra nguồn gốc những bút toán cần kiểm tra cũng như những nhân viên liên quan đến bút toán đó.

 

Việc giám sát từng khâu làm việc của nhân viên cũng được tối ưu. Nhà quản lý chỉ cần ở một nơi, mở giao diện hợp nhất của ERP ra là có thể nắm trong tay tất cả kết quả làm việc của tất cả nhân viên, từ những con số nhỏ nhất như trong buổi sáng nay nhân viên đó đã bán các sản phẩm nào và đem về doanh thu bao nhiêu.

 

Một số phần mềm ERP còn có tính năng tự động phân tích cơ sở dữ liệu để gán nhân viên vào nhiệm vụ phù hợp với thế mạnh của họ, nhà quản lý không phải mất nhiều thời gian cho công đoạn này.

 

ERP giúp tạo ra mạng xã hội nội bộ trong doanh nghiệp

 

Phần mềm ERP thường tích hợp tính năng liên lạc nội bộ giữa các người dùng thuộc cùng một hệ thống. Đó là việc chat riêng tư hoặc cập nhật trạng thái cá nhân giống như cơ chế hoạt động của một mạng xã hội nội bộ thực thụ.

 

erp-la-gi-02

ERP tạo ra một mạng xã hội nội bộ trong doanh nghiệp

 

4. Hạn chế của phần mềm ERP

 

Tuy mang lại nhiều lợi ích cho doanh nghiệp, nhưng có rất nhiều ý kiến cho rằng giải pháp ERP đã lỗi thời trong kỷ nguyên 4.0 – khi công nghệ đang cải tiến liên tục và hướng tới sự nhỏ gọn, chuyên biệt hoá từng bộ phận.

 

ERP đòi hỏi chi phí sử dụng lớn nhưng không đáp ứng đúng nhu cầu của doanh nghiệp

 

ERP không cho phép tách lẻ từng ứng dụng phục vụ các công đoạn làm việc khác nhau của doanh nghiệp mà cố định trong một gói tổng hợp với chi phí rất lớn (ít nhất là 30.000 $ – dựa trên báo giá của các nhà cung cấp). Tuy nhiên, đa số doanh nghiệp lại không cần thiết sử dụng tất cả các phân hệ trong đó, khiến cho việc mua cả gói ERP mà không dùng hết bị lãng phí nghiêm trọng. Chưa kể tới những ứng dụng thừa đó không thể xoá đi mà vẫn tồn tại cồng kềnh ở đó.

 

Lại có một số doanh nghiệp cần dùng thêm các phần mềm đặc thù khác để đảm bảo hoạt động trơn tru. Khi đó, vấn đề lớn nhất là làm sao để ERP tích hợp tốt với các giải pháp bên ngoài này, từ việc trao đổi dữ liệu cho đến quy trình làm việc. Tất nhiên, việc này không hề dễ dàng, vì ERP gần như được lập trình cố định.

 

Như vậy, về cấu trúc phần mềm, ERP bị cồng kềnh và cứng nhắc so với đa số doanh nghiệp. Lại thêm mức chi phí sử dụng đắt đỏ, doanh nghiệp cần thực sự thận trọng khi quyết định sử dụng ERP hay không.

ERP đòi hỏi tốc độ triển khai chậm chạp, mất nhiều công sức

 

Việc triển khai một giải pháp công nghệ phụ thuộc rất nhiều vào tốc độ làm việc của bên cung cấp phần mềm và tốc độ làm quen với phương thức làm việc mới của doanh nghiệp. Đáng tiếc là với ERP, cả hai yếu tố này đều tiêu tốn nhiều thời gian và công sức.

 

Trước hết là việc tích hợp đầy đủ hệ thống cồng kềnh của ERP vào doanh nghiệp. Công đoạn này yêu cầu phải trang bị máy chủ trung tâm dữ liệu và cơ sở hạ tầng mạng tới tất cả “ngõ ngách” nhỏ nhất của doanh nghiệp. Vấn đề bảo mật, yêu cầu sao lưu và khôi phục dữ liệu cũng phải tính toán kỹ lưỡng để đảm bảo ổn định vận hành cho toàn hệ thống. Về việc nhân viên trong doanh nghiệp sẽ sử dụng ERP như thế nào, có nhiều ý kiến trái chiều, trong đó nổi bật lên là sự lo ngại về việc phải ngay lập tức thay đổi cách vận hành của cả một bộ máy doanh nghiệp.

 

ERP gây gia tăng rủi ro trong quá trình sản xuất, kinh doanh

 

Việc đơn giản hoá dòng dữ liệu trên một hệ thống duy nhất sẽ rất thuận lợi khi ERP hoạt động trơn tru. Tuy nhiên, chỉ cần phát sinh một vấn đề trong khâu bất kỳ, một công đoạn làm việc sẽ bị tắc nghẽn, kéo theo sự đình trệ của toàn bộ quy trình phía sau. Cũng phải hiểu rằng việc triển khai ERP không chỉ ảnh hưởng đến một phần nhất định của doanh nghiệp mà là tất cả các bộ phận, hoạt động. Nhà quản trị doanh nghiệp không được phép liều mình với ERP, bởi cái giá phải trả nếu giải pháp này không phù hợp là quá lớn: doanh nghiệp bị “chết” trong suốt quãng thời gian dài.

 

ERP rất khó nâng cấp khi doanh nghiệp cần thay đổi

 

Các nhà cung cấp giải pháp ERP phải đáp ứng yêu cầu của nhiều nhóm với các nhu cầu, quy trình và mục tiêu rất khác nhau. Kết quả là, hầu hết các giải pháp ERP đều chỉ có thế mạnh trong một lĩnh vực – như tài chính – và yếu hơn nhiều ở những phân hệ khác. Một vấn đề nữa là doanh nghiệp luôn mong muốn được cải tiến công nghệ để thức thời hơn trong kỷ nguyên 4.0. Giải pháp ERP gặp phải bất lợi lúc này, khi mà nếu muốn thay đổi dù chỉ một tính năng, doanh nghiệp sẽ phải tạm ngưng hoạt động và đưa cả hệ thống ERP cồng kềnh ra để lập trình lại.

 

Trong khi có, các nhà cung cấp phần mềm chuyên biệt lại không ngừng cải tiến và đưa ra phiên bản nâng cấp miễn phí cho khách hàng đang sử dụng. Họ cũng sẵn sàng tư vấn và hỗ trợ 24/7, chứ không phải như với ERP – doanh nghiệp mua một hệ thống về và phải tự tìm cách xoay sở với nó.

 

5. Mô hình quản lý bằng ERP phù hợp với doanh nghiệp như thế nào?

 

Doanh nghiệp có thể cân nhắc giải pháp phần mềm ERP khi nhìn nhận chính xác mình đã ở một trong 3 tình huống sau:

 

  1. Doanh nghiệp thường xuyên xảy ra sai sót trong quá trình nhập / xuất và chuyển dữ liệu, ví dụ như chênh lệch số lượng hàng hoá tồn kho, nhầm lẫn giao hàng cho khác, chồng chéo thông tin hoá đơn,… hoặc ngày càng nhiều khách hàng trung thành than phiền về chất lượng sản phẩm / dịch vụ.
  2. Doanh nghiệp bắt đầu tăng nhanh về khối lượng giao dịch kinh doanh, có dự định mở rộng quy mô và  muốn phòng tránh rủi ro phát sinh.
  3. Doanh nghiệp hiện đang phải làm việc với bộ máy quản lý cồng kềnh, hiệu quả kém; muốn thực hiện tái cấu trúc tổng thể.

 

Doanh nghiệp phải có sẵn nguồn kinh phí lớn, nhằm phục vụ triển khai phần mềm và vẫn duy trì trả lương cho công nhân viên đều đặn. Một tiêu chí nữa doanh nghiệp cần đáp ứng là sự đồng lòng ủng hộ của nhân viên và / hoặc một chiến lược truyền thông nội bộ hiệu quả.

 

6. Một vài doanh nghiệp cung cấp phần mềm ERP ở Việt Nam

 

Nhiều doanh nghiệp Việt Nam hiện nay đã đủ khả năng tự thiết kế những phần mềm ERP “thuần Việt” như BRAVO, ERPViet, Amis.vn, MyERP,…

 

Một trong những thế mạnh hiển nhiên của các phần mềm ERP trong nước là có quy trình xử lý tài chính – kế toán tuân thủ theo chuẩn mực kế toán Việt Nam hiện hành, cũng như cập nhật kịp thời và nhanh chóng những thay đổi liên tục của các thông tư, quyết định, hướng dẫn,… trong nước.

 

7. Về các phần mềm chuyên biệt – giải pháp thay thế của ERP

 

Khi giải pháp ERP trở nên lỗi thời trước những bước tiến vượt bậc của công nghệ, những phần mềm chuyên biệt cho từng lĩnh vực trong doanh nghiệp đã nhanh chóng thay thế và chiếm lĩnh thị trường. Chúng tiết kiệm chi phí, nhỏ gọn, tân tiến và linh hoạt hơn  ERP rất nhiều khi áp dụng vào thực tế vận hành doanh nghiệp.

 

Rất nhiều doanh nghiệp đã lựa chọn giải pháp thay thế này. Thay vì tiêu tốn nguồn chi phí khổng lồ cho một hệ thống cồng kềnh vừa thừa vừa thiếu, doanh nghiệp chỉ lựa chọn sử dụng những phần mềm chuyên biệt mà họ thực sự cần.  Những phần mềm này được thiết kế ra để phục vụ một và chỉ một nghiệp vụ nhất định, nên chúng sở hữu nhiều tính năng chuyên sâu và hỗ trợ hiệu quả. Đặc biệt, mua một bộ các phần mềm chuyên biệt vẫn rẻ hơn nhiều so với một hệ thống ERP.

 

Nếu như ERP đòi hỏi phải tốn nhiều thời gian để set up và chạy thử, thì các phần mềm chuyên biệt lại chỉ mất từ 1-2 ngày là doanh nghiệp đã sở hữu bộ dữ liệu hoàn chỉnh và nhân viên đã có thể sử dụng thành thạo rồi. Song song với việc tiến hành set up một phần mềm, các phòng ban / bộ phận khác vẫn hoạt động hiệu quả bình thường. Theo đó, doanh nghiệp có thể lần lượt triển khai từng phần mềm để không làm gián đoạn tiến độ vận hành chung của doanh nghiệp.

Nguồn: https://base.vn

Bắt đầu với Vue JS. Ứng dụng CRUD dùng Vuex, Vutify, .NET Core

Cuối tuần rảnh rỗi ngồi tìm hiểu thêm về VueJs. (Latest stable version: 2.6.11) Framework Frontend nổi tiếng bên cạnh ReactJs và AngularJs.

Theo như định nghĩa trên trang chủ thì Vue là một framework dùng để xây dựng giao diện người dùng (user interfaces). Cụ thể, Vue là một progressive framework (framework linh động), cho phép và khuyến khích phát triển ứng dụng theo từng bước. Phần lõi của VueJS chủ yếu là vào phần View, ngoài ra có thể cài thêm các thành phần, thư viện hỗ trợ để đáp ứng nhu cầu xây dựng những ứng dụng SPA (Single-Page-Applications) . Khác với các monolithic framework (framework nguyên khối) cung cấp tất cả mọi thứ cần có để xây dựng app trong một framework duy nhất.

Để bắt đầu không gì nhanh nhất bằng cách vào thẳng trang chủ của nó tại https://vuejs.org, đọc và thực hành theo.

Do tìm hiểu về SPA nên không quan tâm mấy cách dùng VueJs kiểu nhúng file js nữa, mà bay thẳng vào trang CLI của nó cho nhanh. Link: https://cli.vuejs.org/guide/installation.html

Có vài câu lệnh CLI cần quan tâm đó là

Cài đặt
npm install -g @vue/cli
Khởi tạo dự án
vue create hello-world
Khởi động server Vue
vue-cli-service serve
Build & mang đi khoe
vue-cli-service build

Bắt tay vào làm thôi

Link mã nguồn: https://github.com/tuanitpro/vue-crud

Deploy website lên VPS với Gitlab CI/CD

Sau một thời gian code ra được môt trang web xịn xò thì bạn có nhu cầu đưa lên internet để mọi người được biết. Bắt đầu với công việc đầu tiên là tìm mua tên miền, hosting hoặc VPS. Bạn có thể mua VPS lại Vultr. 1 VPS có thể cài đặt được nhiều trang web.

Như vậy sau quá trình chuẩn bị thành công, chúng ta sẽ đẩy code từ local lên hosting, server. Quá trình này khá là mất công. Cách cổ điển hiện nay được nhiều người dùng là dùng FTP, hoặc upload thằng từ các trang quản lý. Đây là công việc khá nhàm chán, tốn thời gian. Khi có một thay đổi nhỏ là phải đi thao tác lại.

Chính vì vậy khái niệm CI/CD ra đời. CI/CD là một bộ đôi công việc, bao gồm CI (Continuous Integration) và CD (Continuous Delivery), ý nói là quá trình tích hợp (integration) thường xuyên, nhanh chóng hơn khi code cũng như thường xuyên cập nhật phiên bản mới (delivery).

Có khá nhiều công cụ hỗ trợ CI/CD, nhưng trong phạm vi bài viết chỉ đề cập công cụ CI/CD của Gitlab, và tất nhiên sử dụng tính năng free. Nếu bạn dùng nhiều hơn, phải trả thêm phí. Khi đó công việc sau khi code xong chỉ là nhấn nút push lên gitlab, ngồi uống cafe và đợi thành qủa.

Để tiết kiệm thời gian cài đặt và mọi thứ suôn sẻ, bạn cần có một chút kiến thức về VPS, SSH, public key, private key…

Một số thứ cần chuẩn bị trước khi bắt tay vào làm.

  • Domain: https://noithatrongviet.com
  • VPS tại vultr.com. Đã cài đặt nginx, thêm domain vào VPS. Xem hướng dẫn
  • Source code: ReactJs
  • Tạo SSH key trong VPS
  • Thêm SSH key vào Gitlab, CI/CD của Project
  • Cài đặt Gitlab Runner
  • Thêm file .gitlab-ci.yml tại folder root trên Gitlab

Chuẩn bị project ReactJs trên Gitlab

Tạo public key & private key cho CI/CD

Login vào SSH của VPS gõ lệnh

ssh-keygen -t rsa -C "[email protected]" -P "" -q -f ~/.ssh/gitlab

Hệ thống tự sinh ra 2 file là gitlab, và gitlab.pub

Trong cửa sổ lệnh terminal gõ:

cat gitlab.pub

Copy đoạn code public key, dán vào mục SSH của tài khoản Gitlab.

Tiếp theo gõ lệnh cat gitlab để lấy private key

cat gitlab

Private key này sẽ gán vào mục Settings CI/CD của repository. Trong mục Variables tạo 1 key mới là SSH_PRIVATE_KEY, dán private key của bạn vào đây

Quay lại cửa sổ Terminal. Gõ nano authorized_keys. Copy public key của bạn vào đây và lưu lại. Chú ý phân quyền đọc file

nano authorized_keys

Tiếp theo gõ nano config

nano config

Nhập code bên dưới và lưu lại

Host gitlab.com
  UseKeychain yes
  AddKeysToAgent yes
  PreferredAuthentications publickey
  IdentityFile ~/.ssh/gitlab

Để kiểm tra private key hoạt động chưa, gõ

ssh -i ~/.ssh/gitlab -o StrictHostKeyChecking=no username@ip-host -p PORT

Mục đích là để có thể login SSH và đẩy file lên mà không cần đăng nhập. Để push file sau khi deploy xong vào VPS, chúng ta sử dụng công cụ rsync. Đọc thêm bài viết hướng dẫn tại: Rsync – Công cụ đồng bộ dữ liệu hiệu quả – Cảm ơn hocvps.com đã viết bài này

Cài đặt Gitlab Runner

Gitlab Runner là thành phần cực kỳ quan trọng trong workflow Gitlab CI. Nếu không có Runner thì sẽ không có lệnh test, deploy nào được thực thi. Runner có nhiều loại, phân biệt dựa vào cái gọi là executor. Khi khởi tạo runner, bạn sẽ phải chọn nó là loại executor nào, và nó sẽ quyết định môi trường thực thi các câu lệnh trong file config ở trên. Bạn có thể tham khảo link https://docs.gitlab.com/runner/executors/ để biết sự khác nhau của các executor cũng như cách cài đặt, cấu hình chúng.

Trong repository setting, mục CI/CD, trên VPS mình chọn cài manual, excutor chọn Shell. Hoặc dùng luôn Share Runner mà không cần cài đặt cũng được.

Đến đây tạm xong phần cài đặt cần thiết. Chúng ta sẽ quay lại repo trên Gitlab.

Thêm file .gitlab-ci.yml tại folder root trên Gitlab

Mặc định Gitlab không có cơ chế nào về CI cho dự án của bạn, chỉ khi nào dự án của bạn có file .gitlab-ci.yml nằm ở thư mục gốc thì Gitlab mới nhận dạng được dự án của bạn muốn áp dụng Gitlab CI. File này có định dạng và cần hợp lệ thì mới có thể hoạt động được, không thì khi bạn push code lên thì Gitlab sẽ báo lỗi file định dạng nội dung của file cấu hình không hợp lệ. Tham khảo cú pháp của cấu hình này tại https://docs.gitlab.com/ce/ci/yaml/

Chú ý phải đúng format. Bạn có thể sửa trực tiếp trên Gitlab, nó sẽ kiểm tra giúp file có đúng format hay không. Đoạn code dưới đây làm 2 việc. Đầu tiên build code từ branh master ra bản release. Câu lệnh npm run build –prod khá quen thuộc. Sau đó deploy lên production thông qua Rsync kết hợp SSH.

# Using the node image to build the React app

image: node:latest
variables:
  PUBLIC_URL: /
# Cache node modules - speeds up future builds
cache:
  paths:
  - node_modules
stages:
- build
- deploy
build:
  stage: build
  script:
    - npm install # Install all dependencies
    - npm run build --prod # Build for prod
  artifacts:
    paths:
    - build 
  only:
    - master # Only run on master branch
deploy_production:
  stage: deploy
  image: ubuntu
  before_script:
  - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
  - eval $(ssh-agent -s)
  - mkdir -p ~/.ssh
  - echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
  - chmod 700 ~/.ssh/id_rsa
  - eval "$(ssh-agent -s)"
  - ssh-add ~/.ssh/id_rsa
  - ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts
  - apt-get install rsync -y -qq
  - apt-get install curl -y -qq

  script:
    - echo "Deploying to server"
    - ssh -i ~/.ssh/gitlab -o StrictHostKeyChecking=no USERNAME@IP-HOST -p PORT
    - rsync -avz --progress -a -e "ssh -p PORT" build/ USERNAME@IP-HOST:/home/www/noithatrongviet.com/public_html
    - echo "Deployed"
  environment:
    name: production
    url: http:noithatrongviet.com
  only:
    - master # Only run on master branch

Chú các thông tin quan trọng: USERNAME@IP-HOST PORT là thông tin tên đăng nhập, port SSH của bạn. /home/www/noithatrongviet.com/public_html thay = đường dẫn trên host của bạn. Xong nhấn nút Commit và vào mục CI/CD -> Pipelines hoặc CI/CD -> Jobs kiểm tra kết quả của bạn.Tận hưởng thành quảChúc các bạn thành công. Từ nay cứ commit code là Gitlab tự động build và deploy lên host cho bạn, quá đã phải không.

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.

Tạo menu lựa chọn trong C/C++

Tạo một Menu có các lựa chọn là 1 bài tập lập trình căn bản, giúp các bạn làm quen với các lệnh xóa màn hình, vòng lặp while, lệnh switch case, lệnh getch. Đây có thể nói là 1 bài tập tổng hợp. Các bạn theo dõi video bên dưới để dễ hình dung hơn.

Tạo menu lựa chọn trong C/C++

Tóm tắt Video

  • Tạo một dự án mới
  • Tạo các chức năng của dự án (bài tập)
  • Tạo các menu chức năng
  • Viết các hàm xử lý menu
  • Gọi hàm main để chạy thử chương trình

Code: Menu.h

#include "Lib.h"

void Menu();		// Ham nay se xuat ra cac danh sach menu
int ChonMenu();		// Ham nay dung de chon 1 menu tuong ung
void XuLyMenu();	// Xu ly menu ung voi menu duoc chon


void Menu() {
	cout << "=================MENU================\n";
	cout << "1. Vua ga - vua cho\n";
	cout << "2. Kiem tra so nguyen to\n";
	cout << "3. Tong cac so nguyen to\n";
	cout << "4. Dao nguoc so\n";
	cout << "5. Tim so sao cho a*b = 2(a+b)\n";
	cout << "6. Tinh giai thua cua mot so\n";
	cout << "7. Tim day Fibonaci\n";
	cout << "8. Hoan vi\n";
	cout << "9. Ve tam giac\n";
	cout << "10. Bai tap cua cac ban";
	cout << "99. Thoat!!!\n";
	cout << "=====================================\n";
}
int ChonMenu()
{
	int n = 0;
	cout << "\n\nMoi chon menu: ";
	cin >> n;
	if (n > 0 || n < 99)
		return n;
	else return ChonMenu();
}
void XuLyMenu() 
{
	int chon = ChonMenu();
	int a = 5; int b = 6;
	switch (chon)
	{
	case 1:		
		cout << "1. Vua ga vua cho.\n";
		VuaGaVuaCho();
		break;
	case 2:
		cout << "2. Kiem tra so nguyen to\n";
		cout << TimSoNT(5);
		break;
	case 3:
		cout << "3. Tong cac so nguyen to\n";
		cout << TinhTongCacSoNT(11);
		break;
	case 4:
		cout << "4. Dao nguoc so\n";
		cout << DaoNguocSo(123);
		break;
	case 5:
		cout << "5. Tim so sao cho a*b = 2(a+b)\n";
		TimSoTichABBang2TongAB(100);
		break;
	case 6:
		cout << "6. Tinh gia thua cua mot so\n";
		cout << TinhGiaiThua(6);
		break;
	case 7:
		cout << "7. Tim day Fibonaci\n";
		for (int i = 0; i < 5; i++)
		{
			cout << Finbonaci(i) << "\t";
		}
		Finbonaci2(7);
		break;
	case 8:
		cout << "7. Hoan vi\n";
		//int a = 5; int b = 6;
		HoanVi(a, b);
		cout << a << "\t" << b;
		break;
	case 9:
		VeHinhTamGiac(4, 5);
		break;
	case 99:
		cout << "Thoat!!!\n";
		exit(1);
		break;
	}
}

Code Source.cpp

#include "Menu.h"
using namespace std;
void main() 
{	
	Menu();
	while (true)
	{	
		XuLyMenu();
	}
	_getch();
}

Hãy sáng tạo theo cách của bạn.

Nếu bạn đã hoàn thành theo hướng dẫn mà vẫn gặp khó khăn, hãy liên hệ với tôi.

Cảm ơn bạn đọc và tôi luôn đánh giá cao phản hồi của bạn.

Hướng dẫn thay đổi background Visual Studio

Khi bạn là một tín đồ của .NET, thường xuyên làm việc với Visual Studio IDE, hẳn bạn sẽ có lúc cảm thấy nhàm chán giao diện của nó. Chỉ có vài Themes đơn giản, nhưng cũng không được nổi bật lắm.

Cũng như Desktop của bạn, lâu lâu chúng ta muốn thay đổi background của nó một chút cho bắt mắt, hay đỡ nhàm chán, đồng thời cũng thể hiện cá nhân hóa của bạn trên màn hình làm việc.

Thật thú vị là chúng ta có một Extension của Visual Studio đó là ClaudiaIDE. Nó cho phép chúng ta thay đổi background làm việc của Visual Studio bằng cách thay đổi hình nền giao diện làm việc. Tải ClaudiaIDE tại đây.
ClaudiaIDE là một mã nguồn mở được chia sẻ tại Github. Nếu bạn có hứng thú có thể tham gia phát triển nó tại: https://github.com/buchizo/ClaudiaIDE

ClaudiaIDE khá thú vị khi cho phép bạn chọn 1 ảnh nền, hay cả 1 thư mục chứa ảnh để thay đổi theo kiểu Slide show. Giống y chang desktop của bạn.

ClaudiaIDE-1
Sau khi cài đặt chúng ta vào mục Tools->Options->ClaudiaIDE để cài đặt lại theo ý thích cá nhân.

ClaudiaIDE-2

Một số thông số cài đặt
###Image
Image background type
– Single: Chỉ chọn 1 ảnh làm nền
– Slideshow: Chọn nhiều ảnh làm nền
– Opacity: Điều chỉnh độ mờ của ảnh nền
###Layout
– Horizontal Alignment: Vị trí của ảnh (Left, Right, Center)
– Vertical Alignment: Vị trí của ảnh (Top, Bottom, Center)
###SingleImage
– File Path: Đường dẫn hình ảnh của bạn
###Slideshow
– Directory Path: Đường dẫn thư mục chứa ảnh
– Image animation interval: Thời gian hiệu ứng chuyển ảnh
– Image extensions: .png, .jpg định dạng ảnh cho phép
– Update interval: Thời gian thay đổi giữa 2 ảnh

Chúc các bạn thành công, và đừng quên chia sẻ giao diện làm việc mới trên Visual Studio của bạn nhé. ?

Multiple file upload in ASP.NET MVC

Khi lập trình web với ASP.NET MVC hay bất kỳ ngôn ngữ nào khác như PHP chẳng hạn, chúng ta đều gặp những tình huống liên quan đến Upload file lên hosting, dễ thấy nhất là upload hình sản phẩm cho một sản phẩm nào đó.

Với ASP.NET MVC chúng ta dễ dàng làm được điều đó, bạn có thể upload 1 file hoặc upload nhiều file (Multiple file upload) trong ASP.NET MVC cùng lúc. Nó cực kỳ hữu ích trong nhiều trường hợp. Đoạn code nhỏ sau đây sẽ minh họa cho vấn đề này.

Multiple file upload in ASP.NET MVC
Multiple file upload in ASP.NET MVC

Code HTML

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Upload file trong ASP.NET MVC</title>
    <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var max_fields = 10; //maximum input boxes allowed
            var wrapper = $(".myinput"); //Fields wrapper
            var add_button = $(".btnAddNew"); //Add button ID

            var x = 0; //initlal text box count
            $(add_button).click(function (e) { //on add input button click
                e.preventDefault();
                if (x < max_fields) { //max input box allowed
                    x++; //text box increment
                    $(wrapper).append('<div>   <input type="file" name="uploadFile['+x+']" /><a href="#" class="btnRemove">Xóa</a></div>'); //add input box
                }
            });

            $(wrapper).on("click", ".btnRemove", function (e) { //user click on remove text
                e.preventDefault(); $(this).parent('div').remove(); x--;
            })
        });
    </script>
</head>
<body>
    <div>
        <h2>Upload file trong ASP.NET MVC</h2>
        <div style="color:red">
            @Html.Raw(TempData["Msg"])
        </div>
        <fieldset>
            <legend>Upload file</legend>
            @using (Html.BeginForm("Upload", "Upload", FormMethod.Post, new { enctype = "multipart/form-data" }))
            {

                <label>Chọn file: </label>
                <br />
                <input type="file" name="uploadFile" required /><br />

                <input type="submit" value="Upload" />

            }
        </fieldset>
        <hr />
        <h3>Upload nhiều file trong ASP.NET MVC</h3>
        <fieldset>
            <legend>Upload multi file</legend>
            @using (Html.BeginForm("UploadMulti", "Upload", FormMethod.Post, new { enctype = "multipart/form-data" }))
            {

                <label>Chọn file: </label><br />
                <a class="btnAddNew" href="#">Thêm</a>
                <br />
                <div id="myinput" class="myinput">
                    <input type="file" name="uploadFile[0]" required /><br />
                </div>
                <br />
                <input type="submit" value="Upload" />

            }
        </fieldset>

Code cs

/**   FileName: UploadController.cs 
          Project Name: DateTime Ajax
          Date Created: 12/17/2014 11:30:58 PM 
          Description:  File Upload trong ASP.NET MVC
          Version: 0.0.0.0 
          Author: Lê Thanh Tuấn - Khoa CNTT
          Author Email: [email protected] 
          Author Mobile: 0976060432
          Author URI: https://tuanitpro.com 
          License: 
     */

    public class UploadController : Controller
    {
        // GET: Upload
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Upload(HttpPostedFileBase uploadFile)
        {
            if (ModelState.IsValid)
            {
                string filePath = Path.Combine(HttpContext.Server.MapPath("/Uploads/demo"),
                                               Path.GetFileName(uploadFile.FileName));
                uploadFile.SaveAs(filePath);
                TempData["Msg"] = string.Format("Upload file {0} thành công", uploadFile.FileName);
            }
            return RedirectToAction("Index");
        }

        [HttpPost]
        public ActionResult UploadMulti(List<HttpPostedFileBase> uploadFile)
        {
            string abc = "";
            string def = "";
            foreach (var item in uploadFile)
            {

                string filePath = Path.Combine(HttpContext.Server.MapPath("/Uploads/demo"),
                                               Path.GetFileName(item.FileName));
                item.SaveAs(filePath);

                abc = string.Format("Upload {0} file thành công", uploadFile.Count);

                def += item.FileName + "; ";


            }
            TempData["Msg"] = abc + "</br>" + def;
            return RedirectToAction("Index");
        }
    }

Live demo

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