65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Repository Design Pattern、Entity Frameworks(Code First)、Unit of Work Testing、Web API、ASP.NET MVC 5 和 Bootstrap 开发示例项目

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (16投票s)

2016年10月24日

CPOL

12分钟阅读

viewsIcon

47280

本项目使用 Entity Framework 6.0 + Code First Approach + Web API 2 + Repository Pattern + ASP.NET MVC 5 + SQL Server 2012 + Unit of Work Testing + Bootstrap 组合开发。

引言

本文将帮助您了解如何在 ASP.NET MVC 应用程序中使用 Repository Pattern、Entity Framework、Web API、SQL Server 2012 和 Unit of Work Testing 来设计项目。我们将为 Book 实体和 Author 实体开发一个示例记账解决方案项目,我们可以在其中执行**创建**、**读取**、**更新**和**删除**操作。

完成本项目后,它将包含五个(5)个相互依赖的子项目。

本项目成果总结

  • 这将帮助您以多层方法开发/设计项目
  • 如何在 Code First Approach 中为类的属性定义主键
  • 如何在 Code First Approach 中定义具有 decimal 数据类型的属性并插入值
  • 如何在 Code First Approach 中为类的属性定义外键
  • 如何在 Code First Approach 中为类的属性定义唯一键
  • 如何为项目安装 Entity Framework
  • 项目级别的 OOP 示例 - 方法重载
  • 使用 Model 填充自定义下拉列表并绑定
  • 为 Web API 方法创建自定义*URI*。
  • 如何将 decimal 值作为 Web API 方法的响应返回。
  • 如何配置可以发送多个参数的方法重载 Web API 方法。
  • 如何从 MVC 调用 WebAPI Controller 端点。
  • 如何在控制器中以不同方式绑定下拉列表值
  • 如何以非强类型和强类型方式在 Create/Edit 视图中绑定下拉列表

架构

该图像显示了项目完成后解决方案资源管理器中的项目列表。


                                                  Architecture

 

1. LibraryApp.Core - 实体和接口

***类库*** - 在本项目中,我们包含

  • 实体
  • 接口

    基本上,我们有两个实体:**Book** 和 **Author**。我们可以使用此项目来存储域实体和数据库操作的接口。根据项目需求,我们可以在此处放置任何即将出现的实体或接口。
2. LibraryApp.Infrastructure - 业务逻辑和数据库配置

***类库*** - 在本项目中,我们定义

  • 数据库配置

    我们可以引用我们的***连接字符串***用于数据库连接和其他数据库配置。
     
  • 数据库操作

    Repositories - 在这里,我们定义了 Core 项目中处理的信息/实体/对象的数据库操作。对于每个实体,我们可以创建一个单独的类文件作为存储库,或者我们可以将所有操作包含在一个存储库类中。
     


  • 我们将使用哪些库来执行数据库操作。例如**Entity Framework**库或**Dapper**(微型 ORM)或其他任何技术。
3. LibraryApp.UnitTest - Unit of Work 测试

***单元测试*** - 在本项目中,我们观察

对基础设施项目存储库的单元测试。我们可以验证我们在基础设施项目中编写的所有代码是否正常运行,如果它们按预期工作,我们可以确保基础设施项目在 Web Service 项目中将正常运行。

4. LibraryApp.WebAPI - Web 服务

***Web API*** - 在本项目中,我们发布

通过在此项目中实例化和引用我们之前的基础设施项目,并进行一些配置,我们可以将基础设施项目中包含的所有操作作为 Web 服务发布。在这里,我们以**HTTP**方法的形式发布它们。我们将这些 HTTP 方法标识为**APIController**方法。
         
我们将此项目用作**服务器**和**客户端**之间的桥梁

服务器端
     LibraryApp.Core
     LibraryApp.Infrastructure

桥梁
      LibraryApp.WebAPI

***客户端***
       LibraryApp.MVC

一旦我们发布了这些数据库操作,我们就可以使用它们来创建前端,作为 Web(**ASP.NET MVC**或**WebForm**)应用程序、移动应用程序或桌面应用程序。

5. LibraryApp.MVC - 前端

***ASP.NET MVC 5*** - 在本项目中,我们执行

如上所述,在此项目中,我们正在创建 Web 应用程序作为前端客户端应用程序。在这里,我们使用**ASP.NET MVC 5**和**Bootstrap**来完成我们的任务。

此外,下图解释了上述每个单一项目如何相互交互

 

           

操作

  1. 包含作者列表的书籍 - 主仪表板
  2. 作者列表仪表板
  3. 创建一本书
  4. 编辑一本书
  5. 删除一本书
  6. 创建一位作者
  7. 编辑一位作者
  8. 删除一位作者


此项目中的操作在此处描绘

主仪表板 - 包含作者的书籍列表


作者仪表板 - 作者列表


创建一本书


编辑一本书


创建一位作者


编辑一位作者

 

数据库

我们使用 Entity Framework **Code First** Approach 开发此项目,项目完成后,数据库结构将如下所示。

规格

在这些表中,**ID**s 定义为**主键**约束和**INT**数据类型,在这里,您将了解到*如何在 Code First Approach 中为类的属性定义主键*。

所有名称和标题都定义为**NVARCHAR**字段,然后可以插入任何语言和字母数字字符。我在这里存储了一些**僧伽罗语**的数据。

实现

从现在开始,我将逐步解释如何在代码级别实现上述项目。


LibraryApp.Core 的步骤

1. 创建一个空的 C# 类库项目,命名为“ProjectName.Core”。
2. 右键单击该项目解决方案,插入两个(2)个新文件夹,命名为“Entities”和“Interfaces”。
3. 现在,在 Entities 文件夹中创建类文件,用于我们想要包含的实体/对象。

Book.cs

我们将**Book**表的**Price**在此处作为**decimal**值,然后您将了解到*如何在 Code First Approach 中定义具有 decimal 数据类型的属性*。

**Book**表的**Author_Id**将作为**外键**来存储作者 ID。请参阅这篇文章以了解更多关于*如何在 Code First Approach 中为类的属性定义外键*的信息。

   public class Book
   {
        public Book()
        {

        }

        [Key]
        public int Book_Id { get; set; }                 // Primary Key Constraint

        [Required]
        public string Book_Title { get; set; }

        [DataType("decimal(16 ,3")]
        public decimal Price { get; set; }

        public string Edition { get; set; }

        [ForeignKey("Author")] 
        public int Author_Id { get; set; }               // Foriegn Key Constraint

        public Author Author { get; set; }
    }

Author.cs

**Author**表的**Last_Name**列具有**唯一键**约束。请参阅这篇文章以了解更多关于*如何在 Code First Approach 中为类的属性定义唯一键*的信息。

    public class Author
    {
        public Author()
        {

        }

        [Key]
        public int Auth_Id { get; set; }                 // Primary Key Constraint

        [StringLength(50)]
        public string First_Name { get; set; }  
        
        [Index("IX_FirstAndSecond", 1, IsUnique = true)] 
        [StringLength(50)]
        public string Last_Name { get; set; }            // Unique Key Constraint

        public string Biography { get; set; }            

        [StringLength(1)]
        [Column(TypeName = "char")]  
        public string IsEmployed { get; set; }

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

正如您在操作图像中已经看到的,我们可能需要一些下拉列表,因此为了向这些下拉列表绑定值,我们在那里使用另一个实体,称为*Basic*。

Basic.cs

    public class Basic
    {
        public int ID { get; set; }   
     
        public string NAME { get; set; }
    }

在我们的仪表板视图中,除了主要实体外,我们还有一些不同的属性,因此,我们在那里使用另一个实体,称为**BookWithAuthor**。

BookWithAuthor.cs

    public class BookWithAuthor
    {
        public int BookWithAuthor_Id { get; set; }  

        public string BookWithAuthor_Title { get; set; }
 
        public string BookWithAuthor_AuthorName { get; set; }

        public decimal Price { get; set; }

        public string Edition { get; set; }
    }

现在我们开始在 Core 项目的 Interfaces 文件夹中创建接口。

IBookRepository.cs

    public interface IBookRepository
    {
        void AddBook(Book book);

        void EditBook(Book book);

        void RemoveBook(int Book_Id);

        IEnumerable<Book> GetBooks();

        Book FindBookById(int Book_Id); 
    }

IAuthorRepository.cs

    public interface IAuthorRepository
    {
        void AddAuthor(Author author);

        void EditAuthor(Author author);

        void RemoveAuthor(int Id);

        IEnumerable<Author> GetAuthors(); 

        Author FindAuthorById(int Id);
    }

IBasic.cs

    public interface IBasic
    {
        IEnumerable<Basic> GetAuthorsIdName();         // Authors Drop Down

        IEnumerable<Basic> GetEditionIdName();         // Book Edition Drop Down

        IEnumerable<Basic> GetEmplyeeStatusIdName();   // Employee Status Drop Down
    }

IBooksWithAuthorsRepository.cs

    public interface IBooksWithAuthorsRepository
    {
        IEnumerable<BookWithAuthor> GetBooksWithAuthors();  //   Main Dashboard Authors With Books

        BookWithAuthor FindBooksWithAuthorsById(int BookWithAuthor_Id); // Get Book Details
    }

完成此基础层后,解决方案将如下所示。

                                        


LibraryApp.Infrastructure 的步骤

1. 创建一个空的 C# 类库项目,命名为“ProjectName.Infrastructure”。
2. 右键单击该项目解决方案或使用程序包管理器控制台,安装 Entity Framework。请参阅这篇文章以了解更多关于*如何为项目安装 Entity Framework*的信息。
3. 在“References”中给予*System.Data.Entity*引用。
4. 在“References”中给予*LibraryApp.Core*引用。
5. 在*App.Config*中定义连接字符串。

App.Config

安装 Entity Framework 后,我们可以在此项目中看到创建的*App.Config*文件。在*</configSections>*标签之后,我们插入此连接字符串。请记住:我们在其余三个(3)个项目中都使用相同的连接字符串。

<connectionStrings>
  <add name="libraryappconnectionstring" connectionString="Data Source=.\sqlexpress;Initial Catalog=LibraryAppDB;Integrated Security=True;MultipleActiveResultSets=true" providerName="System.Data.SqlClient" />
</connectionStrings> 

6. 现在创建一个 Context 类文件来定义数据库配置。

LibraryContext.cs

我们在此处引用 web.config 中的连接字符串,以及需要在数据库中生成的表。

    public class LibraryContext : DbContext
    {
        public LibraryContext() : base("name=libraryappconnectionstring") // Connection String
        {

        }
        // Tables goin to create in Database
        public DbSet<Book> Books { get; set; }   

        public DbSet<Author> Authors { get; set; }

    }

7. 在存储库类中实现接口中声明的方法。

正如我之前提到的,您可以为每个实体创建单独的存储库,或者像这样将所有操作添加到一个存储库中。

LibraryRepository.cs

这是整个项目中最重要的类,因为我们在此处进行**业务逻辑**操作。简单地说,显示哪些详细信息以及将执行哪些/如何执行操作。

在此存储库类中,我们继承了 Core 项目中包含的所有接口,然后我们可以实现我们在接口中声明的方法。

在这里,我使用 Entity Framework 的**Linq to SQL**方法来执行我们的操作。

 public class LibraryRepository : IBookRepository, 
                                  IAuthorRepository,
                                  IBooksWithAuthorsRepository,
                                  IBasic
    {

     LibraryContext context = new LibraryContext();

     #region //-----------Books with Authors

     //  Books List with Authors First Name + Last Name
     public IEnumerable<BookWithAuthor> GetBooksWithAuthors()
     {
       var bookswithauthors = (
                              from book in context.Books
                              join author in context.Authors
                              on book.Author_Id equals author.Auth_Id
                              select new BookWithAuthor
                              {
                               BookWithAuthor_Id = book.Book_Id,
                               BookWithAuthor_Title = book.Book_Title,
                               BookWithAuthor_AuthorName = author.First_Name + " " + author.Last_Name,
                               Edition = book.Edition,
                               Price = book.Price
                              }).ToList();

        return bookswithauthors;
     }

     // Find book details along with author name using book id
     public BookWithAuthor FindBooksWithAuthorsById(int BookWithAuthor_Id)
     {
        var bookwithauthor = (
                              from book in context.Books
                              join author in context.Authors
                              on book.Author_Id equals author.Auth_Id
                              where book.Book_Id == BookWithAuthor_Id
                              select new BookWithAuthor
                              {
                               BookWithAuthor_Id = book.Book_Id,
                               BookWithAuthor_Title = book.Book_Title,
                               BookWithAuthor_AuthorName = author.First_Name + " " + author.Last_Name,
                               Edition = book.Edition,
                               Price = book.Price
                              }).FirstOrDefault();


         return bookwithauthor;
      }

      #endregion
        
      #region //-----------Books

      // Add a Book
      public void AddBook(Book book)
      {
         context.Books.Add(book);
         context.SaveChanges();
      }

      // Edit a Book
      public void EditBook(Book book)
      {
         context.Entry(book).State = System.Data.Entity.EntityState.Modified;
         context.SaveChanges();
      }
      
      //Find book by id
      public Book FindBookById(int Book_Id)
      {
         var c = (from r in context.Books where r.Book_Id == Book_Id select r).FirstOrDefault();
         return c;
      }

      // Get books
      public IEnumerable<Book> GetBooks()
      {
         return context.Books;
      }

      // Delete a book
      public void RemoveBook(int Book_Id)
      {
         Book book = context.Books.Find(Book_Id);
         context.Books.Remove(book);
         context.SaveChanges();
      }

      //Get book price by book Id
      //OOP - Method Overloading 
      public decimal findBookPrice(int? book_id)
      {
        var bookprice = (
                          from r in context.Books
                          where r.Book_Id == book_id
                          select r.Price
                         ).FirstOrDefault();

        return bookprice;
      }  

      //Get book price by book Id and book name       
      //OOP - Method Overloading 
      public decimal findBookPrice(int? book_id, string bookname)
      {
         var bookprice = (
                          from book in context.Books
                          where book.Book_Id == book_id | book.Book_Title == bookname
                          select book.Price
                         ).FirstOrDefault();

         return bookprice;
      }

      #endregion

      #region //-----------Authors

      // Add an author
      public void AddAuthor(Author author)
      {
         context.Authors.Add(author);
         context.SaveChanges();
      }

      // Edit an author
      public void EditAuthor(Author author)
      {
         context.Entry(author).State = System.Data.Entity.EntityState.Modified;
         context.SaveChanges();
      }

      // Find an author by id
      public Author FindAuthorById(int Author_Id)
      {
         var c = (from r in context.Authors where r.Auth_Id == Author_Id select r).FirstOrDefault();
         return c;
      }

      // Get author list
      public IEnumerable<Author> GetAuthors()
      {
         return context.Authors;
      }

      // Delete an author
      public void RemoveAuthor(int Author_Id)
      {
         Author author = context.Authors.Find(Author_Id);
         context.Authors.Remove(author);
         context.SaveChanges();
      }

      #endregion

      #region //-----------DropDowns

      // Populate author id and name dropdown
      public IEnumerable<Basic> GetAuthorsIdName()
      {
         var authoridname = (
                             from author in context.Authors
                             select new Basic
                             {
                               ID = author.Auth_Id,
                               NAME = author.First_Name + " " + author.Last_Name
                             }).ToList();

         return authoridname;
      }

      // Populate book edition id,name dropdown and binding using Model
      public IEnumerable<Basic> GetEditionIdName()
      {
        return new List<Basic>(new[]
             {
                new Basic()
                {
                  ID = 1,
                  NAME = "1st Edition"
                },
                new Basic()
                {
                    ID = 2,
                    NAME = "2nd Edition"
                }
                // Add more Editions
            });
        }

      // Populate book employee status,id dropdown and binding using Model
      public IEnumerable<Basic> GetEmplyeeStatusIdName()
      {
         return new List<Basic>(new[]
             {
                new Basic()
                {
                    ID = 1,
                    NAME = "Y"
                },
                new Basic()
                {
                    ID = 2,
                    NAME = "N"
                }
             });
        }

        #endregion

    }

8. 通过 Code First Approach 插入示例数据来初始化数据库。

LibraryDbInitalize.cs

 public class LibraryDbInitalize : DropCreateDatabaseIfModelChanges<LibraryContext>
    {
        protected override void Seed(LibraryContext context)
        {
            //Adding initial Author data
            context.Authors.Add
            (
                  new Author
                  {
                      Auth_Id = 1,
                      First_Name = "Author FirstName 001",
                      Last_Name = "Author LastName 001",
                      Biography = "Author 1st Bio",
                      IsEmployed = "Y"
                  }
              );

            //Adding initial Book data
            context.Books.Add
            (
                  new Book
                  {
                      Book_Id = 1,
                      Book_Title = "Book Title 001",
                      Edition = "1st Edition",
                      Price = 40.0M,   //this is how insert values to decimal field in code level
                      Author_Id = 1
                  }
              );

            context.SaveChanges();

            base.Seed(context);

        }
    }

完成此 LibraryApp.Infrastructure 项目后,结构将如下所示。

                                          

 


现在我们将测试在存储库类中实现的方法。

LibraryApp.UnitTest 的步骤

1. 创建一个空的 C# 单元测试项目,命名为“ProjectName.UnitTest”。
2. 像上述项目一样,在 app.config 文件中添加连接字符串。
3. 在“References”中给予*LibraryApp.Core*和*LibraryApp.Infrastructure*引用。
4. 右键单击该项目解决方案,插入一个类并命名为“ProjectNameRepositoryTest”。

LibraryRepositoryTest.cs

    public class LibraryRepositoryTest
    {
        LibraryRepository Repo;
         
        // initialize the test class
        [TestInitialize]
        public void TestSetup()
        {
            LibraryDbInitalize db = new LibraryDbInitalize();
            System.Data.Entity.Database.SetInitializer(db);
            Repo = new LibraryRepository();
        }

        #region Author  

        // check valid number of author/s(1) existing in current DB
        [TestMethod]
        public void IsRepositoryInitalizeWithValidNumberOfData_Author()
        {
            var result = Repo.GetAuthors();
            Assert.IsNotNull(result);
            var numberOfRecords = result.ToList().Count;
            Assert.AreEqual(1, numberOfRecords);
        }
        
        // check add author method working and total number of authors(2) correct
        [TestMethod]
        public void IsRepositoryAddsAuthor()
        {
            Author productToInsert = new Author
            {
                Auth_Id = 4,
                First_Name = "Author FirstName 004",
                Last_Name = "Author LastName 004",
                Biography = "Author 4th Bio"

            };
            Repo.AddAuthor(productToInsert);
            // If Product inserts successfully, 
            //number of records will increase to 4 
            var result = Repo.GetAuthors();
            var numberOfRecords = result.ToList().Count;
            Assert.AreEqual(2, numberOfRecords);
        }
        #endregion

        #region Book 

        // check valid number of book/s(1) existing in current DB
        [TestMethod]
        public void IsRepositoryInitalizeWithValidNumberOfData_Book()
        {
            var result = Repo.GetBooksWithAuthors();
            Assert.IsNotNull(result);
            var numberOfRecords = result.ToList().Count;
            Assert.AreEqual(1, numberOfRecords);
        }

        // check add book method working and total number of books(2) correct
        [TestMethod]
        public void IsRepositoryAddsBook()
        {
            Book productToInsert = new Book
            {
                Book_Id = 4,
                Book_Title = "Book Title 004",
                Price = 9.00M,
                Edition = "4th Edition",
                Author_Id = 1
                

            };
            Repo.AddBook(productToInsert);
            // If Product inserts successfully, 
            //number of records will increase to 4 
            var result = Repo.GetBooks();
            var numberOfRecords = result.ToList().Count;
            Assert.AreEqual(2, numberOfRecords);
        }
        #endregion

        #region Books with Author 

        // check valid number of book and author/s(1) existing in current DB
        [TestMethod]
        public void IsRepositoryInitalizeWithValidNumberOfData_BooksWithAuthor()
        {
            var result = Repo.GetBooksWithAuthors();
            Assert.IsNotNull(result);
            var numberOfRecords = result.ToList().Count;
            Assert.AreEqual(1, numberOfRecords);
        }

        #endregion   
  
        #region DropDowns

         // check valid number of author/s(1) listing in dropdown
        [TestMethod]
        public void IsRepositoryInitalizeWithValidNumberOfData_AuthorDropDown()
        {
            var result = Repo.GetAuthorsIdName();
            Assert.IsNotNull(result);
            var numberOfRecords = result.ToList().Count;
            Assert.AreEqual(1, numberOfRecords);
        }
        #endregion
    }

我在这里只写了几个测试用例,像上面一样,在进入客户端之前,您可以为所有需要测试的方法编写测试用例。

5. 然后通过转到*Test > Run > All Tests*来运行测试。

如果所有测试都通过,您将在 Test Explorer 中看到类似以下的屏幕。


 

完成此 LibraryApp.UnitTest 项目后,结构将如下所示。

                                              

 


现在,让我们在我们的*LibraryApp.WebAPI*项目中将测试过的方法发布为 HTTP 方法。

LibraryApp.WebAPI 的步骤

1. 创建一个空的 C# Web API 项目,命名为“ProjectName.WebAPI”。
2. 右键单击该项目解决方案或使用程序包管理器控制台安装 Entity Framework。
3. 像上述项目一样,在 web.config 文件中添加连接字符串。
4. 给予以下库引用。
         System.Data.Entity
          LibraryApp.Core
         LibraryApp.Infrastructure
 
5. 像上述项目一样,在 web.config 文件中添加连接字符串。
6. 右键单击此项目的控制器文件夹,插入控制器。

BooksController.cs

选择*“使用 Entity Framework 的 Web API 2 Controller”*。

暂时选择数据上下文类,我们选择在 LibraryApp.Infrastructure 中创建的“LibraryContext”。稍后,我们将对此进行更改。

选择模型类,这是我们在 LibraryApp.Core 中创建的。

作为 Controller 名称,您可以为此输入任何名称。

完成上述步骤后,Apicontroller 会自动生成如下所示的方法,它是用**强耦合**架构构建的。因为它正在实例化 LibraryContext 类。

BooksController.cs

public class BooksController : ApiController
    {
        private LibraryContext db = new LibraryContext();

        // GET: api/Books

        public IQueryable<Book> GetBooks()
        {
            return db.Books;
        }

        // GET: api/Books/5
        [ResponseType(typeof(Book))]
        public IHttpActionResult GetBook(int id)
        {
            Book book = db.Books.Find(id);
            if (book == null)
            {
                return NotFound();
            }

            return Ok(book);
        }

        // PUT: api/Books/5
        [ResponseType(typeof(void))]
        public IHttpActionResult PutBook(int id, Book book)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != book.Book_Id)
            {
                return BadRequest();
            }

            db.Entry(book).State = EntityState.Modified;

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!BookExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/Books
        [ResponseType(typeof(Book))]
        public IHttpActionResult PostBook(Book book)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            db.Books.Add(book);
            db.SaveChanges();

            return CreatedAtRoute("DefaultApi", new { id = book.Book_Id }, book);
        }

        // DELETE: api/Books/5
        [ResponseType(typeof(Book))]
        public IHttpActionResult DeleteBook(int id)
        {
            Book book = db.Books.Find(id);
            if (book == null)
            {
                return NotFound();
            }

            db.Books.Remove(book);
            db.SaveChanges();

            return Ok(book);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }

        private bool BookExists(int id)
        {
            return db.Books.Count(e => e.Book_Id == id) > 0;
        }
    }

由于我们使用了 Visual Studio 的脚手架,它正在创建 **LibraryContext** 类的对象来执行我们的操作。但是我们不希望在 Web API 项目中有 LibraryContext 类。LibraryContext 继承自 **DbContext**,它属于 System.Data.Entity,是 Entity Framework 的一个组件。

如果我们在 MVC(客户端)项目中包含任何 Entity Framework 的内容,那么我们将直接将 UI 和数据库技术紧密耦合。

但在我们的例子中,如果我们使用 LibraryContext,那么我们将在 Web API 项目中使用 Entity Framework 的组件,这是一个部分紧密耦合的场景。原因是我们将 Web API 项目与 MVC 前端项目连接。

我们需要使我们的项目完全摆脱紧密耦合。因此,为了执行我们的操作,我们在 Infrastructure 项目中已经有了 Repository 类,我们可以使用它。

我们需要将上述强耦合架构更改为**松耦合**架构,因此我们如下更改上述代码。

BooksController.cs

    public class BooksController : ApiController
    {
        private LibraryRepository db = new LibraryRepository();

        //// GET: api/Books
        // we can fetch data as XML file via https://:13793/api/Books URL
        public IEnumerable<Book> GetBooks()
        {
            // Calling the Reopsitory project GetBooks method
            return db.GetBooks();             
        }

        // GET: api/Books/5
        // we can fetch data as XML file via https://:13793/api/Books/{id} URL 
        [ResponseType(typeof(Book))]
        public IHttpActionResult GetBook(int id)
        {
            // Calling the Reopsitory project FindBookById method
            Book book = db.FindBookById(id); 
            if (book == null)
            {
                return NotFound();
            }

            return Ok(book);
        }


        //// PUT: api/Books/5
        // we can perform edit book method
        [ResponseType(typeof(void))]        
        public IHttpActionResult PutBook(int id, Book book)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != book.Book_Id)
            {
                return BadRequest();
            }
            // Calling the Reopsitory project EditBook method
            db.EditBook(book);   

            return StatusCode(HttpStatusCode.NoContent);
        }

        //// POST: api/Books/
        // we can perform add book method
        [ResponseType(typeof(Book))]
        public IHttpActionResult PostBook(Book book)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            // Calling the Reopsitory project AddBook method
            db.AddBook(book); 

            return CreatedAtRoute("DefaultApi", new { id = book.Book_Id }, book);
        }

        //// DELETE: api/Books//5
        // we can perform delete book method
        [ResponseType(typeof(Book))]
        public IHttpActionResult DeleteBook(int id)
        {
            Book book = db.FindBookById(id);
            if (book == null)
            {
                return NotFound();
            }
            // Calling the Reopsitory project RemoveBook method
            db.RemoveBook(id);  

            return Ok(book);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
               // db.Dispose();
            }
            base.Dispose(disposing);
        }

        // we can fetch data as XML file via https://:13793/api/Books/Editions URL
        // Here we are sharing Book Editions Dropdown as web service URL
        [Route("api/Books/Editions")]
        public IEnumerable<Basic> GetEditionIdNameWebAPI()
        {
            // Calling the Reopsitory project GetEditionIdName method
            return db.GetEditionIdName(); 
        }

    }

如果我们运行上述 LibraryApp.WebAPI 项目,并在 Web 浏览器中复制以下 URI,我们将收到如下 XML 文件。

**GetBooks()** HTTP 方法 *https://:13793/api/Books*


 

**GetBook(int id)** HTTP 方法 *https://:13793/api/Books/{id}*

与上述 BooksController 的创建过程类似,我们正在生成 AuthorsController 类和 BooksWithAuthorsController。

AuthorsController.cs

    public class AuthorsController : ApiController
    {
        private LibraryRepository db = new LibraryRepository();

        // GET: api/Authors
        // we can fetch data as XML file via https://:13793/api/Authors URI
        public IEnumerable<Author> GetAuthors()
        {
            // Calling the Reopsitory project GetAuthors method
            return db.GetAuthors();
        }

        // GET: api/Authors/5
        // we can fetch data as XML file via https://:13793/api/Authors/{id} URI
        [ResponseType(typeof(Author))]
        public IHttpActionResult GetAuthor(int id)
        {
            // Calling the Reopsitory project FindAuthorById method  
            Author author = db.FindAuthorById(id);
            if (author == null)
            {
                return NotFound();
            }

            return Ok(author);
        }

        // PUT: api/Authors/5
        // we can perform edit author method
        [ResponseType(typeof(void))]
        public IHttpActionResult PutAuthor(int id, Author author)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != author.Auth_Id)
            {
                return BadRequest();
            } 
            // Calling the Reopsitory project EditAuthor method           
            db.EditAuthor(author);               


            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/Authors
        // we can perform add author method
        [ResponseType(typeof(Author))]
        public IHttpActionResult PostAuthor(Author author)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            // Calling the Reopsitory project AddAuthor method  
            db.AddAuthor(author);

            return CreatedAtRoute("DefaultApi", new { id = author.Auth_Id }, author);
        }

        // DELETE: api/Authors/5
        // we can perform delete author method
        [ResponseType(typeof(Author))]
        public IHttpActionResult DeleteAuthor(int id)
        {
            Author author = db.FindAuthorById(id);
            if (author == null)
            {
                return NotFound();
            }
            // Calling the Reopsitory project RemoveAuthor method  
            db.RemoveAuthor(id);

            return Ok(author);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                //db.Dispose();
            }
            base.Dispose(disposing);
        }

        // GET: api/Authors
        // we can fetch data as XML file via https://:13793/api/Authors/all URI
        // Here we are sharing author id and name Dropdown as web service URL
        [Route("api/Authors/all")] 
        public IEnumerable<Basic> GetAuthorsIdName()
        {
            // Calling the Reopsitory project GetAuthorsIdName method
            return db.GetAuthorsIdName();
        }

        // GET: api/Authors
        // we can fetch data as XML file via https://:13793/api/Authors/EmploymentStatus URI
        // Here we are sharing employee status Dropdown as web service URL
        [Route("api/Authors/EmploymentStatus")]
        public IEnumerable<Basic> GetEmplyeeStatusIdNameWebAPI()
        {
            // Calling the Reopsitory project GetEmplyeeStatusIdName method
            return db.GetEmplyeeStatusIdName();
        }
    }
 

借助*`[System.Web.Http.Route("api/ControllerName/AnyName")]`*,我们可以为我们的方法创建自定义 URI,如上一个方法所示。

BooksWithAuthorsController.cs

    public class BooksWithAuthorsController : ApiController
    {
       
        private LibraryRepository db = new LibraryRepository();

        // GET: api/BooksWithAuthors
        // we can fetch data as XML file via https://:13793/api/BooksWithAuthors URI
        public IEnumerable<BookWithAuthor> GetBookswithAuthors()
        {
            // Calling the Reopsitory project GetBooksWithAuthors method
            return db.GetBooksWithAuthors();
        }

        // GET: api/BooksWithAuthors/3
        // we can fetch data as XML file via https://:13793/api/BooksWithAuthors/{id} URI
        [ResponseType(typeof(BookWithAuthor))]
        public IHttpActionResult GetBooksWithAuthorsById(int id)
        {
            // Calling the Reopsitory project FindBooksWithAuthorsById method
            BookWithAuthor bookwithauthor = db.FindBooksWithAuthorsById(id);
            if (bookwithauthor == null)
            {
                return NotFound();
            }

            return Ok(bookwithauthor);
        }

        // GET: api/BooksWithAuthors/3/Price  
        // we can fetch data as XML file via https://:13793/api/BooksWithAuthors/{id}/Price URI
        [ResponseType(typeof(decimal))]
        [System.Web.Http.Route("api/BooksWithAuthors/{id}/Price")]
        public IHttpActionResult GetBooksPriceById(int? id)
        {
            // Calling the Reopsitory project findBookPrice method
            decimal bookprice = db.findBookPrice(id);

            return Ok(bookprice);
        }

        // GET: api/BooksWithAuthors/3/booktitle/Price 
        // https://:13793/api/BooksWithAuthors/{id}/{book title}/Price URI
        [ResponseType(typeof(decimal))]
        [System.Web.Http.Route("api/BooksWithAuthors/{id}/{name}/Price")]
        public IHttpActionResult GetBooksPriceById(int? id, string name = null)
        {
            // Calling the Reopsitory project findBookPrice method
            decimal bookprice = db.findBookPrice(id,name);

            return Ok(bookprice);
        }
    } 

上述 **GetBooksPriceById()** 方法的**特色**是,作为这些重载方法的响应,我们返回 decimal 值。

您将从中学到的另一件事是如何配置可以发送多个参数的方法重载 Web API 方法。

**GetBooksPriceById(int? id)** HTTP 方法
https://:13793/api/BooksWithAuthors/{id}/Price

**Getbookspricebyid(int? Id, String Name =null)** HTTP 方法
https://:13793/api/bookswithauthors/{id}/{book_title}/Price

完成此 Web 服务项目后,我们可以通过将 URL 复制并粘贴到浏览器中来检查这些 GET 方法是否能从数据库正确获取数据。


现在我们转到我们项目堆栈的最后一个部分,即 LibraryApp.MVC ASP.NET MVC 5 客户端。

LibraryApp.MVC 的步骤

1. 创建一个空的 C# ASP.NET MVC 5 项目,命名为“ProjectName.MVC”。
2. 像上述项目一样,在 web.config 文件中添加连接字符串。
3. 给予以下库引用。

         System.Net.Http.Formatting
         System.Net.Http.Headers
         LibraryApp.Core
         LibraryApp.Infrastructure
         LibraryApp.WebAPI

4. 现在,在 Model 文件夹中添加一个新类,命名为“LibraryClient.cs”。

为了创建 MVC Controller,我们必须在 C# 代码中调用上述 API。如果您在互联网上搜索相关内容,您会发现大多数示例都使用**通过简单的 AJAX 调用使用 jQuery**来满足此场景。

但是,我们可以使用`HttpClient`。在本节中,您将了解如何**从 MVC 调用 WebAPI Controller 端点**。WebAPI Controller 操作返回 JSON。因此,当您调用它们时,您会收到一个大字符串作为响应。然后,您将在 MVC 应用程序中将此字符串解析为对象,然后就可以使用了。

所以,让我们通过**LibraryClient**文件来处理我们的 HTTP 方法,该文件可以在 MVC Controller 中使用。

LibraryClient.cs

public class LibraryClient
{
  //All the HTTP URI we are assigning here to ease of reuse     
  private string BOOKWITHAUTHOR_URI = "https://:13793/api/BooksWithAuthors";
  private string AUTHOR_URI = "https://:13793/api/Authors";
  private string BOOK_URI = "https://:13793/api/Books";

 #region //Books with Authors

 // Get all Book with Authors
 public IEnumerable<BookWithAuthor> GetAllBookWithAuthors()
 {
  try
  {
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(BOOKWITHAUTHOR_URI);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    HttpResponseMessage response = client.GetAsync("BooksWithAuthors").Result;
    if (response.IsSuccessStatusCode)
     return response.Content.ReadAsAsync<IEnumerable<BookWithAuthor>>().Result;

     return null;
   }
   catch
   {
     return null;
   }
  }

// Get a specific Book with Author
public BookWithAuthor GetBookwithAuthor(int id)
{
 try
 {
   HttpClient client = new HttpClient();
   client.BaseAddress = new Uri(BOOKWITHAUTHOR_URI);
   client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
   HttpResponseMessage response = client.GetAsync("BooksWithAuthors/" + id).Result;
   if (response.IsSuccessStatusCode)
    return response.Content.ReadAsAsync<BookWithAuthor>().Result;

    return null;
  }
  catch
  {
    return null;
  }
}

 #endregion

 #region // Book
 
 //Get book details only

 public Book GetBook(int id)
 {
  try
  {
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(BOOK_URI);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    HttpResponseMessage response = client.GetAsync("Books/" + id).Result;
    if (response.IsSuccessStatusCode)
     return response.Content.ReadAsAsync<Book>().Result;

     return null;
  }
  catch
  {
    return null;
  }
}     
     
 // Create a Book
 public bool CreateBook(Book book)
 {
  try
   {
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(BOOK_URI);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    HttpResponseMessage response = client.PostAsJsonAsync("Books/", book).Result;

    return response.IsSuccessStatusCode;
   }
   catch
   {
    return false;
   }
 }

 // Edit a Book
 public bool EditBook(Book book)
 {
  try
  {
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(BOOK_URI);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    HttpResponseMessage response = client.PutAsJsonAsync("Books/" + book.Book_Id, book).Result;

    return response.IsSuccessStatusCode;
  }
  catch
  {
    return false;
  }
 }

 // Delete a Book
 public bool DeleteBook(int id)
 {
  try
  {
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(BOOK_URI);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    HttpResponseMessage response = client.DeleteAsync("Books/" + id).Result;

    return response.IsSuccessStatusCode;
  }
  catch
  {
    return false;
  }
 }

 #endregion

 #region // Author

 // Get All Authors
 public IEnumerable<Author> GetAllAuthors()
 {
 try
 {
  HttpClient client = new HttpClient();
  client.BaseAddress = new Uri(AUTHOR_URI);
  client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  HttpResponseMessage response = client.GetAsync("Authors").Result;
  if (response.IsSuccessStatusCode)
   return response.Content.ReadAsAsync<IEnumerable<Author>>().Result;

   return null;
  }
  catch
  {
   return null;
  }
 }

 // Get a specific author
 public Author GetAuthor(int id)
 {
 try
 {
  HttpClient client = new HttpClient();
  client.BaseAddress = new Uri(AUTHOR_URI);
  client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  HttpResponseMessage response = client.GetAsync("Authors/" + id).Result;
  if (response.IsSuccessStatusCode)
   return response.Content.ReadAsAsync<Author>().Result;

   return null;
  }
  catch
  {
   return null;
  }
 }

 //Create an author
 public bool CreateAuthor(Author author)
 {
  try
  {
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(AUTHOR_URI);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    HttpResponseMessage response = client.PostAsJsonAsync("Authors/", author).Result;

    return response.IsSuccessStatusCode;
  }
  catch
  {
    return false;
  }
 }

 // Edit an author
 public bool EditAuthor(Author author)
 {
  try
  {
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(AUTHOR_URI);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    HttpResponseMessage response = client.PutAsJsonAsync("Authors/" + author.Auth_Id, author).Result;

    return response.IsSuccessStatusCode;
  }
  catch
  {
    return false;
  }

 }

 // Delete an author
 public bool DeleteAuthr(int id)
 {
  try
  {
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(AUTHOR_URI);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    HttpResponseMessage response = client.DeleteAsync("Authors/" + id).Result;

    return response.IsSuccessStatusCode;
  }
  catch
  {
    return false;
  }

 }

 //All the DropDown in this system
 
 // Get all the authors id and name
 public IEnumerable<Basic> GetAuthorsIdName()
 {
  try
  {
  HttpClient client = new HttpClient();
  client.BaseAddress = new Uri(AUTHOR_URI);
  client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  HttpResponseMessage response = client.GetAsync("Authors/all").Result;
  if (response.IsSuccessStatusCode)
   return response.Content.ReadAsAsync<IEnumerable<Basic>>().Result;

   return null;
  }
  catch
  {
   return null;
  }
 }

 //Get employee status id and name
 public IEnumerable<Basic> GetEmplyeeStatusIdNameMVCModel()
 {
  try
   {
   HttpClient client = new HttpClient();
   client.BaseAddress = new Uri(AUTHOR_URI);
   client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
   HttpResponseMessage response = client.GetAsync("Authors/EmploymentStatus").Result;
   if (response.IsSuccessStatusCode)
    return response.Content.ReadAsAsync<IEnumerable<Basic>>().Result;
     
    return null;
  }
  catch
  {
    return null;
  }
 }

 //Get book editions id and name
 public IEnumerable<Basic> GetEditionIdNameMVCModel()
 {
  try
  {
   HttpClient client = new HttpClient();
   client.BaseAddress = new Uri(AUTHOR_URI);
   client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
   HttpResponseMessage response = client.GetAsync("Books/Editions").Result;
   if (response.IsSuccessStatusCode)
    return response.Content.ReadAsAsync<IEnumerable<Basic>>().Result;
    
   return null;
  }
  catch
  {
   return null;
  }
 }

 #endregion

}

现在我们可以轻松地在我们的 mvc controller 中重用上面的 LibraryClient 方法,如下所示。

BookWithAuthorController.cs

public class BookWithAuthorController : Controller
{
  // GET: BooksWithAuthors
  public ActionResult BookswithAuthors()
  {
     LibraryClient bwu = new LibraryClient();
     ViewBag.listBookswithAuthors = bwu.GetAllBookWithAuthors();
     return View();
   }
}

BooksController.cs

public class BooksController : Controller
{
   // GET: Books/Create
  public ActionResult Create()  
  {
    LibraryClient lc = new LibraryClient();
    ViewBag.listAuthors = lc.GetAuthorsIdName().Select
                              (x => new SelectListItem
                               { Value = x.ID.ToString(),
                                 Text = x.NAME
                               });
    ViewBag.listEditions = lc.GetEditionIdNameMVCModel().Select
                              (x => new SelectListItem 
                               { Value = x.NAME,
                                 Text = x.NAME
                               });
  
   return View();
  }

 // POST: Books/Create
 [HttpPost]
 public ActionResult Create(Book book)
 {
    LibraryClient lc = new LibraryClient();
    lc.CreateBook(book);
 
    return RedirectToAction("BookswithAuthors", "BookWithAuthor");
  }

 // GET: Books/Delete
 public ActionResult Delete(int id)
 {
    LibraryClient lc = new LibraryClient();
    lc.DeleteBook(id);
    return RedirectToAction("BookswithAuthors", "BookWithAuthor");
 }

 // GET: Books/Edit
 [HttpGet]
 public ActionResult Edit(int id)
 {
    LibraryClient lc = new LibraryClient();
    Book book = new Book();
    book = lc.GetBook(id);
    ViewBag.listAuthors = lc.GetAuthorsIdName().Select
                                               (x => new SelectListItem 
                                               { Value = x.ID.ToString(), 
                                                 Text = x.NAME 
                                               });
    ViewBag.listEditions = lc.GetEditionIdNameMVCModel().Select
                                              (x => new SelectListItem 
                                              { Value = x.NAME, 
                                                Text = x.NAME 
                                              });
  
    return View("Edit", book);
 }

 // POST: Books/Edit
 [HttpPost]
 public ActionResult Edit(Book book)
 {
    LibraryClient pc = new LibraryClient();
    pc.EditBook(book);
 
    return RedirectToAction("BookswithAuthors", "BookWithAuthor");
 }
}

要使用 ViewBag 属性绑定下拉列表,我们可以遵循

第 1 种方法

ViewBag.listAuthers = new SelectList(lc.GetAuthersIdName(), "ID", "Name")

第 2 种方法

ViewBag.listAuthers = lc.GetAuthersIdName().Select(x => new SelectListItem
{
    Value = x.ID.ToString(),
    Text = x.Name
});

AuthorsController.cs

public class AuthorsController : Controller
{
  // GET: Authors
  public ActionResult Index()
  {
      LibraryClient lc = new LibraryClient();
      ViewBag.listAuthors = lc.GetAllAuthors();             

      return View();
   }

        // GET: Authors/Create
   public ActionResult Create()
   {
      LibraryClient lc = new LibraryClient();
      ViewBag.listEmploymentStatus = lc.GetEmplyeeStatusIdNameMVCModel().Select
                                       (x => new SelectListItem 
                                       { Value = x.NAME,
                                         Text = x.NAME
                                       });

      return View("Create");

   }

   // POST: Authors/Create
   [HttpPost]
   public ActionResult Create(Author author)
   {
     LibraryClient lc = new LibraryClient();
     lc.CreateAuthor(author);

     return RedirectToAction("Index", "Authors");
   }

   // GET: Authors/Delete
   public ActionResult Delete(int id)
   {
    LibraryClient lc = new LibraryClient();
    lc.DeleteAuthr(id);

    return RedirectToAction("Index", "Authors");
   }

  // GET: Authors/Edit
  [HttpGet]
  public ActionResult Edit(int id)
  {
    LibraryClient lc = new LibraryClient();
    Author author = new Author();
    ViewBag.listEmploymentStatus = lc.GetEmplyeeStatusIdNameMVCModel().Select
                                     (x => new SelectListItem 
                                     { Value = x.NAME, 
                                       Text = x.NAME
                                     });
    author = lc.GetAuthor(id);

    return View("Edit", author);
  }

  // POST: Authors/Edit
  [HttpPost]
  public ActionResult Edit(Author author)
  {
    LibraryClient pc = new LibraryClient();
    pc.EditAuthor(author);
    return RedirectToAction("Index", "Authors");
  }
}

包含作者列表的书籍 - 主仪表板

@{
    ViewBag.Title = "Books with Authors";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h4>Books with Authors</h4>

<p>
    <button type="button" class="btn btn-primary btn-sm" onclick="location.href='@Url.Action("Create","Books")';"><span class="glyphicon glyphicon-plus"></span> Create New Book</button>
   <button type="button" class="btn btn-primary btn-sm" onclick="location.href='@Url.Action("Index", "Authors")';"><span class="glyphicon glyphicon-user"></span> Authors List</button>
</p>

<table class="table">
    <tr>
        <th>
            Book ID
        </th>
        <th>
            Book Title
        </th>
        <th>
            Author Name
        </th>
        <th>
            Edition
        </th>
        <th>
            Price
        </th>
        <th>
            Actions
        </th>
    </tr>

    @foreach (var item in ViewBag.listBookswithAuthors)
    {
        <tr>
            <td>
                @item.BookWithAuthor_Id
            </td>
            <td>
                @item.BookWithAuthor_Title

            </td>
            <td>
                @item.BookWithAuthor_AuthorName
            </td>
            <td>
                @item.Edition
            </td>
            <td>
                @item.Price
            </td>

<td>
 <a class="btn btn-danger btn-xs" onclick="return confirm('Are you Sure ?')" href="@Url.Action("Delete","Books" , new { id = item.BookWithAuthor_Id })"><span class="glyphicon glyphicon-trash"></span> Delete Book</a>
 <a class="btn btn-warning btn-xs" href="@Url.Action("Edit","Books" , new { id = item.BookWithAuthor_Id })"><span class="glyphicon glyphicon-scissors"></span> Edit Book</a>
</td>
        </tr>
    }

</table>

创建一本书

@model LibraryApp.Core.Entities.Book

@{
    ViewBag.Title = "Create";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h4>Create a Book</h4>

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">

        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Book_Title, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Book_Title, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Book_Title, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Edition, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("Edition", (IEnumerable<SelectListItem>)ViewBag.listEditions, "Select the Edition", htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Edition, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Author_Id, "Author Name", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @*Non Strongly Type*@
                @Html.DropDownList("Author_Id", (IEnumerable<SelectListItem>)ViewBag.listAuthors, "Select the author name", htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Author_Id, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}


<div>
 <p>
  <button type="button" class="btn btn-primary btn-sm" onclick="location.href='@Url.Action("BookswithAuthors", "BookWithAuthor")';"><span class="glyphicon glyphicon-list-alt"></span> Book List</button>
 </p>
</div>

如果您注意到这里,上面的*Create Book View*类,它使用非强类型方法绑定其下拉列表。

***非强类型***方法

 @Html.DropDownList("Author_Id", (IEnumerable<SelectListItem>)ViewBag.listAuthors, "Select the author name", htmlAttributes: new { @class = "form-control" })

***强类型***方法

 @Html.DropDownList(m => m.Auther_Id, (IEnumerable<SelectListItem>)ViewBag.listAuthers, "Select the author name", htmlAttributes: new { @class = "form-control" })

编辑一本书

@model LibraryApp.Core.Entities.Book

@{
    ViewBag.Title = "Edit";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h4>Edit Book</h4>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Book_Id)

        <div class="form-group">
            @Html.LabelFor(model => model.Book_Title, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Book_Title, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Book_Title, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Edition, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @*@Html.EditorFor(model => model.Edition, new { htmlAttributes = new { @class = "form-control" } })*@
                @Html.DropDownList("Edition", (IEnumerable<SelectListItem>)ViewBag.listEditions, "Select the Edition", htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Edition, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Author_Id, "Author Name", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @*Non Strongly Type*@
                @Html.DropDownList("Author_Id", (IEnumerable<SelectListItem>)ViewBag.listAuthors, "Select the author name", htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Author_Id, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
 <p>
    <button type="button" class="btn btn-primary btn-xs" onclick="location.href='@Url.Action("BookswithAuthors", "BookWithAuthor")';">Back to List</button>
 </p>
</div>

作者列表仪表板

@{
    ViewBag.Title = "Authors";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h4> All Authors</h4>

<p>
 <button type="button" class="btn btn-primary btn-sm" onclick="location.href='@Url.Action("BookswithAuthors", "BookWithAuthor")';"><span class="glyphicon glyphicon-list-alt"></span> Book List</button>
 <button type="button" class="btn btn-primary btn-sm" onclick="location.href='@Url.Action("Create", "Authors")';"><span class="glyphicon glyphicon-plus"></span> Create an Author</button>
</p>

<table class="table">
    <tr>
        <th>
            ID
        </th>
        <th>
            First Name
        </th>
        <th>
            Last Name
        </th>
        <th>
            Biography
        </th>
        <th>
            Employed
        </th>
        <th>
            Action
        </th>
    </tr>

    @foreach (var item in ViewBag.listAuthors)
    {
        <tr>
            <td>
                @item.Auth_Id
            </td>
            <td>
                @item.First_Name

            </td>
            <td>
                @item.Last_Name
            </td>
            <td>
                @item.Biography
            </td>
            <td>
               
                @if (item.IsEmployed == "Y")
                {

                    <p><span class="glyphicon glyphicon-ok" style="color:lime"></span></p>
                }

                @if (item.IsEmployed == "N")
                {
                    <p><span class="glyphicon glyphicon-remove" style="color:deeppink"></span></p>   
                    
                }

            </td>

<td>
 <a class="btn btn-danger btn-xs" onclick="return confirm('Are you Sure ?')" href="@Url.Action("Delete","Authors" , new { id = item.Auth_Id })"><span class="glyphicon glyphicon-trash"></span> Delete Author</a>
 <a class="btn btn-warning btn-xs" href="@Url.Action("Edit","Authors" , new { id = item.Auth_Id })"><span class="glyphicon glyphicon-scissors"></span> Edit Author</a>
</td>
        </tr>
    }

</table>

创建一位作者

@model LibraryApp.Core.Entities.Author

@{
    ViewBag.Title = "Create";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h4>Create an Author</h4>

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">

        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.First_Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.First_Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.First_Name, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Last_Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Last_Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Last_Name, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Biography, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Biography, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Biography, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.IsEmployed, "Employed", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("IsEmployed", (IEnumerable<SelectListItem>)ViewBag.listEmploymentStatus, "Select the Employment Status", htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.IsEmployed, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
 <p>
   <button type="button" class="btn btn-primary btn-sm" onclick="location.href='@Url.Action("Index", "Authors")';"><span class="glyphicon glyphicon-user"></span> Authors List</button>
 </p>       
</div>

编辑一位作者

@model LibraryApp.Core.Entities.Author

@{
    ViewBag.Title = "Edit";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h4>Edit Author</h4>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
         <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Auth_Id)

        <div class="form-group">
            @Html.LabelFor(model => model.First_Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.First_Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.First_Name, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Last_Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Last_Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Last_Name, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Biography, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Biography, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Biography, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.IsEmployed,"Employed", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("IsEmployed", (IEnumerable<SelectListItem>)ViewBag.listEmploymentStatus, "Select the Employment Status", htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.IsEmployed, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
 <p>
  <button type="button" class="btn btn-primary btn-sm" onclick="location.href='@Url.Action("Index", "Authors")';"><span class="glyphicon glyphicon-user"></span> Authors List</button>
 </p>
</div>

以下 GitHub 地址包含此示例记账解决方案应用程序的完整源代码。由于此项目是使用 Code First Approach 构建的,因此无需包含 SQL 脚本。所以您可以轻松尝试开发它。

在开发此应用程序时,如果您发现任何错误/问题或有任何疑问,请随时发表评论。您的建议/意见对我非常重要,因为我尚处于开发初期阶段。

原始帖子可以在此处找到。

参考文献

© . All rights reserved.