Entity Framework 4.3.1 Code First 連接 PostgreSQL 9.2.3 小結


在CentOS6.3上源碼編譯裝好PostgreSQL9.2.3,下一步嘗試將數據庫從Microsoft SQL Server 2000遷移到PostgreSQL,並且嘗試使用Entity Framework 4.3.1 Code First 連接 PostgreSQL 9.2.3,使它們協同工作。

微軟的EF Code First Database Migration原生支持SQL Server 2008以后版本,這個沒辦法,PostgreSQL上建數據表索引什么的,都要自己寫SQL語句。采用開源的Npgsql庫作為Data Provider,下載地址:http://npgsql.projects.pgfoundry.org,目前版本2.0.12。

首先在項目中引用Npgsql.dll,然后在web.config或app.config中添加下面的代碼:

<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=4.3.1.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </configSections>
  <system.data>
    <DbProviderFactories>
      <remove invariant="Npgsql"></remove>
      <add name="Npgsql Data Provider"
           invariant="Npgsql"
           description=".Net Framework Data Provider for Postgresql Server"
           type="Npgsql.NpgsqlFactory, Npgsql, 
                 Version=2.0.12.0, Culture=neutral, 
                 PublicKeyToken=5d8b90d52f46fda7" />
    </DbProviderFactories>
  </system.data>
  <connectionStrings>
    <add name="DB"
         connectionString="Server=192.168.211.100;Port=5432;Database=kit;User Id=postgres;Password=123456;CommandTimeout=20;"
         providerName="Npgsql" />
  </connectionStrings>
</configuration>

在實例化DBContext時,使用下面的代碼阻止EF去初始化數據庫:

public class BestDbContext : DbContext
{
        public BestDbContext(string databaseName, bool isDoInitialize = false) : base(databaseName) 
        {
            if (!isDoInitialize)
            {
                Database.SetInitializer<BestDbContext>(null);
            }
        }
}

.......

//實例化DbConetxt的代碼,DB對應config文件中的connectionStrings設置
using (var db = new BestDbContext("name=DB"))
{
    Product product = new Product() { ... };
db.Products.Add(product);
db.SaveChanges();
....... }

微軟的EF會把dbo作為默認的數據庫schema,為了省事,在PostgreSQL中建立完數據庫,再創建個名字為dbo的schema,所有的數據表和其他對象都建立在這個schema下。

否則就要在定義類時使用下面的代碼,聲明類所對應的數據表和schema:

[Table("Products", Schema="public")] 
public class Product
{
    ......
}

注意,PostgreSQL要求數據庫對象名稱如果不是全小寫,就必須用雙引號括起來,這在自行編寫SQL語句時要格外注意,請參考后面的示例代碼。

對於EF中隨處都用到的ID字段,也就是自增字段,在PostgreSQL建表時要指定sequence的OWNER為對應的字段,參考下面的代碼最后一行:

CREATE SEQUENCE dbo."Products_Id_seq"
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 1;
ALTER TABLE dbo."Products_Id_seq"
  OWNER TO postgres;

CREATE TABLE dbo."Products"
(
  "Id" integer NOT NULL DEFAULT nextval('dbo."Products_Id_seq"'::regclass),
  "Name" character varying(50) NOT NULL,
  "Remark" character varying(500),
  "Sequence" integer NOT NULL,
  "IsAvailable" boolean NOT NULL,
  "CreatedBy" integer NOT NULL,
  "CreatedTime" timestamp without time zone NOT NULL,
  "UpdatedBy" integer NOT NULL,
  "UpdatedTime" timestamp without time zone NOT NULL,
  CONSTRAINT "Products_pkey" PRIMARY KEY ("Id"),
  CONSTRAINT "Products_Name_key" UNIQUE ("Name")
)
WITH (
  OIDS=FALSE
);
ALTER TABLE dbo."Products"
  OWNER TO postgres;

ALTER SEQUENCE dbo."Products_Id_seq" OWNED BY dbo."Products"."Id";

否則,EF會無法獲得新建對象的ID,報如下錯誤:"A null store-generated value was returned for a non-nullable member...",參考這個網頁:http://pgfoundry.org/forum/message.php?msg_id=1006377&group_id=1000140

EF能自動處理好前面提到的數據庫對象名必須用雙引號括起來的問題,這一點很欣慰。

關於數據類型,下面列出一部分SQL Server類型和PostgreSQL類型的對應關系:

int --> integer

nvarchar(50) --> character varying(50)

nvarchar(max) 或 ntext --> text

datatime --> timestamp without time zone

對於boolean類型,PostgreSQL不支持用0或1隱式轉換,直接在SQL語句中使用true和false即可。

接下來解決Timestamp的問題,參考了這里:http://stackoverflow.com/questions/1035980/postgresql-update-timestamp-when-row-is-updated 的文章。

為了防止並發訪問時出問題,對本項目中的Stocks庫存表,設置了Timestamp並發控制列,在使用PostgreSQL數據庫時,做以下調整:

首先,在實體類定義中定義RowVersion列,設為DateTime類型:

    public class Stock
    {
        public int Id { get; set; }

        [Required(ErrorMessageResourceName = "Generic_Required", ErrorMessageResourceType = typeof(ValidationMessage))]
        public Location Location { get; set; }
        
        [Required(ErrorMessageResourceName = "Generic_Required", ErrorMessageResourceType = typeof(ValidationMessage))]
        public Part Part { get; set; }

        public Batch Batch { get; set; }

        [Required(ErrorMessageResourceName = "Generic_Required", ErrorMessageResourceType = typeof(ValidationMessage))]
        public int Quantity { get; set; }

        [Required(ErrorMessageResourceName = "Generic_Required", ErrorMessageResourceType = typeof(ValidationMessage))]
        public int UpdatedBy { get; set; }

        [Required(ErrorMessageResourceName = "Generic_Required", ErrorMessageResourceType = typeof(ValidationMessage))]
        public DateTime UpdatedTime { get; set; }

        public DateTime RowVersion { get; set; }
    }

在DbContext的構造器中加入下面的代碼:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Stock>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    modelBuilder.Entity<Stock>().Property(p => p.RowVersion).IsConcurrencyToken().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);      
}  

對應的Stocks表的創建腳本:

CREATE SEQUENCE dbo."Stocks_Id_seq"
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 1;
ALTER TABLE dbo."Stocks_Id_seq"
  OWNER TO postgres;


CREATE TABLE dbo."Stocks"
(
  "Id" integer NOT NULL DEFAULT nextval('dbo."Stocks_Id_seq"'::regclass),
  "Batch_BatchNo" character varying(50),
  "Quantity" integer NOT NULL,
  "UpdatedBy" integer NOT NULL,
  "UpdatedTime" timestamp without time zone NOT NULL,
  "Location_Id" integer NOT NULL,
  "Part_PartNo" character varying(50) NOT NULL,
  "RowVersion" timestamp without time zone NOT NULL,
  CONSTRAINT "Stocks_pkey" PRIMARY KEY ("Id"),
  CONSTRAINT "Stocks_Batch_BatchNo_fkey" FOREIGN KEY ("Batch_BatchNo")
      REFERENCES dbo."Batches" ("BatchNo") MATCH SIMPLE
      ON UPDATE RESTRICT ON DELETE RESTRICT,
  CONSTRAINT "Stocks_Location_Id_fkey" FOREIGN KEY ("Location_Id")
      REFERENCES dbo."Locations" ("Id") MATCH SIMPLE
      ON UPDATE RESTRICT ON DELETE RESTRICT,
  CONSTRAINT "Stocks_Part_PartNo_fkey" FOREIGN KEY ("Part_PartNo")
      REFERENCES dbo."Parts" ("PartNo") MATCH SIMPLE
      ON UPDATE RESTRICT ON DELETE RESTRICT
)
WITH (
  OIDS=FALSE
);
ALTER TABLE dbo."Stocks"
  OWNER TO postgres;

ALTER SEQUENCE dbo."Stocks_Id_seq" OWNED BY dbo."Stocks"."Id";


CREATE UNIQUE INDEX "Stocks_Batch_BatchNo_Location_Id_Part_PartNo_idx"
  ON dbo."Stocks"
  USING btree
  ("Batch_BatchNo" COLLATE pg_catalog."default", "Location_Id", "Part_PartNo" COLLATE pg_catalog."default");

然后,在PostgreSQL中執行下面的腳本,建立觸發器和對應的函數:

CREATE OR REPLACE FUNCTION dbo.update_rowversion_column()
  RETURNS trigger AS
$BODY$
BEGIN
   NEW."RowVersion" = now(); 
   RETURN NEW;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION dbo.update_rowversion_column()
  OWNER TO postgres;

CREATE TRIGGER update_stocks_rowversion
  BEFORE UPDATE
  ON dbo."Stocks"
  FOR EACH ROW
  EXECUTE PROCEDURE dbo.update_rowversion_column();

至此,已知問題都解決了,下一步將關注復雜條件的查詢效率。

 

 

 


免責聲明!

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



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