Category Archive C#

Byphunsanit

C#: Error CS0579 Duplicate ‘System.Reflection.AssemblyCompanyAttribute’

เขียน c# อยู่ๆเจอ error Error CS0579 จริงๆ ไม่ใช่อยู่ๆ หรอก ทุกอย่างมันมีเหตุปัจจัยมันถึงแจ้ง error มาให้ดู โดยที่เจอคือ

  • Error CS0579 Duplicate ‘System.Reflection.AssemblyConfigurationAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyConfigurationAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyFileVersionAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyProductAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyTitleAttribute’
  • Error CS0579 Duplicate ‘System.Reflection.AssemblyVersionAttribute’

ดูโดยรวมแล้วมันคือไฟล์ที่บันทึกเวอร์ชั่นของโปรเจคที่เราได้เขียนเอาไว้ สาเหตุก็น่าจะง่ายๆ ไปแก้ไขมันแล้วพังนั่นเอง แต่ไม่น่าพังนะ ก็ไปแก้ตามวิธีปกติเองนี่น่า ไปไล่ๆ ดูจนพบวิธีแก้ คือ

  1. ไปที่โฟลเดอร์ของ project ที่มีปัญหา
  2. ค้นหาไฟล์ *.Assemblyinfo.cs
  3. จะเห็นว่ามันมีไฟล์ตัวนี้อยู่ 5 ตัวเลย ลบทิ้งทั้งหมด
  4. ลอง clearn แล้ว run project ดูใหม่ ถ้ายัง error ไปบรรทัดที่มันเตือนอยู่แล้วลบทิ้งไปเลย

Cr. Duplicate AssemblyVersion Attribute

Byphunsanit

c#: Multiple Document Interface (MDI)

ตัวอย่างเขียน app แบบมีหลายฟอร์มย่อยอยู่ใน window หลัก หรือที่ฝรั่งเรียกไว้ว่า Multiple Document Interface (MDI)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PrototypesCSharp
{
    public partial class FormMDI : Form
    {
        public FormMDI()
        {
            InitializeComponent();
        }

        public Form TryGetFormByName(string frmName)
        {
            if (panelMDI.Controls.Count > 0)
            {
                panelMDI.Controls.RemoveAt(0);
            }

            var formType = Assembly.GetExecutingAssembly().GetTypes()
                .Where(a => a.BaseType == typeof(Form) && a.Name == frmName)
                .FirstOrDefault();

            if (formType == null)
            {//if no form
                return null;
            }

            Form frm = (Form)Activator.CreateInstance(formType);

            //https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.form.acceptbutton?view=net-5.0

            frm.AutoSize = true;
            frm.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            frm.Height = panelMDI.Height;
            frm.TopLevel = false;
            frm.TopMost = true;
            frm.WindowState = FormWindowState.Maximized;
            frm.Width = panelMDI.Width;

            frm.Show();

            panelMDI.Controls.Add(frm);

            return frm;
        }

        //on menu click
        private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            try
            {
                String formName = e.ClickedItem.Name.Split(new[] { "toolStripMenuItem" }, StringSplitOptions.None)[1];

                Form frm = TryGetFormByName(formName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        //resize form inside when main windows resize
        private void panelMDI_Resize(object sender, EventArgs e)
        {
            foreach (Form frm in panel1.Controls)
            {
                frm.WindowState = FormWindowState.Normal;

                frm.WindowState = FormWindowState.Maximized;
            }
        }

    }
}

code ชุดนี้ทำงานโดยเมื่อ user click บน menuStrip (ไม่ใช้ตัว item นะ) จะอ่าน item name แล้วตัด toolStripMenuItem ออกเพื่อเรียกฟอร์ม ต้องขอขอบคุณ nemesv สำหรับคำตอบในเรื่อง Winforms, create form instance by form name ทำให้เขียน code สะอาดขึ้นเยอะเลย ไม่ต้องมา set ให้ item ทีละตัวแล้ว

Byphunsanit

c#: build .exe with version

ต้องเก็บไฟล์ที่ build ไฟล์เป็น version ต่างๆ ให้ user เอาไว้เทส ถ้านานๆ ทำทียังพอจะเปลี่ยนชื่อให้ได้ แต่ถ้าทำบ่อยๆ คงจะน่าเบื่อ ให้คอมทำให้ซิ

เปิดไฟล์ *.csproj ใน folder ของ project ออกมาแก้ ปกติก็ใส่ไว้ท้าย ๆ ไฟล์

  <Target Name="GetAssmeblyVersion" AfterTargets="Build">
    <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
      <Output TaskParameter="Assemblies" ItemName="MyAssemblyIdentities" />
    </GetAssemblyIdentity>
    <Message Text="Assmebly Version: %(MyAssemblyIdentities.Version)" />
  </Target>
  <Target Name="PostBuild" AfterTargets="GetAssmeblyVersion">
    <Exec Command="copy /Y "$(ProjectDir)$(OutDir)\$(TargetName).exe" "$(ProjectDir)$(OutDir)\$(TargetName) v.%(MyAssemblyIdentities.Version)_$(ConfigurationName).exe"

" />
  </Target>

</Project>

หลังจาก build code ชุดนี้จะทำตามคำสังใน PostBuild ในที่นี้คือใช้ command copy .exe ตามปกติแล้วเพิ่ม version เข้าไปให้

ขอขอบคุณ

Byphunsanit

c#: แบ่งงานช่วยกันรุมให้เสร็จโดย Tasks

cpu รุ่นใหม่ๆ สามารถทำงานได้พร้อมๆ กันหลายๆ งานในเวลาเดียวกัน ทำไมจะไม่เขียนโปรแกรมให้ทำงานพร้อมๆ กันจะได้เสร็จไว้ๆ ละ

ตัวอย่าง c# Console App

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {

        private static void Main(string[] args)
        {
            Dictionary<string,
            string> months = new Dictionary<string,
            string>() {
        {"01","January"},
        {"02","February"},
        {"03","March"},
        {"04","April"},
        {"05","May"},
        {"06","June"},
        {"07","July"},
        {"08","August"},
        {"09","September"},
        {"10","October"},
        {"11","November"},
        {"12","December"},
            };
            var reultsts = new List<List<string>>();
            Console.WriteLine("Start Main");
            reultsts = RunAsync(months).Result;
            Console.WriteLine("End Main get reultsts " + reultsts.Count() + " items");

            Console.ReadKey();
        }

        public static async Task<List<List<string>>> RunAsync(Dictionary<string, string> months)
        {
            var tasks = new List<Task>();
            var reultsts = new List<List<string>>();
            Console.WriteLine("Start RunAsync");
            foreach (KeyValuePair<string, string> item in months)
            {
                tasks.Add(Task.Run(() =>
                {
                    reultsts.Add(TaskRun(item.Key, item.Value).Result);
                }));
            }
            Task t = Task.WhenAll(tasks);
            try
            {
                t.Wait();
            }
            catch { }
            Console.WriteLine("End RunAsync");
            return reultsts;
        }

        public static async Task<List<string>> TaskRun(string key, string value)
        {
            Random random = new Random();
            string message;
            int sleep;
            var results = new List<string>();
            for (int i = 0; i < 2; i++)
            {
                sleep = random.Next(1, 10) * 1000;
                System.Threading.Thread.Sleep(sleep);
                message = "TaskRun month " + key + " " + i + " " + sleep;
                results.Add(message);
                Console.WriteLine("\t" + message);
            }
            return results;
        }
    }
}

จะเห็นว่าไม่ได้ทำงานตามลำดับเดือน แถมบางเดือนจะทำครั้งที่ 1 และครั้งที่ 2 ห่างกันมากด้วยซ้ำไปเพราะว่าการจำลองการทำงานด้วย sleep ที่สุ่มไว้ได้เวลาต่างกันมาก อันไหนทำเสร็จแล้วมันก็จะไปหยิบงานอื่นมาทำก่อนโดยไม่ต้องรอให้งานเสร็จตามลำดับ

Byphunsanit

C#: Configurations

C# จะเก็บ configurations อย่าง connection strings ไว้ในไฟล์ app.config หรือ web.config เวลา compiled โปรแกรมไปไว้ที่เครื่อง user แล้วเหมือนมันจะเปิดเผยไปหน่อย แถมเวลาเปลี่ยน environment จาก dev ไป uat อะไรแบบนี้ทีก็มาแก้กันที ถ้าแค่ database ตัวเดียวยังไม่เท่าไหร่ แต่ถ้าต้องติดต่อหลายก้อน แล้วต้องสลับไปมา ตอนที่แก้บัคพร้อม uat นี่พลาดได้ง่ายๆ
วิธีที่นิยมกันก็คือสร้าง class ขึ้นมาใหม่ แล้วใช้ config เอาจาก class แทน เช่น ไฟล์ Configurations.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrototypesCSharp
{
    class Configurations
    {
        public static string environment = "DEV"; // DEV, UAT, PRODUCTION

        public static Dictionary<string, string> DSNs()
        {

            var dsns = new Dictionary<string, string>();

            switch (Configurations.environment)
            {

                case "DEV":
                    {
                        dsns["dns1"] = "Server=127.0.0.1;Database=prototype_DEV;User Id=pitt;Password=phunsanit";
                        dsns["dns1_replicate"] = "Server=127.0.0.1;Database=prototype_DEV;User Id=pitt;Password=phunsanit";
                        dsns["dns2"] = "Server=127.0.0.1;Database=prototype_DEV;User Id=pitt;Password=phunsanit";
                        dsns["dns2_replicate"] = "Server=127.0.0.1;Database=prototype_DEV;User Id=pitt;Password=phunsanit";
                    }
                    break;

                case "PRODUCTION":
                    {
                        dsns["dns1"] = "Server=127.0.0.1;Database=prototype;User Id=pitt;Password=phunsanit;";
                        dsns["dns1_replicate"] = "Server=127.0.0.1;Database=prototype;User Id=pitt;Password=phunsanit;";
                        dsns["dns2"] = "Server=127.0.0.1;Database=prototype;User Id=pitt;Password=phunsanit;";
                        dsns["dns2_replicate"] = "Server=127.0.0.1;Database=prototype;User Id=pitt;Password=phunsanit;";
                    }
                    break;

                case "UAT":
                    {
                        dsns["dns1"] = "Server=127.0.0.1;Database=prototype_UAT;User Id=pitt;Password=phunsanit";
                        dsns["dns1_replicate"] = "Server=127.0.0.1;Database=prototype_UAT;User Id=pitt;Password=phunsanit";
                        dsns["dns2"] = "Server=127.0.0.1;Database=prototype_UAT;User Id=pitt;Password=phunsanit";
                        dsns["dns2_replicate"] = "Server=127.0.0.1;Database=prototype_UAT;User Id=pitt;Password=phunsanit";
                    }
                    break;

            }

            return dsns;
        }

    }
}

เวลาจะใช้ในฟอร์มอื่นก็แค่เรียกใช้อย่าง

        string dns1 = Configurations.DSNs()["dns1"].ToString();
        SqlConnection objConn = new SqlConnection(dns1);

แบบนี้ถ้าอยากจะเปลี่ยน environment จาก DEV, UAT หรือ PRODUCTION ก็เปลี่ยนแค่ public static string environment = “DEV”; // DEV, UAT, PRODUCTION บรรทัดเดียว ก็ได้แล้ว ถ้ามี database อื่นหรือคอนฟิกอื่นๆ ก็แค่เพิ่มรายการเข้าไป ตามที่ใช้

เพื่อให้ง่ายในการดูว่าโปรแกรมเรากำลังใช้ environment ตัวไหนอยู่อาจจะใช้วิธีอย่าง C#: แสดง program version