{"id":143,"date":"2022-08-24T19:48:33","date_gmt":"2022-08-24T11:48:33","guid":{"rendered":"https:\/\/miie.net\/?p=143"},"modified":"2022-08-24T19:48:33","modified_gmt":"2022-08-24T11:48:33","slug":"ultimate-asp-net-core-web-api-chapter3-database-model-and-repository-pattern","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=143","title":{"rendered":"Chapter3:DATABASE MODEL AND REPOSITORY PATTERN"},"content":{"rendered":"<h1>3 DATABASE MODEL AND REPOSITORY PATTERN<\/h1>\n<p>In this chapter, we are going to create a database model and transfer it to the MSSQL database by using the code first approach. So, we are going to learn how to create entities (model classes), how to work with the DbContext class, and how to use migrations to transfer our created database model to the real database. Of course, it is not enough to just create a database model and transfer it to the database. We need to use it as well, and for that, we will create a Repository pattern as a data access layer.<br \/>\n\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e00\u4e2a\u6570\u636e\u5e93\u6a21\u578b\uff0c\u5e76\u4f7f\u7528\u4ee3\u7801\u4f18\u5148\u65b9\u6cd5\u5c06\u5176\u4f20\u8f93\u5230 MSSQL \u6570\u636e\u5e93\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u5c06\u5b66\u4e60\u5982\u4f55\u521b\u5efa\u5b9e\u4f53\uff08\u6a21\u578b\u7c7b\uff09\uff0c\u5982\u4f55\u4f7f\u7528DbContext\u7c7b\uff0c\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u8fc1\u79fb\u5c06\u6211\u4eec\u521b\u5efa\u7684\u6570\u636e\u5e93\u6a21\u578b\u4f20\u8f93\u5230\u771f\u5b9e\u6570\u636e\u5e93\u3002\u5f53\u7136\uff0c\u4ec5\u4ec5\u521b\u5efa\u4e00\u4e2a\u6570\u636e\u5e93\u6a21\u578b\u5e76\u5c06\u5176\u4f20\u8f93\u5230\u6570\u636e\u5e93\u662f\u4e0d\u591f\u7684\u3002\u6211\u4eec\u8fd8\u9700\u8981\u4f7f\u7528\u5b83\uff0c\u4e3a\u6b64\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e00\u4e2a\u5b58\u50a8\u5e93\u6a21\u5f0f\u4f5c\u4e3a\u6570\u636e\u8bbf\u95ee\u5c42\u3002\u200c\u200c<\/p>\n<p>With the Repository pattern, we create an abstraction layer between the data access and the business logic layer of an application. By using it, we are promoting a more loosely coupled approach to access our data in the database.<br \/>\n\u4f7f\u7528\u5b58\u50a8\u5e93\u6a21\u5f0f\uff0c\u6211\u4eec\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u6570\u636e\u8bbf\u95ee\u548c\u4e1a\u52a1\u903b\u8f91\u5c42\u4e4b\u95f4\u521b\u5efa\u4e86\u4e00\u4e2a\u62bd\u8c61\u5c42\u3002\u901a\u8fc7\u4f7f\u7528\u5b83\uff0c\u6211\u4eec\u6b63\u5728\u63a8\u5e7f\u4e00\u79cd\u66f4\u677e\u6563\u8026\u5408\u7684\u65b9\u6cd5\u6765\u8bbf\u95ee\u6570\u636e\u5e93\u4e2d\u7684\u6570\u636e\u3002<\/p>\n<p>Also, our code becomes cleaner, easier to maintain, and reusable. Data access logic is stored in a separate class, or sets of classes called a repository, with the responsibility of persisting the application\u2019s business model.<br \/>\n\u6b64\u5916\uff0c\u6211\u4eec\u7684\u4ee3\u7801\u53d8\u5f97\u66f4\u52a0\u6e05\u6670\uff0c\u66f4\u6613\u4e8e\u7ef4\u62a4\u548c\u53ef\u91cd\u7528\u3002\u6570\u636e\u8bbf\u95ee\u903b\u8f91\u5b58\u50a8\u5728\u4e00\u4e2a\u5355\u72ec\u7684\u7c7b\u6216\u79f0\u4e3a\u5b58\u50a8\u5e93\u7684\u7c7b\u96c6\u4e2d\uff0c\u8d1f\u8d23\u6301\u4e45\u5316\u5e94\u7528\u7a0b\u5e8f\u7684\u4e1a\u52a1\u6a21\u578b\u3002<\/p>\n<p>So, let\u2019s start with the model classes first.<br \/>\n\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u5148\u4ece\u6a21\u578b\u7c7b\u5f00\u59cb\u3002<\/p>\n<h2>3.1 Creating Models<\/h2>\n<p>Using the example from the second chapter of this book, we are going to extract a new Class Library (.NET Core) project named <strong>Entities<\/strong>.<br \/>\n\u4f7f\u7528\u672c\u4e66\u7b2c\u4e8c\u7ae0\u4e2d\u7684\u793a\u4f8b\uff0c\u6211\u4eec\u5c06\u63d0\u53d6\u4e00\u4e2a\u540d\u4e3a Entities \u7684\u65b0\u7c7b\u5e93 \uff08.NET Core\uff09 \u9879\u76ee\u3002(\u65b0\u5efa\u4e00\u4e2aEntities\u7c7b\u5e93\u9879\u76ee)<\/p>\n<p>Don\u2019t forget to add the reference from the main project to the Entities project.<br \/>\n\u4e0d\u8981\u5fd8\u8bb0\u5c06\u4e3b\u9879\u76ee\u4e2d\u7684\u5f15\u7528\u6dfb\u52a0\u5230\u5b9e\u4f53\u9879\u76ee\u4e2d\u3002<\/p>\n<p>Inside it, we are going to create a folder named Models, which will contain all the model classes (entities). Entities represent classes that Entity Framework Core uses to map our database model with the tables from the database. The properties from entity classes will be mapped to the database columns.<br \/>\n\u5728\u5176\u4e2d\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a Models \u7684\u6587\u4ef6\u5939\uff0c\u8be5\u6587\u4ef6\u5939\u5c06\u5305\u542b\u6240\u6709\u6a21\u578b\u7c7b\uff08\u5b9e\u4f53\uff09\u3002\u5b9e\u4f53\u8868\u793a\u5b9e\u4f53\u6846\u67b6\u6838\u5fc3\u7528\u4e8e\u5c06\u6570\u636e\u5e93\u6a21\u578b\u4e0e\u8868\u6620\u5c04\u7684\u7c7b\u4ece\u6570\u636e\u5e93\u4e2d\u3002\u5b9e\u4f53\u7c7b\u7684\u5c5e\u6027\u5c06\u6620\u5c04\u5230\u6570\u636e\u5e93\u5217\u3002<\/p>\n<p>So, in the Models folder we are going to create two classes and modify them:<br \/>\n\u56e0\u6b64\uff0c\u5728 Models \u6587\u4ef6\u5939\u4e2d\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e24\u4e2a\u7c7b\u5e76\u5bf9\u5176\u8fdb\u884c\u4fee\u6539\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/Company.cs\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing System.ComponentModel.DataAnnotations;\n\nnamespace Entities.Models\n{\n    public class Company { \n        &#x5B;Column(&quot;CompanyId&quot;)] \n        public Guid Id { get; set; } \n        &#x5B;Required(ErrorMessage = &quot;Company name is a required field.&quot;)]\n        &#x5B;MaxLength(60, ErrorMessage = &quot;Maximum length for the Name is 60 characters.&quot;)] \n        public string Name { get; set; } \n\n        &#x5B;Required(ErrorMessage = &quot;Company address is a required field.&quot;)]\n        &#x5B;MaxLength(60, ErrorMessage = &quot;Maximum length for the Address is 60 characters&quot;)] \n        public string Address { get; set; } \n\n        public string Country { get; set; } \n        public ICollection&lt;Employee&gt; Employees { get; set; } }\n    public class Employee { \n        &#x5B;Column(&quot;EmployeeId&quot;)] \n        public Guid Id { get; set; } \n\n        &#x5B;Required(ErrorMessage = &quot;Employee name is a required field.&quot;)]\n        &#x5B;MaxLength(30, ErrorMessage = &quot;Maximum length for the Name is 30 characters.&quot;)] \n        public string Name { get; set; } \n\n        &#x5B;Required(ErrorMessage = &quot;Age is a required field.&quot;)] \n        public int Age { get; set; }\n\n        &#x5B;Required(ErrorMessage = &quot;Position is a required field.&quot;)]\n        &#x5B;MaxLength(20, ErrorMessage = &quot;Maximum length for the Position is 20 characters.&quot;)] \n        public string Position { get; set; } \n\n        &#x5B;ForeignKey(nameof(Company))] \n        public Guid CompanyId { get; set; } \n\n        public Company Company { get; set; } }\n}\n<\/pre>\n<p>We have created two classes: the Company and Employee. Those classes contain the properties which Entity Framework Core is going to map to the columns in our tables in the database. But not all the properties will be mapped as columns. The last property of the Company class (Employees) and the last property of the Employee class (Company) are navigational properties; these properties serve the purpose of defining the relationship between our models.<br \/>\n\u6211\u4eec\u521b\u5efa\u4e86\u4e24\u4e2a\u7c7b\uff1a\u516c\u53f8\u548c\u5458\u5de5\u3002\u8fd9\u4e9b\u7c7b\u5305\u542b\u5b9e\u4f53\u6846\u67b6\u6838\u5fc3\u5c06\u6620\u5c04\u5230\u6570\u636e\u5e93\u4e2d\u8868\u4e2d\u7684\u5217\u7684\u5c5e\u6027\u3002\u4f46\u5e76\u975e\u6240\u6709\u5c5e\u6027\u90fd\u5c06\u6620\u5c04\u4e3a\u5217\u3002\u516c\u53f8\u7c7b\uff08\u5458\u5de5\uff09\u7684\u6700\u540e\u4e00\u4e2a\u5c5e\u6027\u548c\u5458\u5de5\u7c7b\uff08\u516c\u53f8\uff09\u7684\u6700\u540e\u4e00\u4e2a\u5c5e\u6027\u662f\u5bfc\u822a\u5c5e\u6027;\u8fd9\u4e9b\u5c5e\u6027\u7528\u4e8e\u5b9a\u4e49\u6a21\u578b\u4e4b\u95f4\u7684\u5173\u7cfb\u3002<\/p>\n<p>We can see several attributes in our entities. The <strong>[Column]<\/strong> attribute will specify that the <strong>Id<\/strong> property is going to be mapped with a different name in the database. The <strong>[Required]<\/strong> and <strong>[MaxLength]<\/strong> properties are here for validation purposes. The first one declares the property as mandatory and the second one defines its maximum length.<br \/>\n\u6211\u4eec\u53ef\u4ee5\u5728\u5b9e\u4f53\u4e2d\u770b\u5230\u51e0\u4e2a\u5c5e\u6027\u3002<strong>[Column]<\/strong> \u7279\u6027\u5c06\u6307\u5b9a <strong>Id<\/strong> \u5c5e\u6027\u5c06\u5728\u6570\u636e\u5e93\u4e2d\u4f7f\u7528\u4e0d\u540c\u7684\u540d\u79f0\u8fdb\u884c\u6620\u5c04\u3002\u6b64\u5904\u7684 <strong>[Required]<\/strong> \u548c <strong>[MaxLength]<\/strong> \u5c5e\u6027\u7528\u4e8e\u9a8c\u8bc1\u76ee\u7684\u3002\u7b2c\u4e00\u4e2a\u5c06\u5c5e\u6027\u58f0\u660e\u4e3a\u5fc5\u9700\u5c5e\u6027\uff0c\u7b2c\u4e8c\u4e2a\u5b9a\u4e49\u5176\u6700\u5927\u957f\u5ea6\u3002<\/p>\n<p>Once we transfer our database model to the real database, we are going to see how all these validation attributes and navigational properties affect the column definitions.<br \/>\n\u5c06\u6570\u636e\u5e93\u6a21\u578b\u4f20\u8f93\u5230\u5b9e\u9645\u6570\u636e\u5e93\u540e\uff0c\u6211\u4eec\u5c06\u770b\u5230\u6240\u6709\u8fd9\u4e9b\u9a8c\u8bc1\u7279\u6027\u548c\u5bfc\u822a\u5c5e\u6027\u5982\u4f55\u5f71\u54cd\u5217\u5b9a\u4e49\u3002<\/p>\n<h2>3.2 Context Class and The Database Connection<\/h2>\n<p>Now, let's create the context class, which will be a middleware component for communication with the database. It must inherit from the Entity Framework Core\u2019s <strong>DbContext<\/strong> class and it consists of <strong>DbSet<\/strong> properties, which EF Core is going to use for the communication with the database. Because we are working with the DBContext class, we need to install the <strong>Microsoft.EntityFrameworkCore<\/strong> package in the Entities project.<br \/>\n\u73b0\u5728\uff0c\u8ba9\u6211\u4eec\u521b\u5efa\u4e0a\u4e0b\u6587\u7c7b\uff0c\u5b83\u5c06\u662f\u7528\u4e8e\u4e0e\u6570\u636e\u5e93\u901a\u4fe1\u7684\u4e2d\u95f4\u4ef6\u7ec4\u4ef6\u3002\u5b83\u5fc5\u987b\u7ee7\u627f\u81ea\u5b9e\u4f53\u6846\u67b6\u6838\u5fc3\u7684 <strong>DbContext<\/strong> \u7c7b\uff0c\u5e76\u4e14\u5b83\u7531 <strong>DbSet<\/strong> \u5c5e\u6027\u7ec4\u6210\uff0cEF Core \u5c06\u4f7f\u7528\u8fd9\u4e9b\u5c5e\u6027\u4e0e\u6570\u636e\u5e93\u8fdb\u884c\u901a\u4fe1\u3002\u7531\u4e8e\u6211\u4eec\u4f7f\u7528\u7684\u662f DBContext \u7c7b\uff0c\u56e0\u6b64\u9700\u8981\u5b89\u88c5Entities \u9879\u76ee\u4e2d\u7684 <strong>Microsoft.EntityFrameworkCore<\/strong> \u5305\u3002<\/p>\n<p>So, let\u2019s navigate to the root of the Entities project and create the RepositoryContext class:<br \/>\n\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u5bfc\u822a\u5230Entities\u9879\u76ee\u7684\u6839\u5e76\u521b\u5efaRepositoryContext\u7c7b\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/RepositoryContext.cs\nusing Entities.Models;\nusing Microsoft.EntityFrameworkCore;\nnamespace Entities\n{\n    public class RepositoryContext : DbContext\n    {\n        public RepositoryContext(DbContextOptions options) : base(options) { }\n        public DbSet&lt;Company&gt; Companies { get; set; }\n        public DbSet&lt;Employee&gt; Employees { get; set; }\n    }\n}\n<\/pre>\n<p>After the class modification, let\u2019s open the <strong>appsettings.json<\/strong> file and add the connection string named <strong>sqlconnection<\/strong>:<br \/>\n\u4fee\u6539\u7c7b\u540e\uff0c\u8ba9\u6211\u4eec\u6253\u5f00 <strong>appsettings.json<\/strong> \u6587\u4ef6\u5e76\u6dfb\u52a0\u540d\u4e3a <strong>sqlconnection<\/strong> \u7684\u8fde\u63a5\u5b57\u7b26\u4e32\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/appsettings.json\n{\n  &quot;Logging&quot;: {\n    &quot;LogLevel&quot;: {\n      &quot;Default&quot;: &quot;Information&quot;,\n      &quot;Microsoft&quot;: &quot;Warning&quot;,\n      &quot;Microsoft.Hosting.Lifetime&quot;: &quot;Information&quot;\n    }\n  },\n  &quot;ConnectionStrings&quot;: {\n    &quot;sqlConnection&quot;: &quot;server=.; database=CompanyEmployee; Integrated Security=true&quot;\n  },\n  &quot;AllowedHosts&quot;: &quot;*&quot;\n  }\n<\/pre>\n<p>It is quite important to have the JSON object with the ConnectionStrings name in our appsettings.json file, and soon you will see why.<br \/>\n\u5728\u6211\u4eec\u7684 appsettings.json \u6587\u4ef6\u4e2d\u5177\u6709 ConnectionStrings \u540d\u79f0\u7684 JSON \u5bf9\u8c61\u975e\u5e38\u91cd\u8981\uff0c\u5f88\u5feb\u4f60\u5c31\u4f1a\u660e\u767d\u4e3a\u4ec0\u4e48\u3002<\/p>\n<p>We have one more step to finish the database model configuration. We need to register the <strong>RepositoryContext<\/strong> class in the application\u2019s dependency injection container as we did with the <strong>LoggerManager<\/strong> class in the previous chapter.<br \/>\n\u6211\u4eec\u8fd8\u6709\u4e00\u4e2a\u6b65\u9aa4\u6765\u5b8c\u6210\u6570\u636e\u5e93\u6a21\u578b\u914d\u7f6e\u3002\u6211\u4eec\u9700\u8981\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165\u5bb9\u5668\u4e2d\u6ce8\u518c <strong>RepositoryContext<\/strong> \u7c7b\uff0c\u5c31\u50cf\u6211\u4eec\u5728\u4e0a\u4e00\u7ae0\u4e2d\u5bf9 <strong>LoggerManager<\/strong> \u7c7b\u6240\u505a\u7684\u90a3\u6837\u3002<\/p>\n<p>So, let\u2019s open the <strong>ServiceExtensions<\/strong> class and add the additional method:<br \/>\n\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u6253\u5f00 <strong>ServiceExtensions<\/strong> \u7c7b\u5e76\u6dfb\u52a0\u5176\u4ed6\u65b9\u6cd5\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ServiceExtensions.cs\nusing Contracts;\nusing Entities;\nusing LoggerService;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\n\/\/using Microsoft.EntityFrameworkCore.SqlServer;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace CompanyEmployees.Extensions\n{\n    public static class ServiceExtensions\n    {\n\n        public static void ConfigureCors(this IServiceCollection services) =&gt;\n            services.AddCors(options =&gt;\n            {\n                options.AddPolicy(&quot;CorsPolicy&quot;, builder =&gt;\n                builder.AllowAnyOrigin()\n                .AllowAnyMethod()\n                .AllowAnyHeader());\n            });\n\n        public static void ConfigureIISIntegration(this IServiceCollection service) =&gt;\n            service.Configure&lt;IISOptions&gt;(options =&gt; { });\n\n        public static void ConfigureLoggerService(this IServiceCollection services) =&gt; \n            services.AddScoped&lt;ILoggerManager, LoggerManager&gt;();\n\n        public static void ConfigureSqlContext(this IServiceCollection services, \n            IConfiguration configuration) =&gt;\n            services.AddDbContext&lt;RepositoryContext&gt;(opts =&gt;\n                opts.UseSqlServer(configuration.GetConnectionString(&quot;sqlConnection&quot;)));\n\n    }\n}\n<\/pre>\n<p>With the help of the <strong>IConfiguration configuration<\/strong> parameter, we can use the <strong>GetConnectionString<\/strong> method to access the connection string from the <strong>appsettings.json<\/strong> file. Moreover, to be able to use the <strong>UseSqlServer<\/strong> method, we need to install the <strong>Microsoft.EntityFrameworkCore.SqlServer<\/strong> package. If we navigate to the <strong>GetConnectionString<\/strong> method definition, we will see that it is an extension method that uses the ConnectionStrings name from the <strong>appsettings.json<\/strong> file to fetch the connection string by the provided key:<br \/>\n\u5728 <strong>IConfiguration configuration<\/strong>\u914d\u7f6e\u53c2\u6570\u7684\u5e2e\u52a9\u4e0b\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528 <strong>GetConnectionString<\/strong> \u65b9\u6cd5\u4ece <strong>appsettings.json<\/strong> \u6587\u4ef6\u4e2d\u8bbf\u95ee\u8fde\u63a5\u5b57\u7b26\u4e32\u3002\u6b64\u5916\uff0c\u4e3a\u4e86\u80fd\u591f\u4f7f\u7528 <strong>UseSqlServer<\/strong> \u65b9\u6cd5\uff0c\u6211\u4eec\u9700\u8981\u5b89\u88c5 <strong>Microsoft.EntityFrameworkCore.SqlServer<\/strong> \u5305\u3002\u5982\u679c\u6211\u4eec\u5bfc\u822a\u5230 <strong>GetConnectionString<\/strong> \u65b9\u6cd5\u5b9a\u4e49\uff0c\u6211\u4eec\u5c06\u770b\u5230\u5b83\u662f\u4e00\u4e2a\u6269\u5c55\u65b9\u6cd5\uff0c\u5b83\u4f7f\u7528 <strong>appsettings.json<\/strong> \u6587\u4ef6\u4e2d\u7684 <strong>ConnectionStrings<\/strong> \u540d\u79f0\u901a\u8fc7\u63d0\u4f9b\u7684\u952e\u83b7\u53d6\u8fde\u63a5\u5b57\u7b26\u4e32\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_2004.jpg\" alt=\"alt\" \/><\/p>\n<p>Afterward, in the <strong>Startup<\/strong> class in the <strong>ConfigureServices<\/strong> method, we are going to add the context service to the IOC right above the <strong>services.AddControllers()<\/strong> line:<br \/>\n\u4e4b\u540e\uff0c\u5728 <strong>ConfigureServices<\/strong> \u65b9\u6cd5\u7684 <strong>Startup<\/strong> \u7c7b\u4e2d\uff0c\u6211\u4eec\u5c06\u4e0a\u4e0b\u6587\u670d\u52a1\u6dfb\u52a0\u5230 <strong>services.AddControllers()<\/strong> \u6b63\u4e0a\u65b9\u7684\u884c\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/Startup.cs\npublic void ConfigureServices(IServiceCollection services)\n        {\n            services.ConfigureCors();\n            services.ConfigureIISIntegration();\n            services.ConfigureLoggerService();\n            services.ConfigureSqlContext(Configuration);\n\n            services.AddControllers();\n        }\n<\/pre>\n<\/p>\n<h2>3.3 Migration and Initial Data Seed<\/h2>\n<p>Migration is a standard process of creating and updating the database from our application. Since we are finished with the database model creation, we can transfer that model to the real database. But we need to modify our <strong>ConfigureSqlContext<\/strong> method first:<br \/>\n\u8fc1\u79fb\u662f\u4ece\u6211\u4eec\u7684\u5e94\u7528\u7a0b\u5e8f\u521b\u5efa\u548c\u66f4\u65b0\u6570\u636e\u5e93\u7684\u6807\u51c6\u8fc7\u7a0b\u3002\u7531\u4e8e\u6211\u4eec\u5df2\u5b8c\u6210\u6570\u636e\u5e93\u6a21\u578b\u7684\u521b\u5efa\uff0c\u56e0\u6b64\u6211\u4eec\u53ef\u4ee5\u5c06\u8be5\u6a21\u578b\u4f20\u8f93\u5230\u5b9e\u9645\u6570\u636e\u5e93\u3002\u4f46\u662f\u6211\u4eec\u9700\u8981\u5148\u4fee\u6539\u6211\u4eec\u7684 <strong>ConfigureSqlContext<\/strong> \u65b9\u6cd5\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ServiceExtensions.cs\nusing Contracts;\nusing Entities;\nusing LoggerService;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace CompanyEmployees.Extensions\n{\n    public static class ServiceExtensions\n    {\n\n        public static void ConfigureCors(this IServiceCollection services) =&gt;\n            services.AddCors(options =&gt;\n            {\n                options.AddPolicy(&quot;CorsPolicy&quot;, builder =&gt;\n                builder.AllowAnyOrigin()\n                .AllowAnyMethod()\n                .AllowAnyHeader());\n            });\n\n        public static void ConfigureIISIntegration(this IServiceCollection service) =&gt;\n            service.Configure&lt;IISOptions&gt;(options =&gt; { });\n\n        public static void ConfigureLoggerService(this IServiceCollection services) =&gt; \n            services.AddScoped&lt;ILoggerManager, LoggerManager&gt;();\n\n        \/\/public static void ConfigureSqlContext(this IServiceCollection services, \n        \/\/    IConfiguration configuration) =&gt;\n        \/\/    services.AddDbContext&lt;RepositoryContext&gt;(opts =&gt;\n        \/\/        opts.UseSqlServer(configuration.GetConnectionString(&quot;sqlConnection&quot;)));\n\n        public static void ConfigureSqlContext(this IServiceCollection services, \n            IConfiguration configuration) =&gt; \n            services.AddDbContext&lt;RepositoryContext&gt;(opts =&gt; \n            opts.UseSqlServer(configuration.GetConnectionString(&quot;sqlConnection&quot;), b =&gt; \n            b.MigrationsAssembly(&quot;CompanyEmployees&quot;)));\n    }\n}\n<\/pre>\n<p>We have to make this change because migration assembly is not in our main project, but in the <strong>Entities<\/strong> project. So, we just change the project for the migration assembly.<br \/>\n\u6211\u4eec\u5fc5\u987b\u8fdb\u884c\u6b64\u66f4\u6539\uff0c\u56e0\u4e3a\u8fc1\u79fb\u7a0b\u5e8f\u96c6\u4e0d\u5728\u6211\u4eec\u7684\u4e3b\u9879\u76ee\u4e2d\uff0c\u800c\u662f\u5728 <strong>Entities<\/strong> \u9879\u76ee\u4e2d\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u53ea\u9700\u66f4\u6539\u8fc1\u79fb\u7a0b\u5e8f\u96c6\u7684\u9879\u76ee\u3002<\/p>\n<p>Before we execute our migration commands, we have to install an additional ef core library: <strong>Microsoft.EntityFrameworkCore.Tools<\/strong><br \/>\n\u5728\u6267\u884c\u8fc1\u79fb\u547d\u4ee4\u4e4b\u524d\uff0c\u6211\u4eec\u5fc5\u987b\u5b89\u88c5\u4e00\u4e2a\u989d\u5916\u7684 ef \u6838\u5fc3\u5e93\uff1a<strong>Microsoft.EntityFrameworkCore.Tools<\/strong><\/p>\n<p>Now, let\u2019s open the Package Manager Console window and create our first migration: PM&gt; Add-Migration DatabaseCreation<br \/>\n\u73b0\u5728\uff0c\u8ba9\u6211\u4eec\u6253\u5f00\u7a0b\u5e8f\u5305\u7ba1\u7406\u5668\u63a7\u5236\u53f0(Package Manager Console)\u7a97\u53e3\u5e76\u521b\u5efa\u6211\u4eec\u7684\u7b2c\u4e00\u4e2a\u8fc1\u79fb\uff1a  <\/p>\n<p>PM&gt; Add-Migration DatabaseCreation<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\u6bcf\u4e2a\u5305\u90fd\u7531\u5176\u6240\u6709\u8005\u8bb8\u53ef\u7ed9\u4f60\u3002NuGet \u4e0d\u8d1f\u8d23\u7b2c\u4e09\u65b9\u5305\uff0c\u4e5f\u4e0d\u6388\u4e88\u5176\u8bb8\u53ef\u8bc1\u3002\u4e00\u4e9b\u5305\u53ef\u80fd\u5305\u62ec\u53d7\u5176\u4ed6\u8bb8\u53ef\u8bc1\u7ea6\u675f\u7684\u4f9d\u8d56\u5173\u7cfb\u3002\u5355\u51fb\u5305\u6e90(\u6e90) URL \u53ef\u786e\u5b9a\u4efb\u4f55\u4f9d\u8d56\u5173\u7cfb\u3002\n\u7a0b\u5e8f\u5305\u7ba1\u7406\u5668\u63a7\u5236\u53f0\u4e3b\u673a\u7248\u672c 6.3.0.131\n\u952e\u5165 &quot;get-help NuGet&quot; \u53ef\u67e5\u770b\u6240\u6709\u53ef\u7528\u7684 NuGet \u547d\u4ee4\u3002\nPM&gt; Add-Migration DatabaseCreation\nBuild started...\nBuild succeeded.\nTo undo this action, use Remove-Migration.\n<\/pre>\n<\/p>\n<p>With this command, we are creating migration files and we can find them in the <strong>Migrations<\/strong> folder in our main project:<br \/>\n\u4f7f\u7528\u6b64\u547d\u4ee4\uff0c\u6211\u4eec\u6b63\u5728\u521b\u5efa\u8fc1\u79fb\u6587\u4ef6\uff0c\u5e76\u4e14\u53ef\u4ee5\u5728\u4e3b\u9879\u76ee\u7684\u201c <strong>Migrations<\/strong> \u201d\u6587\u4ef6\u5939\u4e2d\u627e\u5230\u5b83\u4eec\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_3001.jpg\" alt=\"alt\" \/><\/p>\n<p>With those files in place, we can apply migration: PM&gt; Update-Database<br \/>\n\u6709\u4e86\u8fd9\u4e9b\u6587\u4ef6\uff0c\u6211\u4eec\u5c31\u53ef\u4ee5\u5e94\u7528\u8fc1\u79fb\uff1aPM&gt; Update-Database<\/p>\n<p>Excellent. We can inspect our database now:<br \/>\n\u975e\u5e38\u597d\u3002\u6211\u4eec\u73b0\u5728\u53ef\u4ee5\u68c0\u67e5\u6211\u4eec\u7684\u6570\u636e\u5e93\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_3002.jpg\" alt=\"alt\" \/><\/p>\n<p>Once we have the database and tables created, we should populate them with some initial data. To do that, we are going to create another folder called <strong>Configuration<\/strong> in the <strong>Entities<\/strong> project and add the <strong>CompanyConfiguration<\/strong> class:<br \/>\n\u521b\u5efa\u6570\u636e\u5e93\u548c\u8868\u540e\uff0c\u6211\u4eec\u5e94\u8be5\u7528\u4e00\u4e9b\u521d\u59cb\u6570\u636e\u586b\u5145\u5b83\u4eec\u3002\u4e3a\u6b64\uff0c\u6211\u4eec\u5c06\u5728 <strong>Entities<\/strong> \u9879\u76ee\u4e2d\u521b\u5efa\u53e6\u4e00\u4e2a\u540d\u4e3a\u201c<strong>Configuration<\/strong>\u201d\u7684\u6587\u4ef6\u5939\uff0c\u5e76\u6dfb\u52a0 <strong>CompanyConfiguration<\/strong> \u7c7b\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Entities.Models;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.EntityFrameworkCore;\nusing System;\n\nnamespace Entities.Configuration\n{\n    public class CompanyConfiguration : IEntityTypeConfiguration&lt;Company&gt;\n    {\n        public void Configure(EntityTypeBuilder&lt;Company&gt; builder)\n        { \n            builder.HasData(\n                new Company \n                { \n                    Id = new Guid(&quot;c9d4c053-49b6-410c-bc78-2d54a9991870&quot;), \n                    Name = &quot;IT_Solutions Ltd&quot;, \n                    Address = &quot;583 Wall Dr. Gwynn Oak, MD 21207&quot;, Country = &quot;USA&quot; }, \n                new Company { Id = new Guid(&quot;3d490a70-94ce-4d15-9494-5248280c2ce3&quot;), \n                    Name = &quot;Admin_Solutions Ltd&quot;, \n                    Address = &quot;312 Forest Avenue, BF 923&quot;, \n                    Country = &quot;USA&quot; }); \n                }\n    }\n}\n<\/pre>\n<p>Let\u2019s do the same thing for the <strong>EmployeeConfiguration<\/strong> class:<br \/>\n\u8ba9\u6211\u4eec\u5bf9 <strong>EmployeeConfiguration<\/strong> \u7c7b\u6267\u884c\u76f8\u540c\u7684\u64cd\u4f5c\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Entities.Models;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.EntityFrameworkCore;\nusing System;\n\nnamespace Entities.Configuration\n{\n    public class EmployeeConfiguration : IEntityTypeConfiguration&lt;Employee&gt;\n    { \n        public void Configure(EntityTypeBuilder&lt;Employee&gt; builder) \n        { \n            builder.HasData(\n                new Employee { \n                    Id = new Guid(&quot;80abbca8-664d-4b20-b5de-024705497d4a&quot;), \n                    Name = &quot;Sam Raiden&quot;, \n                    Age = 26, \n                    Position = &quot;Software developer&quot;, \n                    CompanyId = new Guid(&quot;c9d4c053-49b6-410c-bc78-2d54a9991870&quot;) \n                }, \n                new Employee { \n                    Id = new Guid(&quot;86dba8c0-d178-41e7-938c-ed49778fb52a&quot;), \n                    Name = &quot;Jana McLeaf&quot;, \n                    Age = 30, \n                    Position = &quot;Software developer&quot;, \n                    CompanyId = new Guid(&quot;c9d4c053-49b6-410c-bc78-2d54a9991870&quot;) \n                }, \n                new Employee { Id = new Guid(&quot;021ca3c1-0deb-4afd-ae94-2159a8479811&quot;), \n                    Name = &quot;Kane Miller&quot;, \n                    Age = 35, \n                    Position = &quot;Administrator&quot;, \n                    CompanyId = new Guid(&quot;3d490a70-94ce-4d15-9494-5248280c2ce3&quot;) \n                }); \n        } \n    }\n}\n<\/pre>\n<p>To invoke this configuration, we have to change the <strong>RepositoryContext<\/strong> class:<br \/>\n\u8981\u8c03\u7528\u6b64\u914d\u7f6e\uff0c\u6211\u4eec\u5fc5\u987b\u66f4\u6539 <strong>RepositoryContext<\/strong> \u7c7b\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Entities.Configuration;\nusing Entities.Models;\nusing Microsoft.EntityFrameworkCore;\nnamespace Entities\n{\n    public class RepositoryContext : DbContext\n    {\n        public RepositoryContext(DbContextOptions options) : base(options) { }\n\n        protected override void OnModelCreating(ModelBuilder modelBuilder) \n        { \n            modelBuilder.ApplyConfiguration(new CompanyConfiguration()); \n            modelBuilder.ApplyConfiguration(new EmployeeConfiguration()); \n        }\n        public DbSet&lt;Company&gt; Companies { get; set; }\n        public DbSet&lt;Employee&gt; Employees { get; set; }\n    }\n}\n<\/pre>\n<p>Now, we can create and apply another migration to seed these data to the database:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u521b\u5efa\u5e76\u5e94\u7528\u53e6\u4e00\u4e2a\u8fc1\u79fb\uff0c\u4ee5\u5c06\u8fd9\u4e9b\u6570\u636e\u79cd\u5b50\u8bbe\u5b9a\u5230\u6570\u636e\u5e93\u4e2d\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nPM&gt; Add-Migration InitialData \nBuild started...\nBuild succeeded.\nTo undo this action, use Remove-Migration.\n\nPM&gt; Update-Database\nBuild started...\nBuild succeeded.\nDone.\n<\/pre>\n<p>This will transfer all the data from our configuration files to the respective tables.<br \/>\n\u8fd9\u4f1a\u5c06\u6240\u6709\u6570\u636e\u4ece\u6211\u4eec\u7684\u914d\u7f6e\u6587\u4ef6\u4f20\u8f93\u5230\u76f8\u5e94\u7684\u8868\u4e2d\u3002<\/p>\n<h2>3.4 Repository Pattern Logic<\/h2>\n<p>After establishing a connection to the database and creating one, it's time to create a generic repository that will provide us with the CRUD methods. As a result, all the methods can be called upon any repository class in our project.<br \/>\n\u5efa\u7acb\u4e0e\u6570\u636e\u5e93\u7684\u8fde\u63a5\u5e76\u521b\u5efa\u4e00\u4e2a\u8fde\u63a5\u540e\uff0c\u662f\u65f6\u5019\u521b\u5efa\u4e00\u4e2a\u901a\u7528\u5b58\u50a8\u5e93\uff0c\u8be5\u5b58\u50a8\u5e93\u5c06\u4e3a\u6211\u4eec\u63d0\u4f9bCRUD\u65b9\u6cd5\u3002\u56e0\u6b64\uff0c\u53ef\u4ee5\u5728\u6211\u4eec\u9879\u76ee\u4e2d\u7684\u4efb\u4f55\u5b58\u50a8\u5e93\u7c7b\u4e0a\u8c03\u7528\u6240\u6709\u65b9\u6cd5\u3002<\/p>\n<p>Furthermore, creating the generic repository and repository classes that use that generic repository is not going to be the final step. We will go a step further and create a wrapper class around repository classes and inject it as a service in a dependency injection container.<br \/>\n\u6b64\u5916\uff0c\u521b\u5efa\u901a\u7528\u5b58\u50a8\u5e93\u548c\u4f7f\u7528\u8be5\u901a\u7528\u5b58\u50a8\u5e93\u7684\u5b58\u50a8\u5e93\u7c7b\u4e0d\u4f1a\u662f\u6700\u540e\u4e00\u6b65\u3002\u6211\u4eec\u5c06\u66f4\u8fdb\u4e00\u6b65\uff0c\u56f4\u7ed5\u5b58\u50a8\u5e93\u7c7b\u521b\u5efa\u4e00\u4e2a\u5305\u88c5\u7c7b\uff0c\u5e76\u5c06\u5176\u4f5c\u4e3a\u670d\u52a1\u6ce8\u5165\u5230\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165\u5bb9\u5668\u4e2d\u3002<\/p>\n<p>Consequently, we will be able to instantiate this class once and then call any repository class we need inside any of our controllers.<br \/>\n\u56e0\u6b64\uff0c\u6211\u4eec\u5c06\u80fd\u591f\u5b9e\u4f8b\u5316\u4e00\u6b21\u6b64\u7c7b\uff0c\u7136\u540e\u5728\u4efb\u4f55\u63a7\u5236\u5668\u4e2d\u8c03\u7528\u6240\u9700\u7684\u4efb\u4f55\u5b58\u50a8\u5e93\u7c7b\u3002<\/p>\n<p>The advantages of this approach will become clearer once we use it in the project.<br \/>\n\u4e00\u65e6\u6211\u4eec\u5728\u9879\u76ee\u4e2d\u4f7f\u7528\u5b83\uff0c\u8fd9\u79cd\u65b9\u6cd5\u7684\u4f18\u52bf\u5c06\u53d8\u5f97\u66f4\u52a0\u6e05\u6670\u3002<\/p>\n<p>That said, let\u2019s start by creating an interface for the repository inside the <strong>Contracts<\/strong> project:<br \/>\n\u4e5f\u5c31\u662f\u8bf4\uff0c\u8ba9\u6211\u4eec\u9996\u5148\u4e3a\u5b58\u50a8\u5e93\u521b\u5efa\u4e00\u4e2a\u63a5\u53e3<strong>Contracts<\/strong>\u9879\u76ee\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing System;\nusing System.Linq;\nusing System.Linq.Expressions;\nnamespace Contracts\n{\n    public interface IRepositoryBase&lt;T&gt;\n    {\n        IQueryable&lt;T&gt; FindAll(bool trackChanges);\n        IQueryable&lt;T&gt; FindByCondition(Expression&lt;Func&lt;T, bool&gt;&gt; expression, bool trackChanges);\n        void Create(T entity);\n        void Update(T entity);\n        void Delete(T entity);\n    }\n}\n<\/pre>\n<p>Right after the interface creation, we are going to create a new Class Library (.NET Core) project with the name <strong>Repository<\/strong> and add the reference to the <strong>Contracts<\/strong> and <strong>Entities<\/strong> class libraries. Inside the <strong>Repository<\/strong> project, we are going to create an abstract class <strong>RepositoryBase<\/strong> \u2014 which is going to implement the <strong>IRepositoryBase<\/strong> interface.<br \/>\n\u5728\u521b\u5efa\u63a5\u53e3\u4e4b\u540e\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a <strong>Repository<\/strong> \u7684\u65b0\u7c7b\u5e93\uff08.NET Core\uff09\u9879\u76ee\uff0c\u5e76\u6dfb\u52a0\u5bf9 <strong>Contracts<\/strong> \u548c <strong>Entities<\/strong> \u7c7b\u5e93\u7684\u5f15\u7528\u3002\u5728 <strong>Repository<\/strong> \u9879\u76ee\u4e2d\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e00\u4e2a\u62bd\u8c61\u7c7b <strong>RepositoryBase<\/strong> \u2014 \u5b83\u5c06\u5b9e\u73b0  <strong>IRepositoryBase<\/strong> \u63a5\u53e3\u3002<\/p>\n<p>We need to reference this project from the main project as well.<br \/>\n\u6211\u4eec\u8fd8\u9700\u8981\u4ece\u4e3b\u9879\u76ee\u4e2d\u5f15\u7528\u6b64\u9879\u76ee\u3002<\/p>\n<p><strong>Additional info:<\/strong> We are going to use EF Core functionalities in the Repository project. Therefore, we need to install it inside the Repository project.<br \/>\n<strong>\u5176\u4ed6\u4fe1\u606f\uff1a<\/strong>\u6211\u4eec\u5c06\u5728\u5b58\u50a8\u5e93\u9879\u76ee\u4e2d\u4f7f\u7528 EF Core \u529f\u80fd\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u9700\u8981\u5c06\u5176\u5b89\u88c5\u5728\u5b58\u50a8\u5e93\u9879\u76ee\u4e2d\u3002<\/p>\n<p>Let\u2019s add the following code to the <strong>RepositoryBase<\/strong> class:<br \/>\n\u8ba9\u6211\u4eec\u5c06\u4ee5\u4e0b\u4ee3\u7801\u6dfb\u52a0\u5230<strong>RepositoryBase<\/strong>\u7c7b\u4e2d\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/RepositoryBase.cs\nusing Contracts;\nusing Entities;\nusing Microsoft.EntityFrameworkCore;\nusing System;\nusing System.Linq;\nusing System.Linq.Expressions;\nnamespace Repository\n{\n    public abstract class RepositoryBase&lt;T&gt; : IRepositoryBase&lt;T&gt; where T : class\n    {\n        protected RepositoryContext RepositoryContext; \n        public RepositoryBase(RepositoryContext repositoryContext) { \n            RepositoryContext = repositoryContext; }\n        public IQueryable&lt;T&gt; FindAll(bool trackChanges) =&gt; \n            !trackChanges ? \n            RepositoryContext.Set&lt;T&gt;().\n            AsNoTracking() :\n            RepositoryContext.Set&lt;T&gt;(); \n        public IQueryable&lt;T&gt; FindByCondition(Expression&lt;Func&lt;T, bool&gt;&gt; expression, \n            bool trackChanges) =&gt; \n            !trackChanges ? \n            RepositoryContext.Set&lt;T&gt;()\n            .Where(expression).AsNoTracking() : \n            RepositoryContext.Set&lt;T&gt;().Where(expression); \n        public void Create(T entity) =&gt; RepositoryContext.Set&lt;T&gt;().Add(entity); \n        public void Update(T entity) =&gt; RepositoryContext.Set&lt;T&gt;().Update(entity); \n        public void Delete(T entity) =&gt; RepositoryContext.Set&lt;T&gt;().Remove(entity);\n    }\n}\n<\/pre>\n<p>This abstract class as well as the <strong>IRepositoryBase<\/strong> interface works with the generic type <strong>T<\/strong>. This type <strong>T<\/strong> gives even more reusability to the <strong>RepositoryBase<\/strong> class. That means we don\u2019t have to specify the exact model (class) right now for the <strong>RepositoryBase<\/strong> to work with. We can do that later on.<br \/>\n\u6b64\u62bd\u8c61\u7c7b\u4ee5\u53ca <strong>IRepositoryBase<\/strong> \u63a5\u53e3\u9002\u7528\u4e8e\u6cdb\u578b\u7c7b\u578b <strong>T<\/strong>\u3002\u6b64\u7c7b\u578b <strong>T<\/strong> \u4e3a <strong>RepositoryBase<\/strong> \u7c7b\u63d0\u4f9b\u4e86\u66f4\u591a\u7684\u53ef\u91cd\u7528\u6027\u3002\u8fd9\u610f\u5473\u7740\u6211\u4eec\u73b0\u5728\u4e0d\u5fc5\u4e3a<strong>RepositoryBase<\/strong>\u7c7b\u6307\u5b9a\u786e\u5207\u7684\u6a21\u578b\uff08\u7c7b\uff09\u3002\u6211\u4eec\u53ef\u4ee5\u7a0d\u540e\u518d\u8fd9\u6837\u505a\u3002<\/p>\n<p>Moreover, we can see the <strong>trackChanges<\/strong> parameter. We are going to use it to improve our read-only query performance. When it\u2019s set to false, we attach the <strong>AsNoTracking<\/strong> method to our query to inform EF Core that it doesn\u2019t need to track changes for the required entities. This greatly improves the speed of a query.<br \/>\n\u6b64\u5916\uff0c\u6211\u4eec\u53ef\u4ee5\u770b\u5230 <strong>trackChanges<\/strong> \u53c2\u6570\u3002\u6211\u4eec\u5c06\u4f7f\u7528\u5b83\u6765\u63d0\u9ad8\u53ea\u8bfb\u67e5\u8be2\u6027\u80fd\u3002\u5f53\u5b83\u8bbe\u7f6e\u4e3a false \u65f6\uff0c\u6211\u4eec\u4f1a\u5c06 <strong>AsNoTracking<\/strong> \u65b9\u6cd5\u9644\u52a0\u5230\u67e5\u8be2\uff0c\u4ee5\u901a\u77e5 EF Core \u5b83\u4e0d\u9700\u8981\u8ddf\u8e2a\u6240\u9700\u5b9e\u4f53\u7684\u66f4\u6539\u3002\u8fd9\u5927\u5927\u63d0\u9ad8\u4e86\u67e5\u8be2\u7684\u901f\u5ea6\u3002<\/p>\n<h2>3.5 Repository User InterFaces and Classes<\/h2>\n<p>Now that we have the <strong>RepositoryBase<\/strong> class, let\u2019s create the user classes that will inherit this abstract class.<br \/>\n\u73b0\u5728\u6211\u4eec\u6709\u4e86 <strong>RepositoryBase<\/strong> \u7c7b\uff0c\u8ba9\u6211\u4eec\u521b\u5efa\u5c06\u7ee7\u627f\u6b64\u62bd\u8c61\u7c7b\u7684\u7528\u6237\u7c7b\u3002<\/p>\n<p>By inheriting from the RepositoryBase class, they will have access to all the methods from it. Furthermore, every user class will have its interface for additional model-specific methods.<br \/>\n\u901a\u8fc7\u4ece <strong>RepositoryBase<\/strong> \u7c7b\u7ee7\u627f\uff0c\u4ed6\u4eec\u5c06\u6709\u6743\u8bbf\u95ee\u8be5\u7c7b\u4e2d\u7684\u6240\u6709\u65b9\u6cd5\u3002\u6b64\u5916\uff0c\u6bcf\u4e2a\u7528\u6237\u7c7b\u90fd\u5c06\u5177\u6709\u7528\u4e8e\u5176\u4ed6\u7279\u5b9a\u4e8e\u6a21\u578b\u7684\u65b9\u6cd5\u7684\u63a5\u53e3\u3002<\/p>\n<p>This way, we are separating the logic that is common for all our repository user classes and also specific for every user class itself.<br \/>\n\u901a\u8fc7\u8fd9\u79cd\u65b9\u5f0f\uff0c\u6211\u4eec\u5c06\u6240\u6709\u5b58\u50a8\u5e93\u7528\u6237\u7c7b\u901a\u7528\u7684\u903b\u8f91\u4ee5\u53ca\u6bcf\u4e2a\u7528\u6237\u7c7b\u672c\u8eab\u7684\u903b\u8f91\u5206\u5f00\u3002<\/p>\n<p>Let\u2019s create the interfaces in the <strong>Contracts<\/strong> project for the <strong>Company<\/strong> and <strong>Employee<\/strong> classes.<br \/>\n\u8ba9\u6211\u4eec\u5728 <strong>Contracts<\/strong> \u9879\u76ee\u4e2d\u4e3a <strong>Company<\/strong> \u521b\u5efa\u63a5\u53e3\uff0c\u7136\u540e <strong>Employee<\/strong> \u7c7b\u3002<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nnamespace Contracts\n{\n    public interface ICompanyRepository { }\n}\n\nnamespace Contracts\n{\n    public interface IEmployeeRepository { }\n}\n<\/pre>\n<p>After this, we can create repository user classes in the <strong>Repository<\/strong> project.<br \/>\n\u5728\u6b64\u4e4b\u540e\uff0c\u6211\u4eec\u53ef\u4ee5\u5728 <strong>Repository<\/strong> \u4e2d\u521b\u5efa\u5b58\u50a8\u5e93\u7528\u6237\u7c7b\u3002<\/p>\n<p>The first thing we are going to do is to create the <strong>CompanyRepository<\/strong> class:<br \/>\n\u6211\u4eec\u8981\u505a\u7684\u7b2c\u4e00\u4ef6\u4e8b\u662f\u521b\u5efaCompanyRepository\u7c7b\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Contracts;\nusing Entities.Models;\nusing Entities;\nnamespace Repository\n{\n    public class CompanyRepository : RepositoryBase&lt;Company&gt;, ICompanyRepository\n    { \n        public CompanyRepository(RepositoryContext repositoryContext) : base(repositoryContext) { } \n    }\n}\n```\nAnd then, the **EmployeeRepository** class:  \n\u7136\u540e\uff0c**EmployeeRepository** \u7c7b\uff1a\n```\nusing Contracts;\nusing Entities.Models;\nusing Entities;\nnamespace Repository\n{\n    public class EmployeeRepository : RepositoryBase&lt;Employee&gt;, IEmployeeRepository { \n        public EmployeeRepository(RepositoryContext repositoryContext) : base(repositoryContext) { }\n    }\n}\n<\/pre>\n<p>After these steps, we are finished creating the repository and repository user classes. But there are still more things to do.<br \/>\n\u5b8c\u6210\u8fd9\u4e9b\u6b65\u9aa4\u540e\uff0c\u6211\u4eec\u5b8c\u6210\u4e86\u5b58\u50a8\u5e93\u548c\u5b58\u50a8\u5e93\u7528\u6237\u7c7b\u7684\u521b\u5efa\u3002\u4f46\u8fd8\u6709\u66f4\u591a\u7684\u4e8b\u60c5\u8981\u505a\u3002<\/p>\n<h2>3.6 Creating a Repository Manager<\/h2>\n<p>It is quite common for the API to return a response that consists of data from multiple resources; for example, all the companies and just some employees older than 30. In such a case, we would have to instantiate both of our repository classes and fetch data from their resources.<br \/>\nAPI \u8fd4\u56de\u7531\u6765\u81ea\u591a\u4e2a\u8d44\u6e90\u7684\u6570\u636e\u7ec4\u6210\u7684\u54cd\u5e94\u662f\u5f88\u5e38\u89c1\u7684;\u4f8b\u5982\uff0c\u6240\u6709\u516c\u53f8\u548c\u4e00\u4e9b 30 \u5c81\u4ee5\u4e0a\u7684\u5458\u5de5\u3002\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u6211\u4eec\u5fc5\u987b\u5b9e\u4f8b\u5316\u4e24\u4e2a\u5b58\u50a8\u5e93\u7c7b\uff0c\u5e76\u4ece\u5176\u8d44\u6e90\u4e2d\u83b7\u53d6\u6570\u636e\u3002\u200c<\/p>\n<p>Maybe it\u2019s not a problem when we have only two classes, but what if we need the combined logic of five or even more different classes? It would just be too complicated to pull that off.<br \/>\n\u5f53\u6211\u4eec\u53ea\u6709\u4e24\u4e2a\u7c7b\u65f6\uff0c\u4e5f\u8bb8\u8fd9\u4e0d\u662f\u95ee\u9898\uff0c\u4f46\u662f\u5982\u679c\u6211\u4eec\u9700\u8981\u4e94\u4e2a\u751a\u81f3\u66f4\u591a\u4e0d\u540c\u7c7b\u7684\u7ec4\u5408\u903b\u8f91\u5462\uff1f\u8981\u505a\u5230\u8fd9\u4e00\u70b9\u592a\u590d\u6742\u4e86\u3002<\/p>\n<p>With that in mind, we are going to create a repository manager class, which will create instances of repository user classes for us and then register it inside the dependency injection container. After that, we can inject it inside our controllers (or inside a business layer class, if we have a bigger app) with constructor injection (supported by ASP.NET Core). With the repository manager class in place, we may call any repository user class we need.<br \/>\n\u8003\u8651\u5230\u8fd9\u4e00\u70b9\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e00\u4e2a\u5b58\u50a8\u5e93\u7ba1\u7406\u5668\u7c7b\uff0c\u5b83\u5c06\u4e3a\u6211\u4eec\u521b\u5efa\u5b58\u50a8\u5e93\u7528\u6237\u7c7b\u7684\u5b9e\u4f8b\uff0c\u7136\u540e\u5728\u4f9d\u8d56\u6ce8\u5165\u5bb9\u5668\u4e2d\u6ce8\u518c\u5b83\u3002\u4e4b\u540e\uff0c\u6211\u4eec\u53ef\u4ee5\u5c06\u5176\u6ce8\u5165\u63a7\u5236\u5668\uff08\u6216\u8005\u5982\u679c\u6211\u4eec\u6709\u66f4\u5927\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u5219\u5728\u4e1a\u52a1\u5c42\u7c7b\u4e2d\uff09\u4f7f\u7528\u6784\u9020\u51fd\u6570\u6ce8\u5165\uff08\u7531 ASP.NET Core\u652f\u6301\uff09\u3002\u6709\u4e86\u5b58\u50a8\u5e93\u7ba1\u7406\u5668\u7c7b\uff0c\u6211\u4eec\u53ef\u4ee5\u8c03\u7528\u6211\u4eec\u9700\u8981\u7684\u4efb\u4f55\u5b58\u50a8\u5e93\u7528\u6237\u7c7b\u3002<\/p>\n<p>But we are also missing one important part. We have the <strong>Create<\/strong>, <strong>Update<\/strong>, and <strong>Delete<\/strong> methods in the <strong>RepositoryBase<\/strong> class, but they won\u2019t make any change in the database until we call the <strong>SaveChanges<\/strong> method. Our repository manager class will handle that as well.<br \/>\n\u4f46\u6211\u4eec\u4e5f\u7f3a\u5c11\u4e00\u4e2a\u91cd\u8981\u90e8\u5206\u3002\u6211\u4eec\u5728 <strong>RepositoryBase<\/strong> \u7c7b\u4e2d\u6709 <strong>Create<\/strong>\u3001<strong>Update<\/strong> \u548c <strong>Delete<\/strong> \u65b9\u6cd5\uff0c\u4f46\u5728\u6211\u4eec\u8c03\u7528 <strong>SaveChanges<\/strong> \u65b9\u6cd5\u4e4b\u524d\uff0c\u5b83\u4eec\u4e0d\u4f1a\u5728\u6570\u636e\u5e93\u4e2d\u8fdb\u884c\u4efb\u4f55\u66f4\u6539\u3002\u6211\u4eec\u7684\u5b58\u50a8\u5e93\u7ba1\u7406\u5668\u7c7b\u4e5f\u5c06\u5904\u7406\u8fd9\u4e2a\u95ee\u9898\u3002<\/p>\n<p>That said, let\u2019s get to it and create a new interface in the <strong>Contract<\/strong> project:<br \/>\n\u4e5f\u5c31\u662f\u8bf4\uff0c\u8ba9\u6211\u4eec\u5f00\u59cb\u4f7f\u7528\u5b83\uff0c\u5e76\u5728<strong>Contract<\/strong> \u9879\u76ee\u4e2d\u521b\u5efa\u4e00\u4e2a\u65b0\u63a5\u53e3\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nnamespace Contracts\n{\n    public interface IRepositoryManager { \n        ICompanyRepository Company { get; } \n        IEmployeeRepository Employee { get; } \n        void Save(); }\n}\n<\/pre>\n<p>And add a new class to the <strong>Repository<\/strong> project:<br \/>\n\u5e76\u5411 <strong>Repository<\/strong> \u9879\u76ee\u6dfb\u52a0\u4e00\u4e2a\u65b0\u7c7b\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Contracts;\nusing Entities;\nnamespace Repository\n{\n    public class RepositoryManager : IRepositoryManager\n    {\n        private RepositoryContext _repositoryContext;\n        private ICompanyRepository _companyRepository;\n        private IEmployeeRepository _employeeRepository;\n        public RepositoryManager(RepositoryContext repositoryContext)\n        {\n            _repositoryContext = repositoryContext;\n        }\n        public ICompanyRepository Company\n        {\n            get\n            {\n                if (_companyRepository == null)\n                    _companyRepository = new CompanyRepository(_repositoryContext);\n                return _companyRepository;\n            }\n        }\n        public IEmployeeRepository Employee\n        {\n            get\n            {\n                if (_employeeRepository == null)\n                    _employeeRepository = new EmployeeRepository(_repositoryContext);\n                return _employeeRepository;\n            }\n        }\n        public void Save() =&gt; _repositoryContext.SaveChanges();\n    }\n}\n<\/pre>\n<p>As you can see, we are creating properties that will expose the concrete repositories and also we have the <strong>Save()<\/strong> method to be used after all the modifications are finished on a certain object. This is a good practice because now we can, for example, add two companies, modify two employees, and delete one company \u2014 all in one action \u2014 and then just call the Save method once. All the changes will be applied or if something fails, all the changes will be reverted:<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u6211\u4eec\u6b63\u5728\u521b\u5efa\u5c06\u516c\u5f00\u5177\u4f53\u5b58\u50a8\u5e93\u7684\u5c5e\u6027\uff0c\u5e76\u4e14\u6211\u4eec\u8fd8\u5177\u6709\u5728\u5bf9\u67d0\u4e2a\u5bf9\u8c61\u5b8c\u6210\u6240\u6709\u4fee\u6539\u540e\u8981\u4f7f\u7528\u7684  <strong>Save()<\/strong>  \u65b9\u6cd5\u3002\u8fd9\u662f\u4e00\u4e2a\u5f88\u597d\u7684\u505a\u6cd5\uff0c\u56e0\u4e3a\u73b0\u5728\u6211\u4eec\u53ef\u4ee5\u6dfb\u52a0\u4e24\u4e2a\u516c\u53f8\uff0c\u4fee\u6539\u4e24\u4e2a\u5458\u5de5\uff0c\u5e76\u5220\u9664\u4e00\u4e2a\u516c\u53f8 - \u6240\u6709\u8fd9\u4e9b\u90fd\u5728\u4e00\u4e2a\u64cd\u4f5c\u4e2d - \u7136\u540e\u53ea\u9700\u8c03\u7528\u4e00\u6b21 Save \u65b9\u6cd5\u3002\u5c06\u5e94\u7528\u6240\u6709\u66f4\u6539\uff0c\u6216\u8005\u5982\u679c\u67d0\u4e9b\u5185\u5bb9\u5931\u8d25\uff0c\u5219\u6240\u6709\u66f4\u6539\u90fd\u5c06\u88ab\u8fd8\u539f\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n_repository.Company.Create(company); \n_repository.Company.Create(anotherCompany); \n_repository.Employee.Update(employee); \n_repository.Employee.Update(anotherEmployee);\n_repository.Company.Delete(oldCompany);\n_repository.Save();\n<\/pre>\n<p>After these changes, we need to register our manager class and add a reference from the Repository to our main project if not already done so. So, let\u2019s first modify the <strong>ServiceExtensions<\/strong> class by adding this code:<br \/>\n\u5b8c\u6210\u8fd9\u4e9b\u66f4\u6539\u540e\uff0c\u6211\u4eec\u9700\u8981\u6ce8\u518c\u7ba1\u7406\u5668\u7c7b\uff0c\u5e76\u5c06\u5b58\u50a8\u5e93\u4e2d\u7684\u5f15\u7528\u6dfb\u52a0\u5230\u4e3b\u9879\u76ee\u4e2d\uff08\u5982\u679c\u5c1a\u672a\u8fd9\u6837\u505a\uff09\u3002\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u9996\u5148\u901a\u8fc7\u6dfb\u52a0\u4ee5\u4e0b\u4ee3\u7801\u6765\u4fee\u6539 <strong>ServiceExtensions<\/strong> \u7c7b\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Contracts;\nusing Entities;\nusing LoggerService;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.EntityFrameworkCore;\nusing Repository;\n\nnamespace CompanyEmployees.Extensions\n{\n    public static class ServiceExtensions\n    {\n        public static void ConfigureCors(this IServiceCollection services) =&gt;\n            services.AddCors(options =&gt;\n            {\n                options.AddPolicy(&quot;CorsPolicy&quot;, builder =&gt;\n                builder.AllowAnyOrigin()\n                .AllowAnyMethod()\n                .AllowAnyHeader());\n            });\n\n        public static void ConfigureIISIntegration(this IServiceCollection service) =&gt;\n            service.Configure&lt;IISOptions&gt;(options =&gt; { });\n\n        public static void ConfigureLoggerService(this IServiceCollection services) =&gt; \n            services.AddScoped&lt;ILoggerManager, LoggerManager&gt;();\n\n        \/\/public static void ConfigureSqlContext(this IServiceCollection services, \n        \/\/    IConfiguration configuration) =&gt;\n        \/\/    services.AddDbContext&lt;RepositoryContext&gt;(opts =&gt;\n        \/\/        opts.UseSqlServer(configuration.GetConnectionString(&quot;sqlConnection&quot;)));\n        public static void ConfigureSqlContext(this IServiceCollection services, \n            IConfiguration configuration) =&gt; \n            services.AddDbContext&lt;RepositoryContext&gt;(opts =&gt; \n            opts.UseSqlServer(configuration.GetConnectionString(&quot;sqlConnection&quot;), b =&gt; \n            b.MigrationsAssembly(&quot;CompanyEmployees&quot;)));\n\n        public static void ConfigureRepositoryManager(this IServiceCollection services) =&gt; \n            services.AddScoped&lt;IRepositoryManager, RepositoryManager&gt;();\n    }\n}\n<\/pre>\n<p>And in the <strong>Startup<\/strong> class inside the <strong>ConfigureServices<\/strong> method, above the <strong>services.AddController()<\/strong> line, we have to add this code:<br \/>\n\u5728 <strong>ConfigureServices<\/strong> \u65b9\u6cd5\u5185\u7684 <strong>Startup<\/strong> \u7c7b\u4e2d\uff0c\u5728 <strong>services.AddController()<\/strong> \u4e0a\u65b9\u884c\uff0c\u6211\u4eec\u5fc5\u987b\u6dfb\u52a0\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic void ConfigureServices(IServiceCollection services)\n        {\n            services.ConfigureCors();\n            services.ConfigureIISIntegration();\n            services.ConfigureLoggerService();\n            services.ConfigureSqlContext(Configuration);\n            services.ConfigureRepositoryManager();\n\n            services.AddControllers();\n        }\n<\/pre>\n<p>Excellent.<br \/>\n\u975e\u5e38\u597d\u3002<\/p>\n<p>As soon as we add some methods to the specific repository classes, we are going to be able to test this logic, but we can just take a peek at how we can inject and use this repository manager.<br \/>\n\u4e00\u65e6\u6211\u4eec\u5c06\u4e00\u4e9b\u65b9\u6cd5\u6dfb\u52a0\u5230\u7279\u5b9a\u7684\u5b58\u50a8\u5e93\u7c7b\u4e2d\uff0c\u6211\u4eec\u5c06\u80fd\u591f\u6d4b\u8bd5\u6b64\u903b\u8f91\uff0c\u4f46\u662f\u6211\u4eec\u53ef\u4ee5\u770b\u4e00\u4e0b\u5982\u4f55\u6ce8\u5165\u548c\u4f7f\u7528\u6b64\u5b58\u50a8\u5e93\u7ba1\u7406\u5668\u3002<\/p>\n<p>All we have to do is to inject the <strong>RepositoryManager<\/strong> service inside the controller and we are going to see the Company and Employee properties that will provide us access to the specific repository methods:<br \/>\n\u6211\u4eec\u6240\u8981\u505a\u7684\u5c31\u662f\u5728\u63a7\u5236\u5668\u4e2d\u6ce8\u5165 <strong>RepositoryManager<\/strong> \u670d\u52a1\uff0c\u6211\u4eec\u5c06\u770b\u5230 Company \u548c Employee \u5c5e\u6027\uff0c\u8fd9\u4e9b\u5c5e\u6027\u5c06\u4f7f\u6211\u4eec\u80fd\u591f\u8bbf\u95ee\u7279\u5b9a\u7684\u5b58\u50a8\u5e93\u65b9\u6cd5\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Contracts;\nusing Microsoft.AspNetCore.Mvc;\nusing System;\nusing System.Collections.Generic;\n\nnamespace CompanyEmployees\n{\n    \/\/public class WeatherForecast\n    \/\/{\n    \/\/    public DateTime Date { get; set; }\n    \/\/    public int TemperatureC { get; set; }\n    \/\/    public int TemperatureF =&gt; 32 + (int)(TemperatureC \/ 0.5556);\n    \/\/    public string Summary { get; set; }\n    \/\/}\n\n    \/\/&#x5B;Route(&quot;&#x5B;controller]&quot;)]\n    \/\/&#x5B;ApiController]\n    \/\/public class WeatherForecastController : ControllerBase\n    \/\/{\n    \/\/    private ILoggerManager _logger;\n    \/\/    public WeatherForecastController(ILoggerManager logger) { _logger = logger; }\n    \/\/    &#x5B;HttpGet]\n    \/\/    public IEnumerable&lt;string&gt; Get()\n    \/\/    {\n    \/\/        _logger.LogInfo(&quot;Here is info message from our values controller.&quot;);\n    \/\/        _logger.LogDebug(&quot;Here is debug message from our values controller.&quot;);\n    \/\/        _logger.LogWarn(&quot;Here is warn message from our values controller.&quot;);\n    \/\/        _logger.LogError(&quot;Here is an error message from our values controller.&quot;);\n    \/\/        return new string&#x5B;] { &quot;value1&quot;, &quot;value2&quot; };\n    \/\/    }\n    \/\/}\n\n    &#x5B;Route(&quot;&#x5B;controller]&quot;)]\n    &#x5B;ApiController]\n    public class WeatherForecastController : ControllerBase\n    {\n        private readonly IRepositoryManager _repository;\n        public WeatherForecastController(IRepositoryManager repository)\n        {\n            _repository = repository;\n        }\n        &#x5B;HttpGet]\n        public ActionResult&lt;IEnumerable&lt;string&gt;&gt; Get()\n        {\n            \/\/_repository.Company.AnyMethodFromCompanyRepository();\n            \/\/_repository.Employee.AnyMethodFromEmployeeRepository();\n            return new string&#x5B;] { &quot;value1&quot;, &quot;value2&quot; };\n        }\n    }\n\n}\n<\/pre>\n<p>We did an excellent job here. The repository layer is prepared and ready to be used to fetch data from the database.<br \/>\n\u6211\u4eec\u5728\u8fd9\u91cc\u505a\u5f97\u5f88\u597d\u3002\u5b58\u50a8\u5e93\u5c42\u5df2\u51c6\u5907\u5c31\u7eea\uff0c\u53ef\u7528\u4e8e\u4ece\u6570\u636e\u5e93\u83b7\u53d6\u6570\u636e\u3002  <\/p>\n<p>As you can see, we have injected our repository inside the controller; this is a good practice for an application of this size. But for larger-scale applications, we would create an additional business layer between our controllers and repository logic and our RepositoryManager service would be injected inside that Business layer \u2014 thus freeing the controller from repository logic.<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u6211\u4eec\u5df2\u7ecf\u5728\u63a7\u5236\u5668\u4e2d\u6ce8\u5165\u4e86\u6211\u4eec\u7684\u5b58\u50a8\u5e93;\u5bf9\u4e8e\u8fd9\u79cd\u5927\u5c0f\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u8fd9\u662f\u4e00\u4e2a\u5f88\u597d\u7684\u505a\u6cd5\u3002\u4f46\u5bf9\u4e8e\u66f4\u5927\u89c4\u6a21\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u6211\u4eec\u5c06\u5728\u63a7\u5236\u5668\u548c\u5b58\u50a8\u5e93\u903b\u8f91\u4e4b\u95f4\u521b\u5efa\u4e00\u4e2a\u989d\u5916\u7684\u4e1a\u52a1\u5c42\uff0c\u5e76\u4e14\u6211\u4eec\u7684 RepositoryManager \u670d\u52a1\u5c06\u6ce8\u5165\u8be5\u4e1a\u52a1\u5c42\u4e2d\uff0c\u4ece\u800c\u5c06\u63a7\u5236\u5668\u4ece\u5b58\u50a8\u5e93\u903b\u8f91\u4e2d\u89e3\u653e\u51fa\u6765\u3002<\/p>\n<p>Now, we can continue towards handling Get requests in our application.<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u7ee7\u7eed\u5904\u7406\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684 Get \u8bf7\u6c42\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>3 DATABASE MODEL AND REPOSITORY PATTERN In this chapter, we are going to create a database model and transfer it to the MSSQL database by using the code first approach. So, we are going to learn how to create entities (model classes), how to work with the DbContext class, and how to use migrations to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[23],"class_list":["post-143","post","type-post","status-publish","format-standard","hentry","category-csharp","tag-ultimate-asp-net-core-web-api"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/143","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=143"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/143\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=143"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}