共用方式為


MVC Web 應用程式的進階 Entity Framework 案例(10/10)

演講者:Tom Dykstra

Contoso University 範例 Web 應用程式示範如何使用 Entity Framework 5 Code First 和 Visual Studio 2012 建立 ASP.NET MVC 4 應用程式。 如需教學課程系列的資訊,請參閱本系列的第一個教學課程

注意

如果您遇到無法解決的問題, 請下載已完成的章節 ,並嘗試重現您的問題。 一般而言,您可以將程式代碼與已完成的程式代碼進行比較,以找出問題的解決方案。 如需一些常見的錯誤以及如何解決這些問題,請參閱 錯誤和因應措施。

在上一個教學課程中,您已實作存放庫和工作模式的單位。 本教學課程涵蓋下列主題:

  • 執行原始 SQL 查詢。
  • 執行無追蹤查詢。
  • 檢查傳送至資料庫的查詢。
  • 使用 Proxy 類別。
  • 停用變更的自動偵測。
  • 儲存變更時停用驗證。
  • 錯誤和解決方式

對於大多數主題,您將使用已建立的頁面。 若要使用原始 SQL 進行批次更新,您將建立一個新頁面來更新資料庫中所有課程的學分數:

顯示更新課程點數初始頁面的螢幕快照。數位 2 會在文字欄位中輸入。

若要使用無追蹤查詢,您會將新的驗證邏輯新增至 [部門編輯] 頁面:

顯示 Contoso 大學系編輯頁面的螢幕快照,其中包含重複的系統管理員錯誤訊息。

執行原始 SQL 查詢

Entity Framework Code First API 包含讓您能夠將 SQL 指令直接傳遞到資料庫的方法。 下列選項可供您選擇:

  • 針對傳回實體類型的查詢使用 DbSet.SqlQuery 方法。 傳回的 DbSet 物件必須是該物件期望的類型,並且資料庫上下文會自動追蹤它們,除非您關閉追蹤。 (請參閱關於 方法的 AsNoTracking 下一節。
  • 針對 Database.SqlQuery 傳回非實體類型的查詢使用 方法。 即使您使用這個方法來擷取實體類型,資料庫內容也不會追蹤傳回的資料。
  • Database.ExecuteSqlCommand 用於非查詢命令。

使用 Entity Framework 的優點之一,是它可避免將程式碼繫結至太接近儲存資料之特定方法的位置。 它可透過產生 SQL 查詢和命令來達成此目的,同時這也可讓您不必自行撰寫。 但在某些特殊情況下,您需要執行手動建立的特定 SQL 查詢,這些方法可讓您處理此類例外狀況。

如同在 Web 應用程式中執行 SQL 命令一樣,您必須採取一些預防措施,以保護您的網站免於遭受 SQL 插入式攻擊。 執行這項操作的方法之一是使用參數化查詢,以確定網頁所提交的字串無法解譯為 SQL 命令。 在本教學課程中,您會在將使用者輸入整合到查詢時,使用參數化查詢。

呼叫返回實體的查詢

假設您想要讓 GenericRepository 類別提供額外的篩選和排序彈性,而不需要使用其他方法建立衍生類別。 達成此目的的其中一種方法是新增接受 SQL 查詢的方法。 然後,您可以在控制器中指定任何類型的篩選或排序,例如 Where 相依於聯結或子查詢的子句。 在本節中,您將瞭解如何實作這類方法。

GetWithRawSql將下列程式代碼新增至 GenericRepository.cs,以建立 方法:

public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
{
    return dbSet.SqlQuery(query, parameters).ToList();
}

CourseController.cs 中,從 Details 方法呼叫新的 方法,如下列範例所示:

public ActionResult Details(int id)
{
    var query = "SELECT * FROM Course WHERE CourseID = @p0";
    return View(unitOfWork.CourseRepository.GetWithRawSql(query, id).Single());
}

在此情況下,您可以使用 GetByID 方法,但您使用 GetWithRawSql 方法來驗證 GetWithRawSQL 方法是否正常運作。

執行 [詳細數據] 頁面,確認選取查詢是否正常運作(選取 [課程 ] 索引標籤,然後 選取一個課程的詳細數據 )。

顯示 Contoso 大學詳細數據頁面的螢幕快照。

呼叫傳回其他類型物件的查詢

先前您已針對顯示每個註冊日期之學生數目的 About 頁面,建立學生統計資料方格。 HomeController.cs 中執行此操作的程式碼使用 LINQ:

var data = from student in db.Students
           group student by student.EnrollmentDate into dateGroup
           select new EnrollmentDateGroup()
           {
               EnrollmentDate = dateGroup.Key,
               StudentCount = dateGroup.Count()
           };

假設您想要編寫直接在 SQL 中檢索此資料的程式碼,而不是使用 LINQ。 若要這樣做,您必須執行會傳回實體物件以外的專案,這表示您需要使用 Database.SqlQuery 方法。

HomeController.cs 中,以下列程式代碼取代 方法中的 About LINQ 語句:

var query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
    + "FROM Person "
    + "WHERE EnrollmentDate IS NOT NULL "
    + "GROUP BY EnrollmentDate";
var data = db.Database.SqlQuery<EnrollmentDateGroup>(query);

運行“關於”頁面。 它會顯示與之前相同的資料。

顯示 Contoso University About 頁面的螢幕快照。

呼叫更新查詢

假設 Contoso 大學管理員希望能夠在資料庫中執行批次更改,例如更改每門課程的學分數。 如果該大學有大量的課程,擷取全部課程作為實體並個別進行變更的效率不佳。 在本節中,您將實作一個網頁,讓使用者指定一個因素,以變更所有課程的點數,而您將執行 SQL UPDATE 語句來進行變更。 網頁看起來將如下圖所示:

顯示更新課程點數初始頁面的螢幕快照。數位 2 會在文字欄位中輸入。

在上一個教學課程中,您已使用泛型存放庫來讀取和更新 Course 控制器中的 Course 實體。 針對此大量更新作業,您必須建立不在泛型存放庫中的新存放庫方法。 若要這樣做,您將建立衍生自 類別的GenericRepository專用CourseRepository類別。

DAL 資料夾中,建立 CourseRepository.cs ,並以下列程式代碼取代現有的程式代碼:

using System;
using ContosoUniversity.Models;

namespace ContosoUniversity.DAL
{
    public class CourseRepository : GenericRepository<Course>
    {
        public CourseRepository(SchoolContext context)
            : base(context)
        {
        }

        public int UpdateCourseCredits(int multiplier)
        {
            return context.Database.ExecuteSqlCommand("UPDATE Course SET Credits = Credits * {0}", multiplier);
        }

    }
}

UnitOfWork.cs 中,將存放 Course 庫類型從 GenericRepository<Course> 變更為 CourseRepository:

private CourseRepository courseRepository;
public CourseRepository CourseRepository
{
    get
    {

        if (this.courseRepository == null)
        {
            this.courseRepository = new CourseRepository(context);
        }
        return courseRepository;
    }
}

CourseController.cs中,新增 UpdateCourseCredits 方法:

public ActionResult UpdateCourseCredits(int? multiplier)
{
    if (multiplier != null)
    {
        ViewBag.RowsAffected = unitOfWork.CourseRepository.UpdateCourseCredits(multiplier.Value);
    }
    return View();
}

這個方法會用於 HttpGetHttpPostHttpGet UpdateCourseCredits當方法執行時,multiplier變數會是 null,檢視會顯示空白文本框和送出按鈕,如上圖所示。

按兩下 [ 更新] 按鈕並 HttpPost 執行 方法時, multiplier 會在文字框中輸入值。 然後,程式代碼會呼叫存放庫 UpdateCourseCredits 方法,這個方法會傳回受影響的數據列數目,並將該值儲存在物件中 ViewBag 。 當檢視收到 物件中 ViewBag 受影響的數據列數目時,它會顯示該數位,而不是文本框和送出按鈕,如下圖所示:

顯示 Contoso University Update Course 點數數據列受影響頁面的螢幕快照。

在 [更新課程點數] 頁面的 Views\Course 資料夾中建立檢視:

顯示 [新增檢視] 對話框的螢幕快照。更新課程點數會在 [檢視名稱] 文字欄位中輸入。

Views\Course\UpdateCourseCredits.cshtml 中,將範本程式碼替換為以下程式碼:

@model ContosoUniversity.Models.Course

@{
    ViewBag.Title = "UpdateCourseCredits";
}

<h2>Update Course Credits</h2>

@if (ViewBag.RowsAffected == null)
{
    using (Html.BeginForm())
    {
        <p>
            Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
        </p>
        <p>
            <input type="submit" value="Update" />
        </p>
    }
}
@if (ViewBag.RowsAffected != null)
{
    <p>
        Number of rows updated: @ViewBag.RowsAffected
    </p>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

藉由選取 [課程] 索引標籤,然後將 "/UpdateCourseCredits" 新增至瀏覽器位址列中的 URL 結尾 (例如:http://localhost:50205/Course/UpdateCourseCredits),以執行 UpdateCourseCredits 方法。 在文字方塊中輸入數目:

此螢幕快照顯示 [更新課程點數初始] 頁面,其中包含文字欄位中輸入的數位 2。

按一下更新。 您會看到受影響的資料列數目:

顯示 [更新課程點數] 頁面的螢幕快照,其中已更新數據列數目。

按一下 [回到清單],以查看課程與已修訂學分數的清單。

顯示 [課程索引] 頁面的螢幕快照。課程清單會顯示修訂后的點數。

如需原始 SQL 查詢的詳細資訊,請參閱 Entity Framework 小組部落格上的原始 SQL 查詢

不追蹤的查詢

當資料庫內容擷取資料庫數據列並建立代表它們的實體物件時,預設會追蹤記憶體中的實體是否與資料庫中的內容同步。 記憶體中的資料所扮演的角色是一個快取,並會在您更新實體時使用。 這個快取通常在 Web 應用程式當中是不需要的,因為內容執行個體通常壽命都很短 (每次要求都會建立一個新的並進行處置),並且通常讀取實體的內容都會在實體重新獲得利用前遭到處置。

您可以使用 方法來指定內容是否追蹤查詢 AsNoTracking 的實體物件。 您會想要進行這項操作的常見案例包括下列情況:

  • 查詢會擷取如此大量的數據,而關閉追蹤可能會明顯提升效能。
  • 您想要附加一個實體以便更新它,但您之前出於不同目的檢索了相同實體。 由於實體已由資料庫內容進行追蹤,您無法連結到您想要變更的實體。 防止這種情況發生的方法之一,就是搭配先前的查詢使用 AsNoTracking 選項。

在本節中,您將實作商業規則,以說明這些案例的第二個案例。 具體來說,您將強制執行商務規則,指出講師不能是多個部門的系統管理員。

DepartmentController.cs中,新增可從 和 Create 方法呼叫Edit的新方法,以確保沒有任何兩個部門具有相同的系統管理員:

private void ValidateOneAdministratorAssignmentPerInstructor(Department department)
{
    if (department.PersonID != null)
    {
        var duplicateDepartment = db.Departments
            .Include("Administrator")
            .Where(d => d.PersonID == department.PersonID)
            .FirstOrDefault();
        if (duplicateDepartment != null && duplicateDepartment.DepartmentID != department.DepartmentID)
        {
            var errorMessage = String.Format(
                "Instructor {0} {1} is already administrator of the {2} department.",
                duplicateDepartment.Administrator.FirstMidName,
                duplicateDepartment.Administrator.LastName,
                duplicateDepartment.Name);
            ModelState.AddModelError(string.Empty, errorMessage);
        }
    }
}

如果沒有驗證錯誤,請在 方法的 HttpPost Edit 區塊中try新增程序代碼,以呼叫這個新方法。 區塊 try 現在看起來像下列範例:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
   [Bind(Include = "DepartmentID, Name, Budget, StartDate, RowVersion, PersonID")]
    Department department)
{
   try
   {
      if (ModelState.IsValid)
      {
         ValidateOneAdministratorAssignmentPerInstructor(department);
      }

      if (ModelState.IsValid)
      {
         db.Entry(department).State = EntityState.Modified;
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DbUpdateConcurrencyException ex)
   {
      var entry = ex.Entries.Single();
      var clientValues = (Department)entry.Entity;

執行 [部門編輯] 頁面,並嘗試將部門的系統管理員變更為已是不同部門系統管理員的講師。 您會收到預期的錯誤訊息:

顯示 [部門編輯] 頁面的螢幕快照,其中包含重複的系統管理員錯誤訊息。

現在再次執行 [部門編輯] 頁面,這次變更 預算 金額。 當您按下 [ 儲存] 時,您會看到錯誤頁面:

顯示 [部門編輯] 頁面的螢幕快照,其中包含對象狀態管理員錯誤訊息。

例外狀況錯誤訊息為 「An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.這是因為下列事件順序所發生:

  • 方法 Edit 會呼叫 ValidateOneAdministratorAssignmentPerInstructor 方法,它會擷取所有以 Kim Abercrombie 為系統管理員的部門。 這會導致英文部門被閱讀。 因為這是正在編輯的部門,因此不會報告任何錯誤。 不過,由於這項讀取作業,從資料庫讀取的英文部門實體現在正由資料庫內容追蹤。
  • Edit方法會嘗試在MVC模型系結器所建立的英文部門實體上設定Modified旗標,但這會失敗,因為內容已經追蹤英文部門的實體。

此問題的其中一個解決方案是讓內容無法追蹤驗證查詢所擷取的記憶體內部部門實體。 這樣做沒有缺點,因為您不會更新此實體,也不會以記憶體中快取的方式再次讀取實體。

DepartmentController.cs 的 方法中 ValidateOneAdministratorAssignmentPerInstructor ,指定無追蹤,如下列所示:

var duplicateDepartment = db.Departments
   .Include("Administrator")
   .Where(d => d.PersonID == department.PersonID)
   .AsNoTracking()
   .FirstOrDefault();

重複您編輯 部門預算 金額的嘗試。 這次作業成功,網站會如預期般傳回 [部門索引] 頁面,其中顯示修訂的預算值。

檢查傳送至資料庫的查詢

有時能夠看到傳送至資料庫的實際 SQL 查詢很有幫助。 若要這樣做,您可以在調試程式中檢查查詢變數,或呼叫查詢的 ToString 方法。 要嘗試此操作,您將查看一個簡單的查詢,然後查看當您添加急切載入、篩選和排序等選項時會發生什麼情況。

Controllers/CourseController 中,以下列程序代碼取代 Index 方法:

public ViewResult Index()
{
    var courses = unitOfWork.CourseRepository.Get();
    return View(courses.ToList());
}

現在,在方法的 return query.ToList();return orderBy(query).ToList(); 語句Get上,於 GenericRepository.cs 中設定斷點。 以偵錯模式執行專案,然後選取 [課程索引] 頁面。 當程式碼到達斷點時,檢查 query 變數。 您會看到傳送到 SQL Server 的查詢。 這是一個簡單的 Select 語句:

{SELECT 
[Extent1].[CourseID] AS [CourseID], 
[Extent1].[Title] AS [Title], 
[Extent1].[Credits] AS [Credits], 
[Extent1].[DepartmentID] AS [DepartmentID]
FROM [Course] AS [Extent1]}

顯示範例 Web 應用程式 [一般存放庫] 索引標籤的螢幕快照。已選取查詢變數。

查詢可能太長,無法顯示在 Visual Studio 的偵錯視窗中。 若要查看整個查詢,您可以複製變數值,並將其貼到文字編輯器中:

顯示變數值的螢幕快照,其中已選取變數值時所顯示的下拉功能表。[複製值] 選項會反白顯示。

現在,您會將下拉式清單新增至 [課程索引] 頁面,讓使用者可以篩選特定部門。 您將按標題對課程進行排序,並指定 Department 導航屬性的預先載入。 在 CourseController.cs 中,將 Index 方法替換為以下程式碼:

public ActionResult Index(int? SelectedDepartment)
{
    var departments = unitOfWork.DepartmentRepository.Get(
        orderBy: q => q.OrderBy(d => d.Name));
    ViewBag.SelectedDepartment = new SelectList(departments, "DepartmentID", "Name", SelectedDepartment);

    int departmentID = SelectedDepartment.GetValueOrDefault(); 
    return View(unitOfWork.CourseRepository.Get(
        filter: d => !SelectedDepartment.HasValue || d.DepartmentID == departmentID,
        orderBy: q => q.OrderBy(d => d.CourseID),
        includeProperties: "Department"));
}

此方法接收 SelectedDepartment 參數中下拉清單的選定值。 如果未選擇任何內容,則該參數將為空。

包含所有部門的 SelectList 集合將傳遞到下拉清單的檢視。 傳遞給 SelectList 建構函數的參數指定值欄位名稱、文字欄位名稱和所選項目。

對於 Course 儲存庫的 Get 方法,程式碼指定了篩選表達式、排序順序以及 Department 導航屬性的預先載入。 如果下拉清單中未選擇任何內容 (即 SelectedDepartment 為空),則篩選表達式始終傳回 true

Views\Course\Index.cshtml 中的開始 table 標記之前,新增以下程式碼以建立下拉清單和提交按鈕:

@using (Html.BeginForm())
{
    <p>Select Department: @Html.DropDownList("SelectedDepartment","All")   
    <input type="submit" value="Filter" /></p>
}

在類別中 GenericRepository 仍設定斷點之後,請執行 [課程索引] 頁面。 繼續執行程式代碼叫用斷點的前兩次,讓頁面顯示在瀏覽器中。 從下拉式清單中選取部門,然後按兩下 [ 篩選]:

顯示 [課程索引] 頁面的螢幕快照,並已選取 [經濟部門]。

這次第一個斷點將用於部門查詢下拉式清單。 跳過該步驟並在下次程式碼到達斷點時查看 query 變數,以便查看 Course 查詢現在的樣子。 您會看到類似以下內容:

{SELECT 
[Extent1].[CourseID] AS [CourseID], 
[Extent1].[Title] AS [Title], 
[Extent1].[Credits] AS [Credits], 
[Extent1].[DepartmentID] AS [DepartmentID], 
[Extent2].[DepartmentID] AS [DepartmentID1], 
[Extent2].[Name] AS [Name], 
[Extent2].[Budget] AS [Budget], 
[Extent2].[StartDate] AS [StartDate], 
[Extent2].[PersonID] AS [PersonID], 
[Extent2].[Timestamp] AS [Timestamp]
FROM  [Course] AS [Extent1]
INNER JOIN [Department] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID]
WHERE (@p__linq__0 IS NULL) OR ([Extent1].[DepartmentID] = @p__linq__1)}

您可以看到 JOIN 查詢現在是一個隨 Department 資料一起載入 Course 資料的查詢,並且它包含一個 WHERE 子句。

使用 Proxy 類別

當實體框架建立實體執行個體時 (例如,當您執行查詢時),它通常會將它們建立為動態產生的衍生類型的執行個體,該衍生類型充當實體的代理。 此 Proxy 會覆寫實體的某些虛擬屬性,以在存取 屬性時自動插入執行動作的勾點。 例如,這個機制可用來支援延遲載入關聯性。

大多數時候您不需要了解代理的這種使用,但也有例外:

  • 在某些情況下,您可能希望阻止實體框架建立代理執行個體。 例如,串行化非 Proxy 實例比串行化 Proxy 實例更有效率。
  • 當您使用 new 運算子執行個體化實體類別時,您不會取得代理執行個體。 這意味著您無法獲得延遲加載和自動更改追蹤等功能。 這通常是沒問題的;您通常不需要延遲加載,因為您正在建立一個不在資料庫中的新實體,並且如果您明確將該實體標記為 Added。 不過,如果您需要延遲載入,而且需要變更追蹤,您可以使用 類別的 DbSet 方法來建立具有 Proxy Create 的新實體實例。
  • 您可能希望從代理類型取得實際的實體類型。 您可以使用 GetObjectType 類別的 ObjectContext 方法來取得 Proxy 類型實例的實際實體類型。

如需詳細資訊,請參閱 Entity Framework 小組部落格上的使用 Proxy

停用變更的自動偵測

Entity Framework 藉由比較實體的目前值與原始值,判斷實體如何變更 (以及因此需要將哪些更新傳送至資料庫)。 原始值會在查詢或附加實體時儲存。 會導致自動變更偵測的一些方法如下:

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries

如果您正在追蹤大量實體,並且在循環中多次呼叫這些方法之一,則可以透過使用 AutoDetectChangesEnabled 屬性暫時關閉自動變更偵測來獲得顯著的效能改進。 如需詳細資訊,請參閱 自動偵測變更

儲存變更時停用驗證

當您呼叫 SaveChanges 方法時,預設情況下,實體框架會在更新資料庫之前驗證所有已變更實體的所有屬性中的資料。 如果您已更新大量實體並且已驗證資料,則這項工作是不必要的,您可以透過暫時關閉驗證來縮短儲存變更的流程所需的時間。 您可以使用 ValidateOnSaveEnabled 屬性來執行此操作。 如需詳細資訊,請參閱驗證

摘要

關於在 ASP.NET MVC 應用程式中使用實體框架的系列教學課程到此結束。 您可以在 ASP.NET 數據存取內容對應中找到其他 Entity Framework 資源的連結。

如需如何在建置 Web 應用程式之後部署 Web 應用程式的詳細資訊,請參閱 MSDN Library 中的 ASP.NET 部署內容對應

如需與MVC相關的其他主題相關信息,例如驗證和授權,請參閱 MVC建議的資源

通知

  • Tom Dykstra 撰寫了本教學課程的原始版本,是 Microsoft Web 平臺和工具內容小組的高級程式設計作者。
  • Rick Anderson (twitter @RickAndMSFT) 共同撰寫本教學課程,並做了大部分的工作更新 EF 5 和 MVC 4。 Rick 是 Microsoft 的高級程式設計作家,專注於 Azure 和 MVC。
  • Rowan Miller 和 Entity Framework 小組的其他成員協助進行程式代碼檢閱,並協助偵錯許多在更新 EF 5 教學課程時出現的移轉問題。

VB

最初產生教學課程時,我們同時提供已完成下載專案的 C# 和 VB 版本。 透過此更新,我們會為每個章節提供 C# 可下載的專案,讓您更輕鬆地在系列中的任何位置開始使用,但由於時間限制和其他優先順序,我們並未針對 VB 執行此動作。 如果您使用這些教學課程建置 VB 專案,並願意與其他人共用該專案,請讓我們知道。

錯誤和因應措施

無法建立/磁碟區副本

錯誤訊息:

當該檔案已經存在時,無法建立/陰影複製 'DotNetOpenAuth.OpenId'。

解決方案:

等待幾秒鐘並重新整理頁面。

更新資料庫無法識別

錯誤訊息:

術語「更新資料庫」不被識別為 cmdlet、函數、指令碼檔案或可操作程序的名稱。 請檢查名稱的拼寫,或者如果包含路徑,請確認路徑是否正確,然後再試一次。(從 Update-Database PMC 中的 命令。

解決方案:

結束 Visual Studio。 重新開啟專案並重試。

驗證失敗

錯誤訊息:

一個或多個實體的驗證失敗。 有關更多詳細信息,請參閱“EntityValidationErrors”屬性。 (從 Update-Database PMC 中的 命令。

解決方案:

導致此問題的原因之一是 Seed 方法執行時出現驗證錯誤。 有關偵錯 Seed 方法的提示,請參閱播種和偵錯實體框架 (EF) DB

HTTP 500.19 錯誤

錯誤訊息:

HTTP 錯誤 500.19 - 內部伺服器錯誤
無法存取要求的頁面,因為頁面的相關組態數據無效。

解決方案:

出現此錯誤的一種方法是擁有解決方案的多個副本,每個副本都使用相同的連接埠號碼。 您通常可以結束 Visual Studio 的所有實例,然後重新啟動您正在處理的專案,以解決此問題。 如果這不起作用,請嘗試變更連接埠號碼。 右鍵點擊項目文件,然後按一下屬性。 選擇 Web 索引標籤,然後在專案 URL 文字方塊中變更連接埠號碼。

搜尋 SQL Server 執行個體時發生錯誤

錯誤訊息:

和 SQL Server 建立連線時,發生與網路相關或執行個體特定的錯誤。 找不到或無法存取伺服器。 檢查執行個體名稱是否正確以及 SQL Server 執行個體是否設定為允許遠端連接。 (提供者:SQL 網路介面,錯誤:26 - 搜尋指定的伺服器/執行個體時發生錯誤)

解決方案:

檢查 連接字串。 如果您手動刪除了資料庫,請變更建構字串中的資料庫名稱。