Entity Framework 5.0系列之自動生成Code First代碼


在前面的文章中我們提到Entity Framework的“Code First”模式也同樣可以基於現有數據庫進行開發。今天就讓我們一起看一下使用Entity Framework Power Tools如何基於現有數據庫生成數據類和數據庫上下等。

Entity Framework Power Tools

基於現有數據庫生成POCO數據類和數據庫上下文需要借助Visual Studio一個擴展插件-- Entity Framework Power Tools(一個Code First反向工程工具)。只要在Visual Studio擴展里面輸入“Entity Framework Power”搜索即可找到最新的擴展,點擊下載即可(如下圖)。當然你也可以到這里Entity Framework Power Tools Beta 3下載安裝包進行安裝。

安裝完之后只要在項目上右鍵選擇Entity Framework->Reverse Engineer Code First(項目中首先需要安裝Entity Framework 包,否則會有錯誤),然后在彈出的窗口中輸入相關的數據庫連接信息即可(我們這里使用“AdventureWorks”數據庫)。

note

注意:如果使用“AdventureWorks”數據庫,可能發生如下錯誤:

One or more errors occurred while loading schema information.

error 6004: The table 'AdventureWorks.Production.Document' is referenced by a relationship, but cannot be found.

這是由於EF目前還不支持SQL Server 2008中新增的“Hierarchyid”數據類型,目前只有先不用該類型的數據了,暫時將它改為其他類型。

然后我們稍等片刻,可以看到Entity Framework Power Tools已經根據所選數據庫自動為你生成了數據類、數據庫上下文操作類、對應的映射配置類並添加了數據庫連接配置。

我們先看一下配置文件,可以看到在配置文件中Entity Framework Power Tools已經自動對數據庫連接串進行了配置,添加了名為“AdventureWorksContext”的數據庫連接串:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="AdventureWorksContext" connectionString="Data Source=.\SQL2008;Initial Catalog=AdventureWorks;Persist Security Info=True;User ID=sa;Password=123;MultipleActiveResultSets=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
  </entityFramework>
</configuration>

與此同時,對生成的數據庫上下文操作類“AdventureWorksContext”進行了配置,在構造函數中使用了上面配置的“AdventureWorksContext”數據庫連接:

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using EFPowerTools.Models.Mapping;

namespace EFPowerTools.Models
{
    public partial class AdventureWorksContext : DbContext
    {
        static AdventureWorksContext()
        {
            Database.SetInitializer<AdventureWorksContext>(null);
        }

        public AdventureWorksContext()
            : base("Name=AdventureWorksContext")
        {
        }

        public DbSet<AWBuildVersion> AWBuildVersions { get; set; }
        public DbSet<DatabaseLog> DatabaseLogs { get; set; }
        public DbSet<ErrorLog> ErrorLogs { get; set; }
        public DbSet<OrderDetail> OrderDetails { get; set; }
        public DbSet<Order> Orders { get; set; }
        public DbSet<Department> Departments { get; set; }
        public DbSet<Employee> Employees { get; set; }
        public DbSet<EmployeeDepartmentHistory> EmployeeDepartmentHistories { get; set; }
        public DbSet<EmployeePayHistory> EmployeePayHistories { get; set; }
        public DbSet<JobCandidate> JobCandidates { get; set; }
        public DbSet<Shift> Shifts { get; set; }
        public DbSet<Address> Addresses { get; set; }
        public DbSet<AddressType> AddressTypes { get; set; }
        public DbSet<BusinessEntity> BusinessEntities { get; set; }
        public DbSet<BusinessEntityAddress> BusinessEntityAddresses { get; set; }
        public DbSet<BusinessEntityContact> BusinessEntityContacts { get; set; }
        public DbSet<ContactType> ContactTypes { get; set; }
        public DbSet<CountryRegion> CountryRegions { get; set; }
        public DbSet<EmailAddress> EmailAddresses { get; set; }
        public DbSet<Password> Passwords { get; set; }
        public DbSet<Person> People { get; set; }
        public DbSet<PersonPhone> PersonPhones { get; set; }
        public DbSet<PhoneNumberType> PhoneNumberTypes { get; set; }
        public DbSet<StateProvince> StateProvinces { get; set; }
        public DbSet<BillOfMaterial> BillOfMaterials { get; set; }
        public DbSet<Culture> Cultures { get; set; }
        public DbSet<Illustration> Illustrations { get; set; }
        public DbSet<Location> Locations { get; set; }
        public DbSet<Product> Products { get; set; }
        public DbSet<ProductCategory> ProductCategories { get; set; }
        public DbSet<ProductCostHistory> ProductCostHistories { get; set; }
        public DbSet<ProductDescription> ProductDescriptions { get; set; }
        public DbSet<ProductInventory> ProductInventories { get; set; }
        public DbSet<ProductListPriceHistory> ProductListPriceHistories { get; set; }
        public DbSet<ProductModel> ProductModels { get; set; }
        public DbSet<ProductModelIllustration> ProductModelIllustrations { get; set; }
        public DbSet<ProductModelProductDescriptionCulture> ProductModelProductDescriptionCultures { get; set; }
        public DbSet<ProductPhoto> ProductPhotoes { get; set; }
        public DbSet<ProductProductPhoto> ProductProductPhotoes { get; set; }
        public DbSet<ProductReview> ProductReviews { get; set; }
        public DbSet<ProductSubcategory> ProductSubcategories { get; set; }
        public DbSet<ScrapReason> ScrapReasons { get; set; }
        public DbSet<TransactionHistory> TransactionHistories { get; set; }
        public DbSet<TransactionHistoryArchive> TransactionHistoryArchives { get; set; }
        public DbSet<UnitMeasure> UnitMeasures { get; set; }
        public DbSet<WorkOrder> WorkOrders { get; set; }
        public DbSet<WorkOrderRouting> WorkOrderRoutings { get; set; }
        public DbSet<ProductVendor> ProductVendors { get; set; }
        public DbSet<PurchaseOrderDetail> PurchaseOrderDetails { get; set; }
        public DbSet<PurchaseOrderHeader> PurchaseOrderHeaders { get; set; }
        public DbSet<ShipMethod> ShipMethods { get; set; }
        public DbSet<Vendor> Vendors { get; set; }
        public DbSet<CountryRegionCurrency> CountryRegionCurrencies { get; set; }
        public DbSet<CreditCard> CreditCards { get; set; }
        public DbSet<Currency> Currencies { get; set; }
        public DbSet<CurrencyRate> CurrencyRates { get; set; }
        public DbSet<Customer> Customers { get; set; }
        public DbSet<PersonCreditCard> PersonCreditCards { get; set; }
        public DbSet<SalesOrderDetail> SalesOrderDetails { get; set; }
        public DbSet<SalesOrderHeader> SalesOrderHeaders { get; set; }
        public DbSet<SalesOrderHeaderSalesReason> SalesOrderHeaderSalesReasons { get; set; }
        public DbSet<SalesPerson> SalesPersons { get; set; }
        public DbSet<SalesPersonQuotaHistory> SalesPersonQuotaHistories { get; set; }
        public DbSet<SalesReason> SalesReasons { get; set; }
        public DbSet<SalesTaxRate> SalesTaxRates { get; set; }
        public DbSet<SalesTerritory> SalesTerritories { get; set; }
        public DbSet<SalesTerritoryHistory> SalesTerritoryHistories { get; set; }
        public DbSet<ShoppingCartItem> ShoppingCartItems { get; set; }
        public DbSet<SpecialOffer> SpecialOffers { get; set; }
        public DbSet<SpecialOfferProduct> SpecialOfferProducts { get; set; }
        public DbSet<Store> Stores { get; set; }
        public DbSet<vEmployee> vEmployees { get; set; }
        public DbSet<vEmployeeDepartment> vEmployeeDepartments { get; set; }
        public DbSet<vEmployeeDepartmentHistory> vEmployeeDepartmentHistories { get; set; }
        public DbSet<vJobCandidate> vJobCandidates { get; set; }
        public DbSet<vJobCandidateEducation> vJobCandidateEducations { get; set; }
        public DbSet<vJobCandidateEmployment> vJobCandidateEmployments { get; set; }
        public DbSet<vAdditionalContactInfo> vAdditionalContactInfoes { get; set; }
        public DbSet<vStateProvinceCountryRegion> vStateProvinceCountryRegions { get; set; }
        public DbSet<vProductAndDescription> vProductAndDescriptions { get; set; }
        public DbSet<vProductModelCatalogDescription> vProductModelCatalogDescriptions { get; set; }
        public DbSet<vProductModelInstruction> vProductModelInstructions { get; set; }
        public DbSet<vVendorWithAddress> vVendorWithAddresses { get; set; }
        public DbSet<vVendorWithContact> vVendorWithContacts { get; set; }
        public DbSet<vIndividualCustomer> vIndividualCustomers { get; set; }
        public DbSet<vPersonDemographic> vPersonDemographics { get; set; }
        public DbSet<vSalesPerson> vSalesPersons { get; set; }
        public DbSet<vSalesPersonSalesByFiscalYear> vSalesPersonSalesByFiscalYears { get; set; }
        public DbSet<vStoreWithAddress> vStoreWithAddresses { get; set; }
        public DbSet<vStoreWithContact> vStoreWithContacts { get; set; }
        public DbSet<vStoreWithDemographic> vStoreWithDemographics { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new AWBuildVersionMap());
            modelBuilder.Configurations.Add(new DatabaseLogMap());
            modelBuilder.Configurations.Add(new ErrorLogMap());
            modelBuilder.Configurations.Add(new OrderDetailMap());
            modelBuilder.Configurations.Add(new OrderMap());
            modelBuilder.Configurations.Add(new DepartmentMap());
            modelBuilder.Configurations.Add(new EmployeeMap());
            modelBuilder.Configurations.Add(new EmployeeDepartmentHistoryMap());
            modelBuilder.Configurations.Add(new EmployeePayHistoryMap());
            modelBuilder.Configurations.Add(new JobCandidateMap());
            modelBuilder.Configurations.Add(new ShiftMap());
            modelBuilder.Configurations.Add(new AddressMap());
            modelBuilder.Configurations.Add(new AddressTypeMap());
            modelBuilder.Configurations.Add(new BusinessEntityMap());
            modelBuilder.Configurations.Add(new BusinessEntityAddressMap());
            modelBuilder.Configurations.Add(new BusinessEntityContactMap());
            modelBuilder.Configurations.Add(new ContactTypeMap());
            modelBuilder.Configurations.Add(new CountryRegionMap());
            modelBuilder.Configurations.Add(new EmailAddressMap());
            modelBuilder.Configurations.Add(new PasswordMap());
            modelBuilder.Configurations.Add(new PersonMap());
            modelBuilder.Configurations.Add(new PersonPhoneMap());
            modelBuilder.Configurations.Add(new PhoneNumberTypeMap());
            modelBuilder.Configurations.Add(new StateProvinceMap());
            modelBuilder.Configurations.Add(new BillOfMaterialMap());
            modelBuilder.Configurations.Add(new CultureMap());
            modelBuilder.Configurations.Add(new IllustrationMap());
            modelBuilder.Configurations.Add(new LocationMap());
            modelBuilder.Configurations.Add(new ProductMap());
            modelBuilder.Configurations.Add(new ProductCategoryMap());
            modelBuilder.Configurations.Add(new ProductCostHistoryMap());
            modelBuilder.Configurations.Add(new ProductDescriptionMap());
            modelBuilder.Configurations.Add(new ProductInventoryMap());
            modelBuilder.Configurations.Add(new ProductListPriceHistoryMap());
            modelBuilder.Configurations.Add(new ProductModelMap());
            modelBuilder.Configurations.Add(new ProductModelIllustrationMap());
            modelBuilder.Configurations.Add(new ProductModelProductDescriptionCultureMap());
            modelBuilder.Configurations.Add(new ProductPhotoMap());
            modelBuilder.Configurations.Add(new ProductProductPhotoMap());
            modelBuilder.Configurations.Add(new ProductReviewMap());
            modelBuilder.Configurations.Add(new ProductSubcategoryMap());
            modelBuilder.Configurations.Add(new ScrapReasonMap());
            modelBuilder.Configurations.Add(new TransactionHistoryMap());
            modelBuilder.Configurations.Add(new TransactionHistoryArchiveMap());
            modelBuilder.Configurations.Add(new UnitMeasureMap());
            modelBuilder.Configurations.Add(new WorkOrderMap());
            modelBuilder.Configurations.Add(new WorkOrderRoutingMap());
            modelBuilder.Configurations.Add(new ProductVendorMap());
            modelBuilder.Configurations.Add(new PurchaseOrderDetailMap());
            modelBuilder.Configurations.Add(new PurchaseOrderHeaderMap());
            modelBuilder.Configurations.Add(new ShipMethodMap());
            modelBuilder.Configurations.Add(new VendorMap());
            modelBuilder.Configurations.Add(new CountryRegionCurrencyMap());
            modelBuilder.Configurations.Add(new CreditCardMap());
            modelBuilder.Configurations.Add(new CurrencyMap());
            modelBuilder.Configurations.Add(new CurrencyRateMap());
            modelBuilder.Configurations.Add(new CustomerMap());
            modelBuilder.Configurations.Add(new PersonCreditCardMap());
            modelBuilder.Configurations.Add(new SalesOrderDetailMap());
            modelBuilder.Configurations.Add(new SalesOrderHeaderMap());
            modelBuilder.Configurations.Add(new SalesOrderHeaderSalesReasonMap());
            modelBuilder.Configurations.Add(new SalesPersonMap());
            modelBuilder.Configurations.Add(new SalesPersonQuotaHistoryMap());
            modelBuilder.Configurations.Add(new SalesReasonMap());
            modelBuilder.Configurations.Add(new SalesTaxRateMap());
            modelBuilder.Configurations.Add(new SalesTerritoryMap());
            modelBuilder.Configurations.Add(new SalesTerritoryHistoryMap());
            modelBuilder.Configurations.Add(new ShoppingCartItemMap());
            modelBuilder.Configurations.Add(new SpecialOfferMap());
            modelBuilder.Configurations.Add(new SpecialOfferProductMap());
            modelBuilder.Configurations.Add(new StoreMap());
            modelBuilder.Configurations.Add(new vEmployeeMap());
            modelBuilder.Configurations.Add(new vEmployeeDepartmentMap());
            modelBuilder.Configurations.Add(new vEmployeeDepartmentHistoryMap());
            modelBuilder.Configurations.Add(new vJobCandidateMap());
            modelBuilder.Configurations.Add(new vJobCandidateEducationMap());
            modelBuilder.Configurations.Add(new vJobCandidateEmploymentMap());
            modelBuilder.Configurations.Add(new vAdditionalContactInfoMap());
            modelBuilder.Configurations.Add(new vStateProvinceCountryRegionMap());
            modelBuilder.Configurations.Add(new vProductAndDescriptionMap());
            modelBuilder.Configurations.Add(new vProductModelCatalogDescriptionMap());
            modelBuilder.Configurations.Add(new vProductModelInstructionMap());
            modelBuilder.Configurations.Add(new vVendorWithAddressMap());
            modelBuilder.Configurations.Add(new vVendorWithContactMap());
            modelBuilder.Configurations.Add(new vIndividualCustomerMap());
            modelBuilder.Configurations.Add(new vPersonDemographicMap());
            modelBuilder.Configurations.Add(new vSalesPersonMap());
            modelBuilder.Configurations.Add(new vSalesPersonSalesByFiscalYearMap());
            modelBuilder.Configurations.Add(new vStoreWithAddressMap());
            modelBuilder.Configurations.Add(new vStoreWithContactMap());
            modelBuilder.Configurations.Add(new vStoreWithDemographicMap());
        }
    }
}

 

在項目中我們還可以看到Entity Framework Power Tools自動創建了一個Models文件夾,這里除了“AdventureWorksContext”類還有所有的數據類。在Models文件夾下還有一個“Mapping”文件夾,這里放了數據類與數據庫的映射配置類,可以看出Entity Framework Power Tools通過fluent API的方式進行映射細節配置(目前Entity Framework Power Tools還不支持Data Annotations方式),關於配置類的具體細節我們這里暫不進行詳細介紹。

下面我們進行數據查詢:

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

namespace EFPowerTools
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new AdventureWorksContext())
            {
                var persons = db.People.Where(p => p.LastName == "Stevens").OrderBy(p=>p.FirstName);
                foreach(var p in persons)
                {
                    Console.WriteLine("FirstName:{0},LastName:{1}", p.FirstName, p.LastName);
                }
            }
        }
    }
}

查詢結果:

自定義模板

事實上Entity Framework Power Tools的功能還遠不止這些,例如我們上面談到Entity Framework Power Tools使用fluent API進行映射配置,而通常情況下開發人員更喜歡使用Data Annotations方式進行映射約定,那么此時你就可以選擇配置自己的模板。

在項目上右鍵選擇Entity Frmaework->Customize Reverse Engineer Templates,此時會看到項目中添加了一個“CodeTemplates”文件夾,里面存放了Entity Framework Power Tools用於生產相關類文件的T4模板(對於不了解T4模板引擎的朋友可以點擊這里Lowering the Barriers to Code Generation with T4),如果此時再使用Entity Framework Power Tools進行Code First反向工程操作將使用當前這個文件夾中的模板進行生成。接下來看一下如何進行模板自定義。

首先讓我們打開Mapping.tt文件,這個文件主要用戶生成配置類,在Entity Framework Power Tools默認生成的代碼中通過fluent API進行表名和數據類名以及屬性名同列名的對應配置,例如下面的代碼:

下面讓我們修改一下Mapping.tt文件,去掉上面的約束,而改用Data Annotations方式進行約定。首先去掉下圖中選擇的代碼,此時生成的映射類中就不再出現上圖中選擇的代碼部分:

然后我們需要修改Entity.tt模板,將數據庫中的表和列的映射聲明到數據類上。先讓我們將數據類和表做對應,這需要在數據類上添加“Table”聲明,標記出表名和架構名(EF默認會使用dbo架構),在如圖位置添加如下代碼:

<# 
        var tableName = (string)efHost.TableSet.MetadataProperties["Table"].Value ?? efHost.TableSet.Name; 
        var conventionTableName = System.Data.Entity.Design.PluralizationServices.PluralizationService 
                .CreateService(new CultureInfo("en")) 
                .Pluralize(efHost.EntityType.Name);

        var schemaName = (string)efHost.TableSet.MetadataProperties["Schema"].Value; 
        schemaName = string.IsNullOrWhiteSpace(schemaName) 
                ? "dbo" 
                : schemaName;

        if(schemaName != "dbo" || conventionTableName != tableName) 
        { 
#> 
    [Table("<#= tableName #>", Schema="<#= schemaName #>")] 
<# 
        }

#> 

這段代碼主要處理邏輯是:當架構不是“dbo”或者表名的復數形式同表名不同時添加“Table”標記聲明。之所以添加判斷是因為EF的默認使用“dbo”架構並且EF在操作表的時候默認將數據類的復數形式作為表名。

接下來添加列標記聲明,在如圖位置添加如下代碼即可:

      var columnName = efHost.PropertyToColumnMappings[property].Name; 
                if(code.Escape(property) != columnName) 
                { 
#> 
        [Column("<#= columnName #>")] 
<# 
                } 

這里只是簡單的對列名和編碼后的列名進行比較,不同則添加列標記,否則不添加(EF默認認為數據類屬性名和表的列名相同)。

由於剛才添加的標記屬性類在“System.ComponentModel.DataAnnotations.Schema” 命名空間下,因此最后還需要在模板頭部添加該命名空間:

 

<# 
       if (efHost.EntityFrameworkVersion >= new Version(4, 4)) 
        { 
#> 
using System.ComponentModel.DataAnnotations.Schema; 
<# 
        } 
        else 
        { 
#> 
using System.ComponentModel.DataAnnotations; 
<# 
        } 
#>

由於之前的EF版本中“Table”和“Column”類不在“System.ComponentModel.DataAnnotations.Schema”命名空間而是在 “System.ComponentModel.DataAnnotations”命名空間中,因此這里我們還需要添加一個判斷。

今天的內容先到此為止,關於如何使用fluent API及Data Annotations定義配置類來進行更多的映射控制請關注后面的文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM