WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

ASP.NET Core MVC で Bootstrap5 Modal の利用

by WebSurfer 2023年11月11日 10:46

Visual Studio 2022 を使って作成する ASP.NET Core MVC アプリで、データベースのテーブル一覧を表示し、その中の項目を選択すると、その詳細を Bootstrap5 の Modal を利用して表示する方法を書きます。

Bootstrap Modal に詳細を表示

この記事ではターゲットフレームワークを .NET 7.0 としています。.NET 5.0 以前をターゲットフレームワークとして作成したプロジェクトでは Bootstrap のバージョンが異なるので注意してください。

この記事のベースとしたアプリは、Microsoft のチュートリアル「CRUD 機能を実装する」です。そのアプリの Student テーブルのレコード一覧を表示する Index ページと各レコードの詳細を表示する Details ページを使います。

チュートリアルでは、Index ページに一覧表示されている各項目の右横のアクションリンク Details をクリックすると、別ページ Details に遷移し、遷移した Details ページ上で選択した項目の詳細を表示するようになっています。チュートリアルの「Details ビューに登録を追加する」セクションの下の方の画像を見てください。

それを、別ページ Details に遷移しないで、この記事の上の画像のように Index ページ上で Bootstrap Modal 内に詳細を表示するようにしてみます。

Bootstrap については、Visual Studio 2022 のテンプレートで、ターゲットフレームワーク .NET 7.0 として作成した ASP.NET Core MVC アプリのプロジェクトに Bootstrap.js, Boorstrap.css v5.1.0 が含まれています。Bootstrap Modal を使うために必要なものもそれらの中に含まれているので何も追加する必要はありません。

レイアウトページ _Layout.cshtml にはプロジェクトの Bootstrap.js, Boorstrap.css を参照する link タグ、script タグが含まれていますので、スキャフォールディングの際 _Layout.cshtml を使用するように設定すれば、それらが参照されるようになります。

Bootstrap5 Modal の詳しい説明は Bootstrap のドキュメント「Modal (モーダル)」にあります。普通の html + css + javascript のページに Bootstrap5 Modal を組み込むなら、そのドキュメントから十分な情報が得られると思います。

ASP.NET Core MVC アプリに、Bootstrap5 Modal を組み込んで、Index ページにのアクションリンクをクリックして詳細データを取得し、Index ページ上で Modal に表示するにはどうしたらいいかというのががこの記事のポイントで、それを以下に述べます。

まずコントローラーのアクションメソッドですが、Index はチュートリアルのものと全く同じ、Details も最後の一行で部分ビューを返すように変更する以外はチュートリアルのものと同じです。

コードは以下の通りとなります。この記事ではアクションメソッドの名前を Index3, Details3 としています。

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MvcNet7App.Data;
using MvcNet7App.Models;

namespace MvcNet7App.Controllers
{
    public class StudentsController : Controller
    {
        private readonly SchoolContext _context;

        public StudentsController(SchoolContext context)
        {
            _context = context;
        }

        // Student のレコード一覧を取得しビューに渡す
        public async Task<IActionResult> Index3()
        {
            return _context.Students != null ?
                   View(await _context.Students.ToListAsync()) :
                   Problem("Entity set 'SchoolContext.Students'  is null.");
        }

        // 詳細を表示する部分ビュー用のアクションメソッド
        public async Task<IActionResult> Details3(int? id)
        {
            if (id == null || _context.Students == null)
            {
                return NotFound();
            }

            //var student = await _context.Students
            //    .FirstOrDefaultAsync(m => m.ID == id);
            var student = await _context.Students
                                        .Include(s => s.Enrollments)
                                            .ThenInclude(e => e.Course)
                                        .AsNoTracking()
                                        .FirstOrDefaultAsync(m => m.ID == id);

            if (student == null)
            {
                return NotFound();
            }

            // 部分ビューを返すように変更
            return PartialView(student);
        }
    }
}

次に、上の Index アクションメソッドに対応するビューですが、スキャフォールディングで自動生成されたコードに (1) Modal 部分を実装、(2) Details アクションリンクを JavaScript のメソッドを呼び出すよう変更、(3) fetch API でサーバーに要求を出し、応答として返される部分ビューを Modal 内の div 要素に書き込んだ後 Modal を表示する JavaScript のメソッドを追加します。

コードは以下の通りです。Modal 部分は Bootstrap 5 のドキュメント「Modal (モーダル)」の「Static backdrop」セクションのサンプルコードをコピペし、若干修正をして利用しています。詳細を書き込む div 要素に id="details" を追加したこと、デフォルトの "medium" サイズでは幅が足らないので CSS に modal-lg を追加したこと、Understood ボタンは削除したことが主な違いです。

@model IEnumerable<MvcNet7App.Models.Student>

@{
    ViewData["Title"] = "Index3";
}

<h1>Index3</h1>

<!-- (1) Modal 部分を実装 -->
<div class="modal fade"
     id="staticBackdrop"
     data-bs-backdrop="static"
     data-bs-keyboard="false" tabindex="-1"
     aria-labelledby="staticBackdropLabel"
     aria-hidden="true">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"
                    id="staticBackdropLabel">
                    Student Details
                </h5>
                <button type="button"
                        class="btn-close"
                        data-bs-dismiss="modal"
                        aria-label="Close">
                </button>
            </div>
            <div class="modal-body" id="details">
                ...
            </div>
            <div class="modal-footer">
                <button type="button"
                        class="btn btn-primary"
                        data-bs-dismiss="modal">
                    Close
                </button>
            </div>
        </div>
    </div>
</div>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.LastName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstMidName)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstMidName)
                </td>
                <td>
                    @* (2) クリックで JavaScript の showDetails
                    を呼び出す。引数には Student の ID を渡す *@ 
                    <a href="javascript:showDetails(@item.ID)">
                        Details
                    </a>
                </td>
            </tr>
        }
    </tbody>
</table>

@section Scripts {
    <script type="text/javascript">

        // (3) fetch API でサーバーに要求を出し、応答として返される
        // 部分ビューを Modal 内の div 要素に書き込んだ、後 Modal を
        // 表示する

        // Modal を表示するためのヘルパーメソッド
        const showBootstrapModal = () => {
            // staticBackdrop は Modal の div 要素の id
            let divModal = document.getElementById('staticBackdrop');
            let myModal = new bootstrap.Modal(divModal);
            myModal.show();
        }

        const showDetails = async (id) => {
            // 部分ビュー用アクションメソッドの url
            // id は Student テーブルのレコードの ID
            const url = "/Students/Details3/" + id;

            // Student 詳細を表示する Modal 内の div 要素
            const resultDiv = document.getElementById('details');

            const response = await fetch(url);
            if (response.ok) {
                // 部分ビューのテキストを取得
                const text = await response.text();

                // テキストを Modal 内の div 要素に書き込み
                resultDiv.innerHTML = text;

                // Modal を表示
                showBootstrapModal();
            }            
        }

    </script>
}

最後に、Details アク��ョンメソッドに対応する部分ビューですが、以下の通りとなっています。チュートリアルで自動生成されたコードから部分ビューに不要な部分を削除しただけです。

@model MvcNet7App.Models.Student

<div>
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.LastName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.LastName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.FirstMidName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.FirstMidName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.EnrollmentDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.EnrollmentDate)
        </dd>

        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Enrollments)
        </dt>
        <dd class="col-sm-10">
            <table class="table">
                <tr>
                    <th>Course Title</th>
                    <th>Grade</th>
                </tr>
                @foreach (var item in Model.Enrollments)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Course.Title)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Grade)
                        </td>
                    </tr>
                }
            </table>
        </dd>
    </dl>
</div>

Tags: , , ,

CORE

ASP.NET Core のカスタムクラスから HttpContext にアクセス

by WebSurfer 2023年9月19日 14:59

ASP.NET Core Web アプリ内のカスタムクラスから HttpContext にアクセスする方法を備忘録として書いておきます。下の画像は、カスタムクラスから HttpContext にアクセスし、ログイン中のユーザー名を取得して表示したものです。赤枠の部分がそれです。

ログイン中のユーザー名を取得

その方法は Microsoft のドキュメント「ASP.NET Core で HttpContext にアクセスする」の中の「カスタム コンポーネントから HttpContext を使用する」を見ればすぐ分かるのですが、どこに書いてあったか忘れて探し回ることがないように自分のブログに書いておくことにしました。

基本的にどうするかというと、Visual Studio のテンプレートを使って作成する ASP.NET Core アプリには Dependency Injection (DI) 機能が組み込まれていますのでそれを利用します。

まず、DI コンテナに IHttpContextAccessor サービスと、それを利用するカスタムクラスを登録します。具体的には、Program.cs に以下のようにコードを追加します。

using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.EntityFrameworkCore;
using MvcNet7App.Data;
using MvcNet7App.Libraries;

var builder = WebApplication.CreateBuilder(args);

// ・・・中略・・・

// DI コンテナに IHttpContextAccessor サービスを登録
builder.Services.AddHttpContextAccessor();

// それを利用するカスタムクラスを登録
builder.Services.AddTransient<IUserRepository, UserRepository>();

// ・・・後略・・・

カスタムクラス UserRepository の例は以下の通りです。DI 機能によって、コンストラクタ経由で IHttpContextAccessor サービスを inject できるようにしています。LogCurrentUser メソッドは、inject された IHttpContextAccessor サービスから HttpContext にアクセスし、ログイン中のユーザー名を取得して戻り値として返します。

namespace MvcNet7App.Libraries
{
    public class UserRepository : IUserRepository
    {
        // DI でコンストラクタの引数経由 IHttpContextAccessor 
        // サービスを inject できるよう設定
        private readonly IHttpContextAccessor _httpContextAccessor;

        public UserRepository(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public string? LogCurrentUser()
        {
            // ログイン中のユーザー名を取得
            var username = _httpContextAccessor.HttpContext?
                           .User.Identity?.Name;
            return username;
        }
    }

    public interface IUserRepository
    {
        public string? LogCurrentUser();
    }
}

上のカスタムクラスを利用する場合は、組み込みの DI 機能を使ってカスタムクラスのインスタンスが渡されるようにします。例えば、MVC の Controller の場合、以下のようにコンストラクタの引数を経由してカスタムクラスのインスタンスを渡しています。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using MvcNet7App.Libraries;
using MvcNet7App.Models;
using System.Diagnostics;

namespace MvcNet7App.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        // 組み込みの DI 機能によりコンストラクタの引数経由で
        // カスタムクラスのインスタンスが渡される
        private readonly Libraries.IUserRepository _user;

        public HomeController(ILogger<HomeController> logger, 
                              IUserRepository user)
        {
            _logger = logger;
            _user = user;
        }

        public IActionResult Index()
        {
            ViewBag.Username = _user.LogCurrentUser();

            return View();
        }

        // ・・・中略・・・
    }
}

ユーザーが上の Home/Index を要求すると、ASP.NET は HomeController のインスタンスを生成して要求を処理しますが、その際 DI コンテナに登録された UserRepository クラスと IHttpContextAccessor サービスのインスタンスは自動的・連鎖的に生成され、コンストラクタの引数経由で inject されます。

なので、Index アクションメソッドで、inject された UserRepository クラスの LogCurrentUser メソッドを使ってログイン中のユーザー名を取得できます。

取得したユーザー名を ViewBag を使って View に渡し、ブラウザ上に表示したのがこの記事の一番上の画像です。赤枠の中の surfer@mail.example.com がそれです。


なお、Controller や View から HttpContext にアクセスする場合は、DI 機能などは使う必要はなく、もっと簡単にできます。詳しくは「ASP.NET Core の HttpContext にアクセスする」を見てください。

Tags: , ,

CORE

ドロップダウンリストを使って絞込み (CORE)

by WebSurfer 2023年8月2日 15:29

先の記事「DropDownList を使って絞込み」の .NET 7.0 ASP.NET Core MVC 版を作ってみました。

ドロップダウンリストを使って絞込み

上の画像がそれで、Microsoft の SQL Server サンプルデータベース Northwind の Orders テーブルから、ドロップダウンリストで選択された Customer (注文主) および Employee (注文を扱った従業員) を WHERE 句の選択条件にしてレコードを抽出し、抽出されたレコードを一覧表示するものです。

SQL Server の Orders テーブルは下の画像の構造となっており、複数の顧客の過去の注文データが 830 レコード含まれています。CustomerID, EmployeeID, ShipVia フィールドは、それぞれ Customers, Employees, Shippers テーブル (顧客、従業員、商品の輸送者の詳細) に FK 制約が張られています。

Northwind Orders テーブル

この Orders テーブルから、この記事の一番上の画像のように、ドロップダウンリストの選択結果に応じてレコードを抽出し、そのレコード一覧を表示する ASP.NET Core MVC アプリを作ります。作り方の概要を以下に書きます。

(1) プロジェクトの作成

Visual Studio 2022 のテンプレートの中から「ASP.NET Core Web アプリ (Model-View-Controller)」を選び ASP.NET Core MVC アプリのプロジェクトを作成します。

テンプレート

この記事の例ではターゲットフレームワークを .NET 7.0 にしています。その他のバージョンではこの記事とは多少違いがあるかもしれません。

(2) コンテキストクラスとエンティティクラスの作成

Entity Framework を利用するので、リバースエンジニアリングという手法を使って、SQL Server データベース Northwind からコンテキストクラスとエンティティクラスを生成します。

作成方法は Microsoft のドキュメント「スキャフォールディング (リバース エンジニアリング)」を見てください。各パラメータについては、こちらの記事 Scaffold-DbContext がまとまっていて分かりやすいと思います。

参考に、EF Core パッケージマネージャーコンソール (PMC) ツールを使った場合の Scaffold-DbContext コマンドの例を下に載せておきます。

Scaffold-DbContext -Connection "Data Source=(localdb)\MSSQLLocalDb;Initial Catalog=Northwind;Integrated Security=True" -Provider Microsoft.EntityFrameworkCore.SqlServer -ContextDir Data -OutputDir Models -Tables Products, Categories, Suppliers, Customers, Orders, Employees, Shippers, "Order Details" -DataAnnotations

生成されたコンテキストクラスとエンティティクラスのダイアグラムを EF Core Power Tools を使って表示すると以下のようになります。

DbContext ダイアグラム

上の画像の内、この記事で関係するのは Order, Customer, Employee, Shipper です。

(3) Controller と View の作成

Program.cs で DI コンテナに NorthwindContext を追加します。コードは以下の通りです。

builder.Services.AddDbContext<NorthwindContext>(options =>
    options.UseSqlServer(builder.Configuration
           .GetConnectionString("NorthwindConnection")));

appsettings.json に接続文字列を追加します。名前は上のコードの "NorthwindConnection" とし、接続文字列本体はリバースエンジニアリ���グの Scaffold-DbContext コマンドで使ったものと同じにします。ただし、文字列中の \ は \\ にエスケープする必要があるので注意してください。

その後で、Visual Studio のスキャフォールディング機能を使って、Orders テーブルの CRUD (Create / Read / Upadate / Delete) を行う Controller と View 一式を生成します。

スキャフォールディング

アプリを実行してみて CRUD 機能が期待通り動くことを確認します。

(4) アクションメソッドの追加

ドロップダウンリストによる絞り込み機能を持つアクションメソッドを、上のステップ (3) で作った Controller に追加します。下のコードの Search メソッドと SearchResult メソッドが追加したアクションメソッドです。

基本的には、先の記事「jQuery ajax で部分ビューの呼出・表示 (CORE)」で書いたのと同様に、ajax によって部分ビューを呼び出して抽出結果のレコード一覧を表示します。

まず、Search メソッドに対応する Search.cshtml (コードは下のステップ(5)参照) にあるボタンクリックで、jQuery ajax を使ってアクションメソッド SearchResult を呼び出します。

アクションメソッド SearchResult が呼び出される時、ドロップダウンリストの選択結果が引数 customerId, employeeId に渡されます。SearchResult は渡された引数の内容に応じてレコードを抽出し、結果を Model として部分ビュー SearchResult.cshtml に渡します。

部分ビューSearchResult.cshtml がレンダリングした html ソース (抽出結果のテーブル) が SearchResult メソッドの応答として返されますので、それをドロップダウンリストの下に表示するようにしています。その結果がこの記事の一番上の画像です。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using MvcNet7App.Data;
using MvcNet7App.Models;

namespace MvcNet7App.Controllers
{
    public class OrdersController : Controller
    {
        private readonly NorthwindContext _context;

        public OrdersController(NorthwindContext context)
        {
            _context = context;
        }

        // ・・・中略・・・

        // Customer と Employee の DropDownList で絞り込みが行えるページを追加
        public IActionResult Search()
        {
            ViewData["CustomerId"] = 
                new SelectList(_context.Customers, "CustomerId", "CompanyName");

            // Employees テーブルは FirstName と LastName でフィールドが別れて
            // いるので、以下のようにして FirstName と LastName を結合する
            var employeeList = _context.Employees
                               .Select(e => new 
                               {
                                   EmployeeId = e.EmployeeId.ToString(),
                                   EmployeeName = e.FirstName + " " + e.LastName
                               });

            ViewData["EmployeeId"] = 
                new SelectList(employeeList, "EmployeeId", "EmployeeName");
            return View();
        }

        // 部分ビュー用のアクションメソッド。DropDownList で ALL を選ぶと
        // 引数の customerId, employeeId には null が渡される
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> SearchResult(string customerId, 
                                   int? employeeId)
        {
            
            // Include が返すのは IIncludableQueryable<TEntity,TProperty>
            // 型なので、var order = ... とすると下の Where メソッドの所
            // で IQueryable<Order> を IIncludableQueryable<Order,Shipper>
            // に暗黙的に変換できませんというエラーになるので注意
            IQueryable<Order> orders = _context.Orders
                                       .Include(o => o.Customer)
                                       .Include(o => o.Employee)
                                       .Include(o => o.ShipViaNavigation);
            
            // null は ALL (全顧客) なのでスキップ
            if (customerId != null)
            {
                orders = orders.Where(o => o.CustomerId == customerId);
            }

            // null は ALL (全従業員) なのでスキップ
            if (employeeId != null)
            {
                orders = orders.Where(o => o.EmployeeId == employeeId);
            }

            return PartialView(await orders.ToListAsync());
        }

        // ・・・中略・・・
    }
}

(5) Search.cshtml

上のステップ (4) のアクションメソッド Search に対応する View です。jQuery ajax を使ってアクションメソッド SearchResult を呼び出し、応答として返された部分ビューの html ソースをドロップダウンリストの下の div 要素に表示するための JavaScript / jQuery のコードを含んでいます。

@{
    ViewData["Title"] = "Search";
}

<h1>Search by Customer and Employee</h1>

@*asp-action 属性を入れて Tag ヘルパーだと認識させないと
CSRF 用のトークンが発行されないので注意*@
<form asp-action="Search">
    Customer: 
    <select id="customerId" name="customerId" 
        asp-items="ViewBag.CustomerId">
        <option value="">ALL</option>
    </select>
    Employee: 
    <select id="employeeId" name="employeeId"  
        asp-items="ViewBag.EmployeeId">
        <option value="">ALL</option>
    </select>
    <input type="submit" value="Search" />
</form>

<div id="result"></div>

@section Scripts {
    <script type="text/javascript">
        //<![CDATA[

        var form = document.querySelector("form");

        // ボタンクリックで form が submit されるという
        // 動きになるので、form 要素の submit イベントに
        // リスナをアタッチして jQuert ajax で処理する
        $(form).on("submit", function (event) {
            // submit されては困るのでキャンセル
            event.preventDefault();

            // jQuery ajax を使って、部分ビューを応答とし
            // て返すアクションメソッド Orders/SeachResult
            // にフォームデータを POST 送信する
            $.ajax({
                type: "POST",
                url: "/Orders/SearchResult",
                
                // フォームデータを取得、送信 data に設定
                data: $(this).serialize(),

                dataType: "html",

                // 要求後、応答が返ってくるまで Loading... 
                // というメッセージを出す
                beforeSend: () => {
                    $("#result").empty();
                    $("#result").append(
                        '<span>Loading...</span>'
                    );
                }
            }).done(function (data) {
                $("#result").empty();
                $("#result").append(data);
            }).fail(function (jqXHR, status, error) {
                $('#result').text('Status: ' + status + 
                    ', Error: ' + error);
            });
        })

        //]]>
    </script>
}

(6) SearchResult.cshtml

アクションメソッド SearchResult からドロップダウンリストの選択結果に従って作成された Model を渡され、それを使って結果をブラウザに表示する html ソースを生成する部分ビューです。

@model IEnumerable<MvcNet7App.Models.Order>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.OrderDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.RequiredDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ShippedDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Freight)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ShipName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ShipAddress)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ShipCity)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ShipRegion)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ShipPostalCode)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ShipCountry)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Customer)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Employee)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ShipViaNavigation)
            </th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.OrderDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.RequiredDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShippedDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Freight)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShipName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShipAddress)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShipCity)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShipRegion)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShipPostalCode)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShipCountry)
            </td>
            <td>
                @Html.DisplayFor(modelItem => 
                    item.Customer!.CompanyName)
            </td>
            <td>
                @{
                    string name = (item.Employee != null) ? 
                                      item.Employee.FirstName + " " + 
                                      item.Employee.LastName : "";
                }
                @Html.DisplayFor(modelItem => name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => 
                    item.ShipViaNavigation!.CompanyName)
            </td>
        </tr>
}
    </tbody>
</table>

Tags: , , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar