티스토리 뷰
저장 프로시저 결과 매핑 전략
키 없는 엔티티 타입 (Keyless Entity Type)으로 매핑 (권장):
- 저장 프로시저의 결과가 BookTag 엔티티의 컬럼과 완전히 일치하지 않거나, 더 많은 조인된 정보(예: Book.Title, Tag.Name)를 포함하고 싶을 때 사용합니다.
- 이 방법은 저장 프로시저의 결과를 읽기 전용으로 사용하며, 복잡한 쿼리나 보고서 출력에 매우 유용합니다.
여기서는 키 없는 엔티티 타입을 사용하여 저장 프로시저 결과를 매핑하는 방법을 예제로 설명하겠습니다.
예제: 저장 프로시저 결과 매핑
시나리오: BookId, TagId뿐만 아니라 Book의 제목(Title), Tag의 이름(Name)까지 포함된 상세 정보를 가져오는 저장 프로시저를 만들고 싶습니다.
1단계: MSSQL 저장 프로시저 생성
데이터베이스에 다음 저장 프로시저를 생성합니다.
SQL
-- MSSQL Server에서 실행할 저장 프로시저
CREATE PROCEDURE GetBookTagDetails
AS
BEGIN
SELECT
bt.BookId,
bt.TagId,
b.Title AS BookTitle, -- Book 테이블에서 제목 가져오기
t.Name AS TagName, -- Tag 테이블에서 이름 가져오기
bt.CreatedDate -- BookTag 테이블의 CreatedDate
FROM
BookTags bt
JOIN
Books b ON bt.BookId = b.Id
JOIN
Tags t ON bt.TagId = t.Id;
END;
GO
2단계: 키 없는 엔티티 타입 모델 정의
저장 프로시저의 결과 컬럼과 정확히 일치하는 새 C# 클래스를 정의합니다. 이 클래스는 데이터베이스 테이블에 직접 매핑되지 않으므로, 기본 키가 필요 없습니다.
// Models/BookTagDetailViewModel.cs
// 이 클래스는 데이터베이스 테이블에 직접 매핑되지 않고, 저장 프로시저의 결과를 받기 위한 DTO (Data Transfer Object) 역할을 합니다.
using System;
namespace CoreMVC.Models
{
public class BookTagDetailViewModel
{
// 저장 프로시저의 SELECT 절 컬럼 이름과 정확히 일치해야 합니다.
public int BookId { get; set; }
public int TagId { get; set; }
public string BookTitle { get; set; } // 조인된 Book의 제목
public string TagName { get; set; } // 조인된 Tag의 이름
public DateTime CreatedDate { get; set; }
}
}
3단계: AppDbContext에 키 없는 엔티티 타입 등록
AppDbContext에 새 DbSet을 추가하고, OnModelCreating에서 이 타입을 **키 없는 엔티티(Keyless Entity)**로 구성합니다.
// Data/AppDbContext.cs
using Microsoft.EntityFrameworkCore;
using CoreMVC.Models;
namespace CoreMVC.Data
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
// ----- 새로운 DbSet 추가 (키 없는 엔티티) -----
public DbSet<BookTagDetailViewModel> BookTagDetails { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// --- 키 없는 엔티티 타입 구성 ---
modelBuilder.Entity<BookTagDetailViewModel>()
.HasNoKey(); // 이 엔티티는 기본 키가 없음을 EF Core에 알립니다.
// 필요하다면 특정 뷰에 매핑할 수도 있습니다.
// 저장 프로시저 결과를 직접 매핑할 때는 ToView()를 사용하지 않습니다. .ToView("SomeDatabaseView");
// 실제 DB 뷰가 있다면 여기에 매핑
}
}
}
#### 4단계: 컨트롤러에서 저장 프로시저 호출 및 결과 사용
이제 컨트롤러에서 `FromSqlRaw` 또는 `FromSqlInterpolated` 메서드를 사용하여
저장 프로시저를 실행하고 결과를 `BookTagDetailViewModel` 타입으로 받을 수 있습니다.
// Controllers/ProductsController.cs
using Microsoft.AspNetCore.Mvc;
using MyWebApp.Models;
using MyWebApp.Data;
using Microsoft.EntityFrameworkCore; // FromSqlRaw/FromSqlInterpolated를 위해 필요
namespace MyWebApp.Controllers
{
public class ProductsController : Controller
{
private readonly AppDbContext _context; // AppDbContext로 변경
public ProductsController(AppDbContext context) // AppDbContext로 변경
{
_context = context;
}
// 기존 Index 액션은 그대로
public async Task<IActionResult> Index()
{
// 예시로 Product 목록을 그대로 보여줍니다.
return View(await _context.Products.ToListAsync());
}
// --- 저장 프로시저 결과를 표시하는 새로운 액션 ---
public async Task<IActionResult> BookTagDetails()
{
// 저장 프로시저 GetBookTagDetails를 호출하고 결과를 BookTagDetailViewModel에 매핑
var bookTagDetails = await _context.BookTagDetails
.FromSqlRaw("EXEC GetBookTagDetails")
.ToListAsync();
return View(bookTagDetails);
}
// Create, Edit, Delete 액션 등은 기존과 동일하게 Book/Tag/BookTag DbSet을 직접 사용합니다.
// 예를 들어, BookTag에 새 데이터를 추가하는 Create 액션:
[HttpGet]
public IActionResult CreateBookTag()
{
ViewBag.Books = _context.Books.Select(b => new { b.Id, b.Title }).ToList();
ViewBag.Tags = _context.Tags.Select(t => new { t.Id, t.Name }).ToList();
return View(); // 빈 폼
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateBookTag(int bookId, int tagId)
{
var newBookTag = new BookTag { BookId = bookId, TagId = tagId, CreatedDate = DateTime.Now };
_context.BookTags.Add(newBookTag); // BookTag DbSet을 직접 사용하여 INSERT
await _context.SaveChangesAsync();
TempData["SuccessMessage"] = "BookTag가 성공적으로 추가되었습니다!";
return RedirectToAction(nameof(BookTagDetails));
}
// ... 기존 CRUD 액션 (생략) ...
}
}
5단계: 저장 프로시저 결과 표시를 위한 뷰 (Views/Products/BookTagDetails.cshtml)
@model IEnumerable<CoreMVC.Models.BookTagDetailViewModel>
@{
ViewData["Title"] = "도서-태그 상세 정보";
}
<h1>@ViewData["Title"]</h1>
<table class="table">
<thead>
<tr>
<th>도서 ID</th>
<th>태그 ID</th>
<th>도서 제목</th>
<th>태그 이름</th>
<th>추가된 날짜</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@Html.DisplayFor(modelItem => item.BookId)</td>
<td>@Html.DisplayFor(modelItem => item.TagId)</td>
<td>@Html.DisplayFor(modelItem => item.BookTitle)</td>
<td>@Html.DisplayFor(modelItem => item.TagName)</td>
<td>@Html.DisplayFor(modelItem => item.CreatedDate)</td>
</tr>
}
</tbody>
</table>
<div>
<a asp-action="Index">제품 목록으로 돌아가기</a>
</div>
정리:
- DbSet<BookTag> 자체는 여전히 BookTags 테이블에 직접 매핑됩니다.
- 저장 프로시저를 통해 Book, Tag 테이블을 조인한 결과를 DbSet<BookTag>에 "테이블처럼" 직접 매핑하는 것은 EF Core의 기본 설계에는 맞지 않습니다.
- 저장 프로시저의 쿼리 결과(읽기 전용)를 가져오려면, FromSqlRaw 또는 FromSqlInterpolated 메서드를 사용하고, 그 결과를 받기 위한 별도의 키 없는 엔티티 타입(예: BookTagDetailViewModel)을 정의하는 것이 가장 일반적이고 권장되는 방법입니다. 이 방법은 저장 프로시저가 반환하는 컬럼에 정확히 일치하는 C# 객체를 얻을 수 있게 해줍니다.
728x90
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- 표현 언어(expression language)
- 제품 등록
- java web-mvc
- 진수 변환
- React
- MainActor
- jstl(java standard tag library)-core
- 인텔리제이(intellij)
- jstl(java standard tag library)
- java-개발 환경 설정하기
- 메이븐(maven)
- 스프링 프레임워크(spring framewordk)
- .submit()
- await
- 스프링 프레임워크(spring framework)
- nl2br
- java.sql
- REST API
- System.Diagnostics
- docker
- In App Purchase
- 특정 문자를 기준으로 자르기
- error-java
- 람다식(lambda expression)
- 스프링 시큐리티(spring security)-http basic 인증
- 스프링 시큐리티(spring security)
- jsp 오픈 소스
- java 키워드 정리
- 문자 자르기
- system.io
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
글 보관함