This guide walks through displaying a list of books using a layered architecture with DTOs, services, and a Razor view.
π§± Step 1: Define the DTO
Create a DTO to shape the data you want to expose in the view.
csharp
namespace library_system_dotnet.Models.Dto.Book
{
    public class BookReadDto
    {
        public int Id { get; set; }
        public string? ISBN { get; set; }
        public string? Title { get; set; }
        public string? Description { get; set; }
        public string? Author { get; set; }
        public int? PublishedYear { get; set; }
        public string? Genre { get; set; }
        public int? CopiesAvailable { get; set; }
        public int? TotalCopies { get; set; }
        public string? Location { get; set; }
        public string? CoverImageUrl { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime UpdatedAt { get; set; }
    }
}
π Step 2: Create a Mapper
Use a manual mapper to convert your Book entity to BookReadDto.
csharp
public static class BookMapper
{
    public static BookReadDto ToReadDto(Models.Book book)
    {
        return new BookReadDto
        {
            Id = book.Id,
            ISBN = book.ISBN,
            Title = book.Title,
            Description = book.Description,
            Author = book.Author,
            PublishedYear = book.PublishedYear,
            Genre = book.Genre,
            CopiesAvailable = book.CopiesAvailable,
            TotalCopies = book.TotalCopies,
            Location = book.Location,
            CoverImageUrl = book.CoverImageUrl
        };
    }
}
π§© Step 3: Define the Service Contract
csharp
public interface IBookService
{
    Task<IEnumerable<BookReadDto>> GetAllAsync(CancellationToken ct = default);
}
βοΈ Step 4: Implement the Service
csharp
public class BookService : IBookService
{
    private readonly AppDbContext _context;
    private readonly IMapper _mapper;
    public BookService(AppDbContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }
    public async Task<IEnumerable<BookReadDto>> GetAllAsync(CancellationToken ct = default)
    {
        var books = await _context.Books.ToListAsync(ct);
        return _mapper.Map<IEnumerable<BookReadDto>>(books);
    }
}
π§ Step 5: Register Services in Program.cs
csharp
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddControllersWithViews();
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddScoped<IBookService, BookService>();
π― Step 6: Create the Controller Action
csharp
[HttpGet("books")]
public async Task<ActionResult<IEnumerable<BookReadDto>>> Index()
{
    var books = await _context.Books.ToListAsync();
    var bookDtos = books.Select(BookMapper.ToReadDto);
    return View(bookDtos);
}
Note: You can switch to _bookService.GetAllAsync() once your service is fully wired.
πΌοΈ Step 7: Create the Razor View (Views/Books/Index.cshtml)
razor
@model IEnumerable<library_system_dotnet.Models.Dto.Book.BookReadDto>
@{
    ViewData["Title"] = "Books";
}
<h1>Books</h1>
<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>@Html.DisplayNameFor(model => model.ISBN)</th>
            <th>@Html.DisplayNameFor(model => model.Title)</th>
            <th>@Html.DisplayNameFor(model => model.Description)</th>
            <th>@Html.DisplayNameFor(model => model.Author)</th>
            <th>@Html.DisplayNameFor(model => model.PublishedYear)</th>
            <th>@Html.DisplayNameFor(model => model.Genre)</th>
            <th>@Html.DisplayNameFor(model => model.CopiesAvailable)</th>
            <th>@Html.DisplayNameFor(model => model.TotalCopies)</th>
            <th>@Html.DisplayNameFor(model => model.Location)</th>
            <th>@Html.DisplayNameFor(model => model.CoverImageUrl)</th>
            <th>@Html.DisplayNameFor(model => model.CreatedAt)</th>
            <th>@Html.DisplayNameFor(model => model.UpdatedAt)</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
    @foreach (var item in Model)
    {
        <tr>
            <td>@Html.DisplayFor(modelItem => item.ISBN)</td>
            <td>@Html.DisplayFor(modelItem => item.Title)</td>
            <td>@Html.DisplayFor(modelItem => item.Description)</td>
            <td>@Html.DisplayFor(modelItem => item.Author)</td>
            <td>@Html.DisplayFor(modelItem => item.PublishedYear)</td>
            <td>@Html.DisplayFor(modelItem => item.Genre)</td>
            <td>@Html.DisplayFor(modelItem => item.CopiesAvailable)</td>
            <td>@Html.DisplayFor(modelItem => item.TotalCopies)</td>
            <td>@Html.DisplayFor(modelItem => item.Location)</td>
            <td>@Html.DisplayFor(modelItem => item.CoverImageUrl)</td>
            <td>@Html.DisplayFor(modelItem => item.CreatedAt)</td>
            <td>@Html.DisplayFor(modelItem => item.UpdatedAt)</td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
    }
    </tbody>
</table>
β Final Notes
- Make sure the view file is named Index.cshtml and placed in /Views/Books/.
 - Ensure your controller inherits from Controller, not ControllerBase, to support view rendering.
 - If you use AutoMapper, ensure the profile is registered and the mapping is defined.
 
    
Top comments (0)