2017. 10. 27. 15:53

[준비하기]

 - VS 2017 템플릿

https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio


- 다운로드 

  https://github.com/nunit/nunit/releases/3.8


- NUnit-Gui

테스트를 진행을 위한 GUI툴

 - https://github.com/NUnitSoftware/nunit-gui/releases


[프로젝트]

1. nuget에서 nunit 설치

    - NUnit3 

    - NUnit3TestAdapter


2. 프로젝트 생성

 Test메뉴에서 NUnit Test Project 선택


3. Attribute (https://github.com/nunit/docs/wiki/Attributes)

 -  [TestFixture]: 클래스에 지정하여 해당 클래스는 테스트를 위한 클래스라는 것을 지정하기 위해 사용

 - [SetUp]: 클래스 멤버나 기타 초기화가 필요한 경우 사용

 - [Test]: 메소드에 선언되며 단위별 테스트를 위한 사용

 - [ExpectedException(typeof(exception class))]: 예상되는 exception을 테스트할 경우 사용

 - [Ignore("메시지")]: 


3. Assert

 - 단위테스트의 주요 기능 결과를 확인 하기 위해 사용되는 클래스

 - 주요기능

  - AreEqual, AreNotEqual, AssertDoublesAreEqual, AreSame, AreNotSame

  - ByVal

  - True, False

  - IsTrue, IsFalse, IsEmpty, IsNull, IsNaN

  - Greater, GreaterOrEqual, , Less, LessOrEqual, Zero

  - Pass, Fail

  - Positive, Negative

  - Null, NotNull, Zero, NotZero

  - That

  - Throws


3. 테스트 실행

 - 메뉴 > 테스트 > 실행


[NUNIT Document]

https://github.com/nunit/docs/wiki/NUnit-Documentation

Posted by CoolDragon
2017. 7. 14. 16:02

- 지정된 폴더의 파일 변화 모니터링 (생성,삭제, 리네임, 변경)

- 파일이 현재 복사가 되는지 File Lock 여부


using System;

using System.Collections.Generic;

using System.IO;

using System.Linq;

using System.Runtime.InteropServices;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;


namespace FileWatcher

{

    /// <summary>

    /// MainWindow.xaml에 대한 상호 작용 논리

    /// </summary>

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();


            Start();

        }


        System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();

        string filePath = null;


        private void Start()

        {

            System.IO.FileSystemWatcher watcher = new System.IO.FileSystemWatcher();

            watcher.Path = @"d:\watch";

            watcher.NotifyFilter = System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName | System.IO.NotifyFilters.Size;


            watcher.Changed += Watcher_Changed;

            watcher.Created += Watcher_Created;

            watcher.Deleted += Watcher_Deleted;

            watcher.Renamed += Watcher_Renamed;

            watcher.EnableRaisingEvents = true;


            timer.Interval = new TimeSpan(0, 0, 1);

            timer.Tick += (s, args) =>

            {

                if (!string.IsNullOrEmpty(filePath))

                {

                    if (IsFileLocked(filePath))

                        Log($"File Locked");

                    else

                    {

                        Log($"File unlocked");

                        timer.Stop();

                    }

                }

            };

        }


        private void Watcher_Renamed(object sender, System.IO.RenamedEventArgs e)

        {

            Log($"{e.ChangeType}, File: {e.FullPath}, Old File: {e.OldFullPath}");

        }


        private void Watcher_Deleted(object sender, System.IO.FileSystemEventArgs e)

        {

            Log($"{e.ChangeType}, File: {e.FullPath}");

        }


        private void Watcher_Created(object sender, System.IO.FileSystemEventArgs e)

        {

            Log($"{e.ChangeType}, File: {e.FullPath}");

            timer.Start();

        }


        private void Watcher_Changed(object sender, System.IO.FileSystemEventArgs e)

        {

            filePath = e.FullPath;

            Log($"{e.ChangeType}, File: {filePath}, Length: {new System.IO.FileInfo(filePath).Length}");

        }


        private void Timer_Tick(object sender, EventArgs e)

        {

            throw new NotImplementedException();

        }


        private void Log(string str)

        {

            this.Dispatcher.Invoke(() =>

            {

                txt.Text += str + "\r\n";

            });

        }


        const int ERROR_SHARING_VIOLATION = 32;

        const int ERROR_LOCK_VIOLATION = 33;

        private bool IsFileLocked(string file)

        {

            //check that problem is not in destination file

            if (File.Exists(file) == true)

            {

                FileStream stream = null;

                try

                {

                    stream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.None);

                }

                catch (Exception ex2)

                {

                    //_log.WriteLog(ex2, "Error in checking whether file is locked " + file);

                    int errorCode = Marshal.GetHRForException(ex2) & ((1 << 16) - 1);

                    if ((ex2 is IOException) && (errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION))

                    {

                        return true;

                    }

                }

                finally

                {

                    if (stream != null)

                        stream.Close();

                }

            }

            return false;

        }

    }

}



Posted by CoolDragon
2016. 5. 26. 14:17

c#에서의 정규식

string tmp = System.Text.RegularExpressions.Regex.Replace("abce!@#%123123가나다라", "[^0-9a-zA-Zㄱ-힗]+", "");

Posted by CoolDragon
2016. 5. 25. 12:20

아래 코드를 입력하면 클래스 오브젝트를 XML 또는 JSON 타입으로 리턴시켜준다.

[Global.aspx]

public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        // 응용 프로그램 시작 시 실행되는 코드
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        RouteConfig.RegisterRoutes(RouteTable.Routes);

        // Content-type 전역지정
        GlobalConfiguration.Configuration.Formatters.Clear();
        //GlobalConfiguration.Configuration.Formatters.Add(new System.Net.Http.Formatting.XmlMediaTypeFormatter());
        GlobalConfiguration.Configuration.Formatters.Add(new System.Net.Http.Formatting.JsonMediaTypeFormatter());
    }
}


Posted by CoolDragon
2016. 5. 12. 15:22

1. 다운로드

리눅스 및 맥, 윈도우즈등 다양한 플랫폼을 지원한다.

http://www.planetcassandra.org/cassandra/


2. 설치

윈도우환경에서 개발할 것이므로 윈도우 MSI installer를 다운로드받고 설치를 한다. 설치 과정은 특별히 선택할게 없었다.

설치 후 윈도우 메뉴에서 아래의 이미지처럼 설치 결과를 확인할 수 있다.

3. 실행 및 데이터베이스 생성

Cassandra CQL Shell을 실행하면 카산드라 콘솔이 생성되며 아래의 스크립트를 추가해본다. 

(본 블로거도 카산드라 초보이므로 카산드라 웹사이트에서 참고하여 대충 만들었다.)

CREATE KEYSPACE demo
  WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 3 };

CREATE TABLE demo.users (
  lastname text PRIMARY KEY,
  age int,
  city text,
  email text,
  firstname text
);

DROP TABLE demo.users;

4. C# 프로젝트

Nu-Get을 통하여 driver를 설치

Install-Package CassandraCSharpDriver
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cassandra;

namespace CassandraSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint("127.0.0.1").Build();
            ISession session = cluster.Connect("demo");

            session.Execute("insert into users (lastname, age, city, email, firstname) values ('Jones', 35, 'Austin', 'bob@example.com', 'Bob')");

            Row result = session.Execute("select * from users where lastname = 'Jones'").First();
            Console.WriteLine("{0} {1}", result["firstname"], result["age"]);
            Console.ReadLine();

            session.Execute("update users set age = 36 where lastname = 'Jones'");
            Row result2 = session.Execute("select * from users where lastname = 'Jones'").First();
            Console.WriteLine("{0} {1}", result2["firstname"], result2["age"]);
            Console.ReadLine();

            session.Execute("delete from users where lastname = 'Jones'");
            RowSet rows = session.Execute("select * from users");
            foreach (Row row in rows)
                Console.WriteLine("{0} {1}", result2["firstname"], result2["age"]);

            Console.ReadLine();
        }
    }
}

5. 결론

그냥 실행만 해보는 수준이다 보니 카산드라가 다른 NoSQL이나 MSSQL과 같은 DB에서 지원하는 기능을 얼마나 지원해주고 더 나은 부분을 확인은 할 수 없었다. 다만, 위에 보는 거와 같이 쉽게 연동이 가능해 보이며 ANSI SQL을 지원하기에 이미 MSSQL, ORACLE, MYSQL의 DB를 사용해본 사람이라면 쉽게 접근할 수 있지 않을까 싶다.


Reference: https://academy.datastax.com/resources/getting-started-apache-cassandra-and-c-net

Posted by CoolDragon
2015. 9. 18. 19:54

잡설

나는 Entity Framework는 그저 Link to SQL의 확장 정도로 인식해 왔다. 하지만 많은 유튜브 동영상 강좌를 보니 생각보다 다양한 하고 복잡한 프로그래밍 방식을 제공하는 것 같았다. 본인 프로그래밍 방식은 RDBMS에 프로시저 등을 생성하고 코드단에서 그 프로시저들을 호출하는 형태로 작업을 많이 해왔다. 그래서 최소한의 필요 데이터 만을 가져오는 것을 지향해서 테이블 단위의 Model을 생성하기 보다는 그때 그때 필요에 의해 모델들을 생성하다 보니 중복되는 code들이 늘어나는 것을 느꼈다. 특히 Relation에 엮여있는 테이블들의 데이터는 어떤 액션을 하느냐에 따라서 더 그런거 같더라.. 포스팅을 하는 지금 다시 생각해 보면 내가 한 코드단 모델링 방식은 잘 못 설계한 것이라는 생각이 든다. 잡설은 여기까지 하고 Entity Framework에 대한 사용법을 정리해보고자 한다.


Pre-Requisite

Entity Framework Power Tools 설치 (선택된 데이터베이스의 스키마를 자동으로 c#코드로 제너레이션 해준다.)

 - https://visualstudiogallery.msdn.microsoft.com/72a60b14-1581-4b9b-89f2-846072eff19d (VS2015 지원안함)

 - https://visualstudiogallery.msdn.microsoft.com/ee4fcff9-0c4c-4179-afd9-7a2fb90f5838


Development Method

 - Database first: 데이터베이스에 접속하여 스키마를 통하여 모델과 엔티티를 자동생성

 - Model First: 비쥬얼 스튜디오에서 모델을 직접 설계하고 관계도를 만들면 엔티티가 자동생성 (ERD와 비슷)

 - Code First: 자동 생성 없이 엔티티와 DBContext를 상속받은 클래스를 개발자가 직접 작성


Package Manager Console

# entity framework 설치 (Nuget을 사용할 수 있다.)

PM> install-package entityframework


# 데이터베이스의 스키마 정보가 c# 코드로 생성(자동 제너레이션)

PM> enable-migrations


# 데이터베이스의 스키마와 c#코드상에 스키마 정보를 비교하여 변경된 스키마 정보를 yyyyMMddHHmmss_FileName이라는 c#파일로 생성

PM> add-migration FileName


# 아래 명령어를 통하여 변경된 스키마를 데이터베이스에 적용

PM> Update-Database


# 데이터베이스에 적용된 스키마 롤백 (선택된 파일의 상태때로 되돌아 간다. 즉, 선택 파일 이후에 변경되었던 스키마 정보는 모두 롤백된다.)

PM> Update-Database -targetmigration 마이그레이션파일명(.cs제외)


# 최초의 스키마 정보에서부터 타켓으로 변경된 스키마 정보(yyyyMMddHHmmss_FileName)가 스크립트로 생성

PM> Update-Database -Script -SourceMigration: @InitialDatabase - TargetMigration: yyyyMMddHHmmss_FileName


DbContext

public class Employee

{

  public string Id {get; set;}

  public string Name {get; set;}

}


public class EmployeeDBContext : DbContext

{

  public DbSet<Employee> Employees {get; set;}


  protected override void OnModelCreating(DbModelBuilder modelBuilder)

  {

    // 여기에 Entity의 컬럼 속성 및 테이블/프로시져 맵핑 등을 설정할 수 있다.

    // 테이블명이 복수형으로 만들어지는 것을 방지

    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

    // Employee에 DB에 프로시져 자동 생성 및 맵핑

    modelBuilder.Entity<Employee>().MapToStoredProcedures

    (p => p.Insert(i => i.HasName("insertEmployee"))

               .Update(i => i.HasName("UpdateEmployee"))

               .Delete(i => i.HasName("DeleteEmployee")));

    base.OnModelCreating(modelBuilder)

  }

}


Pattern

 - Repositories Pattern

 - Unit of Work


DB Initializer

// DB 초기화 클래스 생성

public class DBInitializer : DropCreateDatabaseIfModelChanges<EmployeeDBContext>

{

  protected override void Seed(EmployeeDBContext context)

  {

    // 여기에 Context에 DataSet<Entity>로 선언된 프로퍼티의 기준 데이터를 생성해 줄 수 있다.

    base.Seed(context); 

  }

}

// Main() 함수나 Global.asax 의 실행부에 아래 함수를 호출하거나

System.Data.Entity.Database.SetInitializer(new DBInitializer());

// 또는 app.config나 web.config의 <entityframwork>에서 설정이 가능하다.

  <entityFramework>

    <contexts>

      <context type="Context Class Name, DLL Name">

        <databaseInitializer type="DBInitializer, DLL Name" />

      </context>

    </contexts>



Attribute

[SoftDelete("Property Name")]

모델 클래스에 SoftDelete()라는 속성을 선언하면 DB에 쿼리를 만드는 시점 Command Tree Interceptor 에서 그 쿼리를 가로채 실제 요청쿼리에 맞게 변환작업을 할 수 있게 한다.

이 기능이 구현하기 위해서는 Custom Attribute class 와 IDbCommandTreeInterceptor 인터페이스를 상속받은 class를 만들어 주어야 한다.


[DisplayFormat(DataFormatString = "{0:C0}")]



Tutorial

http://www.entityframeworktutorial.net/code-first/entity-framework-code-first.aspx


참조

https://www.youtube.com/playlist?list=PL6n9fhu94yhUPBSX-E2aJCnCR3-_6zBZx

https://www.youtube.com/watch?v=iwEG1M2ONrw

https://www.youtube.com/watch?v=vspEYmp6zWc

https://www.youtube.com/watch?v=i9Il79a2uBU

Posted by CoolDragon
2013. 11. 1. 14:43
    public static class Base64String
    {
        public static string Base64Encode(this string src, System.Text.Encoding enc)
        {
            byte[] arr = enc.GetBytes(src);
            return Convert.ToBase64String(arr);
        }

        public static string Base64Decode(this string src, System.Text.Encoding enc)
        {
            byte[] arr = Convert.FromBase64String(src);
            return enc.GetString(arr);
        }
    }
Posted by CoolDragon
2011. 3. 31. 14:47
재귀호출에 따른 하위폴더 접근 및 파일 삭제 코드 샘플이다.

샘플에는 선택한 경로에 .svn폴더가 있으면 그 .svn 폴더 정보를 모두 삭제한다.



RemoveSvn.zip

Posted by CoolDragon
2010. 7. 22. 10:25
WCF 서비스를 REST 방식으로 웹 서비스를 이용하고자 샘플을 만들어 보았으며
아래 샘플소스는 WCF + ASP.NET + jQuery 를 사용하였다.


간단히 HttpModule을 이용하여 URL Rewrite처리를 하여 .svc 의 URL을 주소에서 감추고자 하였다.
(IIS7은 URL Rewrite를 IIS에서 제공해주므로 구현 안하고 설정으로 사용 가능 할듯)
(IIS6은 HttpModule방식과 정규식과 web.config와 같은 설정파일에서 관리)

[실제URL]


참고 URL
1.
[Programming JSON with WCF in .NET Framework 3.5]

2.
[Rob Windsor on WCF with REST, JSON and RSS]

3.
[WCF and JQuery Using JSON]

4.
[Consuming JSON data in .NET with WCF]

5.
[클라이언트 페이지 json요청]
[서버 WCF 서비스  json요청 처리]

6.WCF REST 방식에서 .SVC를 제거하는 방법
Posted by CoolDragon
2010. 7. 21. 18:43
오늘에서야 알았다.

c#에서의 Replace 함수와 javascript의 replace함수는 같은듯 하지만 다르다란 걸 말이다.
그 차이점이 뭐냐하면.. 아래와 같다.

[c#]
string str = "?,?,?,?,?";
str = str.Replace("?", "TEST"); MessageBox.Show(str);
이렇게 하면 결과는 아래와 같이 출력된다.
TEST,TEST,TEST,TEST,TEST
[javascript]
var str = "?,?,?,?,?"; str = str.replace("?", "TEST"); alert(str);
이렇게 하면 결과는 아래와 같이 출력된다.
TEST,?,?,?,?

나 혼자만 몰랐나? 응응?? -_-;;
Posted by CoolDragon