Database
.Net의 데이터베이스 처리 관련 클래스
ASP는 DB마다 다른 클래스를 사용한다. 아래의 클래스의 접두어로 DB이름이 붙으며, 아래 클래스 별로 같은 기능을 수행한다.
- Connection : 데이터베이스 연결
- Command : 명령 실행
- DataReader : Select의 결과값
- DataSet : 메모리 상의 데이터베이스로 Select같은 결과 값 저장
- DataAdapter : 명령어 전달 및 실행 후 DataSet에 값 전달
- DataTable : DataSet 안의 메모리 상의 테이블
- DataView : DataSet 안의 메모리 상의 뷰
주요 작업
연결
SqlConnection con = new SqlConnection(); // Web.config에 설정된 값 con.ConnectionString = ConfigurationManager.ConnectcionStrings["ConnectionString"].ConnectionString; con.Open(); // DB 작업 con.Close();
명령
SqlCommand cmd = new SqlCommand(); cmd.Connection = con; // 명령에 사용할 커넥션 지정 cmd.CommandText = @"sql문"; //sql또는 프로시저 문 지정 cmd.CommandType = CommandType.Text // CommandType의 형식, Text=SQL, StoredProcedure = 프로시저 cmd.ExecuteNonQuery(); // Select 이외의 명령, 영향 받은 행의 개수 반환 cmd.ExecuteReader(); // Select문 실행, 다중 값 반환 cmd.ExecuteScalar(); // Select문 실행, 단일 값 반환
결과 처리
SqlDataReader reader = cmd.ExecuteReader(); reader.FieldCount; // Select 문 실행결과에서 열의 개수 reader.HasRows; // Select 문 실행 후 반환되는 레코드 존재 여부 while(reader.Read()) { reader["컬럼명"]; reader.GetString(1); // 매개변수로 인덱스를 받는다. } reader.Close();
DataAdapter & DataSet
메모리에 테이블 등을 생성해 데이터를 조작하는 클래스
DataAdapter와 DataReader의 차이
- DataReader: 연결 지향적이고, 단방향, 읽기 전용으로 데이터를 순차적으로 읽어야 할 때 사용. 성능이 중요하고 데이터를 한 번에 한 행씩 처리해야 할 때 적합.
- DataAdapter: 비연결 지향적이고, 데이터를 로컬로 가져와 조작 및 캐싱해야 할 때 사용. 오프라인 데이터 처리 및 데이터베이스에 대한 대량의 변경 사항을 반영할 때 적합.
SqlDataAdapter adapter = new SqlDataAdapter(); adapter.SelectCommand = cmd; DataSet ds = new DataSet(); adapter.Fill(ds, "테이블이름"); foreach (DataRow row in dataSet.Tables["테이블이름"].Rows) { Console.WriteLine(row["컬럼이름"]); }
Entity Framework
.Net의 ORM, Java의 JPA와 비슷한듯 하다. 실제로 DB 연결을 담당하는 클래스는 DbContext이며 일반적으로는 1개의 클래스만 사용한다. 아래의 경우에는 여러개를 사용할 수 있다(출처). 여러개를 사용한다면 스키마 단위로 나누는 것이 좋아보인다.
- 여러 데이터베이스에 접속해야하는 경우
- 도메인 모델이 복잡해 명확한 분리가 필요한 경우
- 모듈러 모놀리식을 구축할 경우
- 읽기 전용 복제본에 접근할 경우(NoTracking으로 성능 향상 가능)
DB에 변경 내용 반영
dotnet tool install --global dotnet-ef # dotnet ef 설치
dotnet ef migrations add init # 마이그레이션 추가, 코드 변경 이후 사용, init은 이름이니 변경가능
dotnet ef database update # 데이터베이스 생성
Model
// 대체로 선언방법과 관계형성은 JPA와 비슷하다.
namespace CrosswordBackend.Models
{
public class Crossword
{
[Key] //기본키 지정
public int Id { get; set; }
public string Info { get; set; }
public DateTime Date { get; set; }
// 외래키 다
public virtual IList<Answer> Answers { get; set; } = new List<Answer>();
public virtual IList<Record> Records { get; set; } = new List<Record>();
}
}
namespace CrosswordBackend.Models
{
[PrimaryKey(nameof(CrosswordId), nameof(BlockNumber), nameof(IsVertical))] //복합키 지정
public class Answer
{
// JPA와 다르게 Id 필드를 선언해야한다.
public int CrosswordId { get; set; }
public int WordId { get; set; }
public int BlockNumber { get; set; }
public bool IsVertical { get; set; }
// 외래키 일
public virtual Word Word { get; set; }
public virtual Crossword Crossword { get; set; }
}
}
// DBContext = DB 세션, 사용 방법은 2가지가 있다
// 1. WebApplicationBuilder에 등록후 DI로 사용
// 2. using문에서 직접 생성해서 사용
namespace CrosswordBackend.Config
{
public class ApplicationDbContext : DbContext
{
// 필드명 기준으로 테이블 명이 결정된다.
public DbSet<Word> Word { get; set; }
public DbSet<Crossword> Crossword { get; set; }
public DbSet<Meaning> Meaning { get; set; }
public DbSet<Answer> Answer { get; set; }
public DbSet<Record> Record { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
}
}
Query
// 대체로 Java의 Querydsl과 비슷하다.
using (var context = new ApplicationDbContext())
{
// DTO 사용
var products = context.Products
.Where(p => p.Price > 100)
.Select(p => new ProductDto
{
Name = p.Name,
Price = p.Price
})
.ToList();
// Join
var products = from p in context.Products
join c in context.Categories on p.CategoryId equals c.CategoryId
select new ProductDto
{
Name = p.Name,
Price = p.Price,
CategoryName = c.Name
};
var result = products.ToList();
// Create
await context.Products.AddAsync(new ProductModel());
// 기본적으로 조회 후 변경 및 삭제를 수행한다. JPA와 비슷
// Update
context.Products.Update(result);
// Delete
context.Products.Remove(result);
// DB에 반영, 암시적으로 트랜잭션을 수행한다.
await context.SaveChangesAsync();
}