C#

LINQ

윤태영(Coding) 2023. 6. 16. 22:08

< LINQ 란? >

LINQ는 Language Integrated Query의 약자로, 데이터를 쿼리하는 기능을 프로그래밍 언어에 통합한 것이다. SQL을 생각하면 되는데, SQL처럼 데이터베이스뿐만 아니라, 배열이나 리스트 같은 메모리에 있는 데이터도 다룰 수 있다.

SQL과 비슷하다고 생각하면 된다. 예를 들어, 도서관에서 책을 찾고 있을 때. 이때 LINQ가 도서관 직원이라고 생각해보자. 이용자가 “모험 소설 중 300페이지 이상인 책들을 보여주세요”라고 요청하면, LINQ(도서관 직원)가 책장을 뒤져서 원하는 책들을 가져다 줄 것이다.

< LINQ 기본 문법 >

LINQ에는 주로 두 가지 문법이 사용된다. 하나는 Query 문법이고, 다른 하나는 Method 문법이다.

[Query 문법]

var result = from item in collection
             where item > 2
             select item;

 

[Method 문법]

var result = collection.Where(item => item > 2);

 

두 문법은 동일한 결과를 내지만, 개인의 취향이나 상황에 따라 선택해 사용할 수 있다.

< 실습 >

[Visual Studio 프로젝트 생성하기]
Visual Studio를 열고, "Create a new project"를 클릭해서. "콘솔 앱(.NET Framework)"를 선택하고, "다음" 버튼을 클릭한다.(이미 .Net Core 프로젝트가 있다면.. 프로젝트를 굳이 새로 안만들어도 될 듯 하다.)


여기서부터는 Program.cs 파일을 아래와 같이 만들 것이다.

밑의 코드를 우선 보자면, 예제 데이터를 리스트로 생성한 뒤(numbers 쪽) 에 LINQ를 사용해서 숫자를 필터링 했다.

queryResult 부분에서는 numbers 리스트에서 2보다 큰 숫자들로 구성되는 쿼리다.

methodResult 부분에서도 똑같이 numbers 리스트에서 2보다 큰 숫자들이 나오게 시도 해보았다.

그리고 출력문 부분에서 foreach문을 각각 1개씩 넣어서, 2보다 큰 숫자들이 나올 수 있게 문을 만들어 놨다.

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            // 예제 데이터
            List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

            // Query 문법 사용
            var queryResult = from number in numbers
                              where number > 2
                              select number;

            // Method 문법 사용
            var methodResult = numbers.Where(number => number > 2);

            // 결과 출력
            Console.WriteLine("Query 문법 결과:");
            foreach (var item in queryResult)
            {
                Console.WriteLine(item);
            }

            Console.WriteLine("Method 문법 결과:");
            foreach (var item in methodResult)
            {
                Console.WriteLine(item);
            }
        }
    }
}

이제 이걸 실행하면 아래와 같이, 원하던 대로 2보다 큰 수들이 나오는 걸 볼 수 있다.

LINQ는 Where 뿐만 아니라 Select, OrderBy, GroupBy 등 다양한 연산을 제공한다. 이것들은 데이터를 필터링하거나 정렬하고, 변환하는데 사용된다. (지금까지 보았다면 느꼈겠지만 사실 SQL과 비슷하다.)
[예시]

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            // 예제 데이터
            List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

            // Query 문법 사용
            var orderedResults = from number in numbers
                                 orderby number descending
                                 select number;

            // 또는

            var orderedMethodResults = numbers.OrderByDescending(number => number);

            // 결과 출력
            Console.WriteLine("Query 문법 결과:");
            foreach (var item in orderedResults)
            {
                Console.WriteLine(item);
            }

            Console.WriteLine("Method 문법 결과:");
            foreach (var item in orderedMethodResults)
            {
                Console.WriteLine(item);
            }
        }
    }
}

numbers라는 리스트 내의 숫자들을 내림차순으로 정렬하는 쿼리를 작성해보았다.

정상적으로 작동한다.

 

< 데이터 원본에서 데이터 조회하기 >

LINQ는 데이터 원본에서 데이터를 조회할 수 있다. 예를 들어, 데이터베이스에서 데이터를 가져오는 경우가 있다. 뿐만 아니라, XML, Docs, Collection, ADO.Net DataSet, Web Service, MS SQL Server 및 다른 Data Server와 같은 다양한 종류와도 호환이 된다.

단, 데이터베이스 연결과 관련된 코드이므로 Entity Framework가 프로젝트에 설정되어 있어야한다.

 

[예제코드]

※   DbContext를 상속받도록 구현하면 아래와 같은 장점이 있다고 한다.

  • [Entity Framework와의 통합] : DbContext를 상속받음으로써 MyDbContext 클래스는 Entity Framework의 핵심 기능과 기능을 이용할 수 있다고 한다. 이는 데이터베이스와의 상호작용, 데이터베이스 쿼리 작성, 트랜잭션 관리 등 다양한 기능을 제공해준다.
  • [데이터베이스 매핑] : DbContext를 상속받은 클래스는 데이터베이스 테이블과 개체 간의 매핑을 설정할 수 있다. 예를 들어, DbSet<T> 프로퍼티를 사용하여 데이터베이스 테이블과 개체 간의 관계를 정의하고, LINQ를 사용하여 데이터베이스에서 데이터를 쿼리할 수 있다.
  • [마이그레이션 지원] : DbContext를 상속받은 클래스를 사용하면 Entity Framework의 마이그레이션 기능을 활용하여 데이터베이스 스키마를 관리할 수 있다. 데이터베이스 스키마 변경을 코드로 추적하고, 변경 사항을 데이터베이스에 적용할 수 있다.
  • [유연한 데이터베이스 연결 구성] : DbContext를 상속받는 클래스에서 OnConfiguring 메서드를 재정의하여 데이터베이스 연결 구성을 설정할 수 있다. 이를 통해 데이터베이스 공급자, 연결 문자열 등을 유연하게 구성할 수 있다.
    ※ OnConfiguring 메서드에서 optionsBuilder.UseSqlServer("YourConnectionString") 부분은 사용자의 실제 데이터베이스 연결 문자열로 수정되어야 한다.
  • [테스트 용이성] : DbContext를 상속받는 클래스는 인터페이스 분리 원칙을 준수하면서, 데이터베이스와의 상호작용을 추상화할 수 있다. 이는 유닛 테스트 등에서 데이터베이스에 대한 의존성을 분리하고, 대체할 수 있는 가짜 데이터 또는 메모리 내 데이터베이스를 사용하여 테스트할 수 있는 유연성을 제공한다.
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace EntityFrameworkExample
{
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class MyDbContext : DbContext
    {
        public DbSet<User> Users { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("YourConnectionString");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new MyDbContext())
            {
                // 새 사용자 추가
                var newUser = new User { Name = "Alice", Age = 27 };
                db.Users.Add(newUser);
                db.SaveChanges();

                // 특정 사용자 조회
                var user = db.Users.FirstOrDefault(u => u.Name == "Alice");
                if (user != null)
                {
                    Console.WriteLine($"Id: {user.Id}, Name: {user.Name}, Age: {user.Age}");
                }

                // 사용자 업데이트
                user.Age = 30;
                db.SaveChanges();

                // 사용자 삭제
                db.Users.Remove(user);
                db.SaveChanges();
            }
        }
    }
}

위 코드는 Users는 데이터베이스의 테이블과 매핑되는 프로퍼티다. User는 해당 테이블의 레코드를 나타내는 클래스로 각 컬럼에 해당하는 프로퍼티들을 가진다. ※ 테이블을 매핑하기 전에 그 테이블이 MySql인지.. Oracle인지 모르기에 

먼저 연결 문자열과 공급자를 설정 해줘야한다.

MySQL 연결 설정을 할려고 한다면,  appsettings.json 파일에서 MySQL 연결 문자열을 설정한다. appsettings.json file이 없을 수 있는데, 없다면

솔루션 탐색기에서 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 "추가" → "새 항목"을 선택해서


"JSON 파일"을 검색하여 선택하고 파일 이름으로 "appsettings.json"을 입력하고 "추가" 버튼을 클릭한다.

Mysql을 쓰고 싶다면,

[appsettings.json 파일에서 MySQL 연결 문자열을 설정한다.] 

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Port=3306;Database=mydatabase;Uid=myusername;Pwd=mypassword;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

 

appsettings.json 파일의 "DefaultConnection" 항목에서 "Uid"와 "Pwd"는 사용자 이름과 비밀번호를 나타낸다. 이렇게 "DefaultConnection"에다가 사용자 이름과 비밀번호를 넣으면 연결 시 해당 정보를 사용하여 데이터베이스에 접속할 수 있다.
예를 들어, "myusername"은 사용자 이름이고, "mypassword"는 비밀번호를 나타내는 곳인데. 실제로 사용할 때에는 이 부분을 자신의 데이터베이스 사용자 이름과 비밀번호로 변경해야 한다.
또한, Database=mydatabase; 부분은 데이터베이스의 이름을 나타낸다. 여기서 "mydatabase"는 실제 사용할 데이터베이스의 이름으로 변경되어야 한다. 데이터베이스 이름은 데이터베이스 서버에서 생성한 데이터베이스의 실제 이름으로 바꿔주어야 한다. (보통 root로 되어 있는 것으로 알고 있다.)

[Startup.cs 파일에서 DbContext에 MySQL 연결 문자열을 설정 한다]

일부 템플릿에서는 프로젝트 생성 시 자동으로 생성되지 않을 수도 있다. Startup.cs 파일이 없는 경우, 아래와 같은 추가 방식으로 직접 Startup.cs 파일을 추가하면 된다.

public void ConfigureServices(IServiceCollection services)
{
    string connectionString = Configuration.GetConnectionString("DefaultConnection");
    services.AddDbContext<MyDbContext>(options =>
        options.UseMySql(connectionString));
    // ...
}

위의 코드로 Mysql을 설정해주면 되는데, 여기서 최상위문을 써야한다고 빨간 줄이 나오면, 그것은 .Net Framwork를 5.0이하로 쓰고 있다는 것이다. 왜냐하면 C# 9.0은 5.0이상 지원을 하기 때문이다. 이것 때문에 매우 골치가 아팠다.

때문에,  https://dotnet.microsoft.com/ko-kr/download/visual-studio-sdks?cid=getdotnetsdk 이 링크에서 8.0 x86을 설치하고. cmd로 dotnet 명령어를 통해 framework가 정상 설치되는 것을 확인 했다.

이후 visual studio installer에서 다시 업데이트를 해보았지만 되지 않아서,

cs8370를 해결하고자, Docs를 자세히 찾아보니... C# 9.0은 Version 5.x로 변경했어야 했다.

확인 한 뒤, Visual Studio Installer에서 개별 설치에서 5.0 version을 다운로드 했다.

 

이후로 안되어서, Console APP보다는 ASP.NET CORE Web App Project가 SQL에 적합하다고 해서,  적합한 Project를 만들어서, 이후 StartUp.cs를 아래와 같이 변경 하고, 코드를 재수정 해주었다.

Program.cs도 재수정 해주었다.

using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace EntityFrameworkExample
{
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class MyDbContext : DbContext
    {
        public DbSet<User> Users { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("YourConnectionString");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new MyDbContext())
            {
                // 새 사용자 추가
                var newUser = new User { Name = "Alice", Age = 27 };
                db.Users.Add(newUser);
                db.SaveChanges();

                // 특정 사용자 조회
                var user = db.Users.FirstOrDefault(u => u.Name == "Alice");
                if (user != null)
                {
                    Console.WriteLine($"Id: {user.Id}, Name: {user.Name}, Age: {user.Age}");
                }

                // 사용자 업데이트
                user.Age = 30;
                db.SaveChanges();

                // 사용자 삭제
                db.Users.Remove(user);
                db.SaveChanges();
            }
        }
    }
}
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using EntityFrameworkExample;

namespace YourNamespace
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            string connectionString = Configuration.GetConnectionString("DefaultConnection");

            services.AddDbContext<MyDbContext>(options =>
                options.UseSqlServer(connectionString));

            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
     
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

 

 

 

연결 문자열에는 해당 데이터베이스에 연결하기 위한 필수 정보 (예: 서버, 포트, 데이터베이스 이름, 사용자 이름, 암호 등)가 포함되어야 한다.
연결 문자열을 통해 데이터베이스 유형을 확인하고, Entity Framework를 사용하여 해당 데이터베이스와 연결하여 작업할 수 있다.

 


🔗 Reference : 이것이 C#이다 3판 

https://product.kyobobook.co.kr/detail/S000201856223

 

이것이 C#이다 | 박상현 - 교보문고

이것이 C#이다 | 전문가로 레벨업!C# 프로그래밍 완전 정복이 책은 C#의 기본, 고급 문법과 더불어 기초 클래스 라이브러리까지 다루고 있다. 총 22개 장으로, 앞서 배운 내용을 활용하면서 단계별

product.kyobobook.co.kr

 

https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9

 

What's new in C# 9.0 - C# Guide

Get an overview of the new features available in C# 9.0.

learn.microsoft.com

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/

 

Language-Integrated Query (LINQ) (C#)

Learn about Language-Integrated Queries (LINQs) and review an example of the complete query operation.

learn.microsoft.com

https://www.javatpoint.com/what-is-linq

 

What is LINQ - Javatpoint

What is LINQ with java tutorial, features, history, variables, programs, operators, oops concept, array, string, map, math, methods, examples etc.

www.javatpoint.com