首頁 > 軟體

Entity Framework使用Code First模式管理儲存過程

2022-03-05 16:00:40

在EF中使用儲存過程和使用檢視是很相似的,一般會使用Database物件上的兩個方法:SqlQuery和ExecuteSqlCommand。為了從儲存過程中讀取很多資料行,我們只需要定義一個類,我們會將檢索到的所有資料行物質化到該類範例的集合中。比如,從下面的儲存過程讀取資料:

CREATE PROCEDURE [dbo].[SelectBooks]
@BookTypeName AS NVARCHAR(10)
AS
BEGIN
select B.Name,B.Author,B.PublicationDate,T.BookTypeName from Books as B
join BookTypes as T on B.BookTypeId=T.BookTypeId where T.BookTypeName=@BookTypeName
END

1、定義實體類

Book實體類定義如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeFirstProcedureApp.Model
{
    public class Book
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public string Author { get; set; }

        public DateTime PublicationDate { get; set; }

        public virtual BookType BookType { get; set; }
    }
}

BookType實體類定義如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeFirstProcedureApp.Model
{
    public class BookType
    {
        public BookType()
        {
            Books = new HashSet<Book>();
        }

        public int BookTypeId { get; set; }

        public string BookTypeName { get; set; }

        public virtual ICollection<Book> Books { get; set; }
    }
}

2、定義與儲存過程結果匹配的實體類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeFirstProcedureApp.Model
{
    public class BookFromProcedure
    {
        public string Name { get; set; }

        public string Author { get; set; }

        public DateTime PublicationDate { get; set; }

        public string BookTypeName { get; set; }
    }
}

注意:類的屬性名必須和儲存過程中定義的列名一致。

3、建立種子初始化器類

using CodeFirstProcedureApp.Model;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeFirstProcedureApp.EF
{
    public class Initializer : DropCreateDatabaseIfModelChanges<EFDbContext>
    {
        protected override void Seed(EFDbContext context)
        {
            // 建立初始化資料
            BookType bookType = new BookType()
            {
                BookTypeName = "文學小說",
                Books = new List<Book>
                {
                  new Book(){Name="人間失格",Author="太宰治",PublicationDate=DateTime.Parse("2015-08-01")},
                  new Book(){Name="解憂雜貨店",Author="東野圭吾",PublicationDate=DateTime.Parse("2014-05-01")},
                  new Book(){Name="追風箏的人",Author="卡勒德胡賽尼",PublicationDate=DateTime.Parse("2006-08-01")},
                  new Book(){Name="百年孤獨",Author="加西亞馬爾克斯",PublicationDate=DateTime.Parse("2011-06-01")},
                  new Book(){Name="霍亂時期的愛情",Author="加西亞馬爾克斯",PublicationDate=DateTime.Parse("2015-06-01")}
                }
            };

            BookType bookType2 = new BookType()
            {
                BookTypeName = "科學",
                Books = new List<Book>
                {
                   new Book(){Name="人類簡史",Author="尤瓦爾赫拉利",PublicationDate=DateTime.Parse("2017-01-01")}
                }
            };

            context.BookTypes.Add(bookType);
            context.BookTypes.Add(bookType2);
            base.Seed(context);
        }
    }
}

4、定義資料上下文類

using CodeFirstProcedureApp.Model;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeFirstProcedureApp.EF
{
    public class EFDbContext :DbContext
    {
        public EFDbContext()
            : base("name=AppConnection")
        {
            Database.SetInitializer(new Initializer());
        }

        // 新增到資料上下文中
        public DbSet<Book> Books { get; set; }

        public DbSet<BookType> BookTypes { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // 設定表名和主鍵
            modelBuilder.Entity<Book>().ToTable("Books").HasKey(p => p.Id);
            modelBuilder.Entity<BookType>().ToTable("BookTypes").HasKey(p => p.BookTypeId);
            // 設定實體關係
            //  BookType和 Books 一對多關係 外來鍵:BookTypeId
            modelBuilder.Entity<BookType>().HasMany(p => p.Books).WithRequired(t => t.BookType)
                .Map(m =>
                {
                    m.MapKey("BookTypeId");
                });
            base.OnModelCreating(modelBuilder);
        }
    }
}

5、執行程式

使用SQL語句建立儲存過程:

var createSql = @"CREATE PROCEDURE [dbo].[SelectBooks]
                                    @BookTypeName AS NVARCHAR(10)
                                    AS
                                    BEGIN
                                    select B.Name,B.Author,B.PublicationDate,T.BookTypeName from Books as B
                                    join BookTypes as T on B.BookTypeId=T.BookTypeId where T.BookTypeName=@BookTypeName
                                    END";
context.Database.ExecuteSqlCommand(createSql);

檢視資料庫:

呼叫儲存過程查詢資料

注意:在使用儲存過程前,先要在儲存過程中執行該儲存過程或者使用上面的程式生成儲存過程。

var sql = "SelectBooks {0}";
var books = context.Database.SqlQuery<BookFromProcedure>(sql, "文學小說");
books.ToList().ForEach(p =>
{
      Console.WriteLine("BookName:" + p.Name + "     Author:" + p.Author + "    BookTypeName:" + p.BookTypeName + "     PublicationDate:" + p.PublicationDate);
});

在上面的程式碼中,我們制定了使用哪個類讀取查詢的結果,建立SQL語句時,也為儲存過程的引數提供了一個格式化預留位置,呼叫SqlQuery時為那個引數提供了一個值。假如要提供多個引數的話,多個格式化預留位置必須要用逗號分隔,還要給SqlQuery提供值的陣列。我們也可以使用表值函數代替儲存過程。儲存過程執行結果如下:

6、執行無返回值的儲存過程

另一個用例就是假如儲存過程沒有任何的返回值,只是對資料庫中的一張表或者多張表執行了一條命令的情況。一個儲存過程做了多少事情不重要,重要的是它不返回任何資料。例如:下面的儲存過程只是更新了一些資料:

CREATE PROCEDURE UpdateBooks
@name AS NVARCHAR(60),
@id as int
AS 
BEGIN
UPDATE Books SET Name=@name
WHERE Id=@id
END

 先在資料庫中執行該儲存過程,然後要呼叫該儲存過程,我們使用ExecuteSqlCommand()方法。該方法會返回儲存過程或者其它任何SQL語句受影響的行數。如果對這個返回值不感興趣,可以忽略返回值。

var sql = "UpdateBooks @name,@id";
SqlParameter[] para = new SqlParameter[]
{
       new SqlParameter("@id",1d),
       new SqlParameter("@name","人間失敗"),
};
var book = context.Books.Where(p => p.Id == 1);
Console.WriteLine("執行儲存過程前的資料為:");
foreach (var item in book)
{
       Console.WriteLine(item.Name + "t" + item.Author + "t" + item.PublicationDate);
}
var rowsAffected = context.Database.ExecuteSqlCommand(sql, para);
Console.WriteLine("影響的行數為{0}條", rowsAffected);
Console.WriteLine("執行儲存過程之後的資料為:");
var books = context.Books.Where(p => p.Id == 1);
foreach (var item in books)
{
        Console.WriteLine(item.Name + "t" + item.Author + "t" + item.PublicationDate);
}

上面的程式碼中為儲存過程提供了兩個引數:一個是Name,一個是Id。這裡需要注意的是:我們必須嚴格按照它們在儲存過程中定義的順序依次傳入相應的值,它們會以引數陣列傳入ExecuteSqlCommand。執行結果如下:

很大程度上,EF降低了儲存過程的需要。然而,仍舊有很多原因要使用它們。這些原因包括安全標準、遺留資料庫或者效率問題。比如,如果需要在單個操作中更新幾千條資料,然後在通過EF檢索出來;如果每次都更新一行,然後在儲存那些範例,效率是很低的。最後,即使使用了SqlQuery()方法呼叫了儲存過程,也可以更新資料。

注意:開發者可以執行任意的SQL語句,只需要將上面SqlQuery或ExecuteSqlCommand方法中的儲存過程名稱改為要執行的SQL語句就可以了。 

範例程式碼下載地址:點此下載

到此這篇關於Entity Framework使用Code First模式管理儲存過程的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com