以前有學過EF一段時間那時EF才4.0彷佛還不太穩定,而如今EF都已7.0版本,同時AspNet Identity都與此大有關聯,看來是大勢所趨因而開始學習EF,在學EF過程當中也遇到一些小問題,特此錄下,以備忘!html
爲了更好的按部就班稍微歸納下典型建立EF Code First過程(看之即懂,懂即略過)sql
第一步先定義兩個類,以下:數據庫
public class Student { public Student() { } public int StudentID { get; set; } public string StudentName { get; set; } } public class Standard { public Standard() { } public int StandardId { get; set; } public string StandardName { get; set; } }
第二步:繼承EF上下文DbContextjson
public class SchoolContext : DbContext { public SchoolContext():base("name=DBConnectionString") { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); } }
學生上下文中構造函數中的name去讀取以下配置文件來命名數據庫名稱: DBByConnectionString 服務器
<add name="DBConnectionString" connectionString="Data Source=.;Initial Catalog=DBByConnectionString;Integrated Security=true" providerName="System.Data.SqlClient"/> </connectionStrings>
而後在控制檯中經過EF上下文添加數據並保存,以下:網絡
using (var ctx = new SchoolContext()) { Student stud = new Student() { StudentName = "New Student" }; ctx.Students.Add(stud); ctx.SaveChanges(); }
最終生成數據庫以及表以下圖:ide
上述建立數據庫的過程只需注意:能夠手動經過添加構造函數的name來命名數據庫名稱或者無需添加name那麼生成的數據庫名稱是以上下文中的命名空間+上下文類來命名數據庫名稱。
函數
下面建立方法是看過園友hystar(EF教程)而寫的,確實是好方法,就搬過來了,爲何說一勞永逸呢?不明白的話,能夠去看看他的文章!首先添加兩個類Student(學生類)和Course(課程類)。學習
public class Student { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public virtual Course Course { get; set; } } public class Course { public int StudentID { get; set; } public string Name { get; set; } public virtual Student Student { get; set; } }
添加EFDbContext類並繼承DbContext上下文,代碼以下:ui
public class EntityDbContext : DbContext { public EntityDbContext() : base("name=test2") { } /// <summary> /// 經過反射一次性將表進行映射 /// </summary> /// <param name="modelBuilder"></param> protected override void OnModelCreating(DbModelBuilder modelBuilder) { var typesRegister = Assembly.GetExecutingAssembly().GetTypes() .Where(type => !(string.IsNullOrEmpty(type.Namespace))).Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); foreach (var type in typesRegister) { dynamic configurationInstance = Activator.CreateInstance(type); modelBuilder.Configurations.Add(configurationInstance); } } }
因爲是手動命名數據庫名稱,固然得讀取配置文件
<connectionStrings> <add name="test2" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=test2;Integrated Security=true" providerName="System.Data.SqlClient"/> </connectionStrings>
上述配置要添加的數據庫創建在VS2013自帶的實例中!咱們首先初始化數據庫看看:
EntityDbContext ctx = new EntityDbContext();
結果運行就出現以下經典錯誤:
在與SQLServer創建鏈接時出現與網絡相關的或特定與實例的錯誤.未找到或沒法訪問服務器.請驗證明例名稱是否正確而且SQL SERVER已配置容許遠程連接provide:命名管道提供程序,error:40 -沒法打開到SQL Server的鏈接)
那確定是沒法鏈接到 (localdb)\v11.0 ,因而當我在服務器打開添加鏈接中添加服務器名爲 (localdb)\v11.0 時也是沒法響應,鏈接不到!最終經過SqlLocalDB命令在Command Prompt(命令行)中輸入
SqlLocalDB.exe start v11.0
啓動該實例纔算完事,主要緣由是安裝了SQL 2012默認啓動的實例該SQL 2012而VS 2013中的實例被中止運行得手動啓動,若是要查看其信息來查看是否已經啓動,經過如下命令便可:
SqlLocalDB.exe info v11.0
VS2013中默認的實例應該是(localdb)\v11.0,若是在服務器中添加鏈接輸入(localdb)\v11.0是錯誤的,你能夠經過上述 SqlLocalDB.exe info v11.0 命令複製並添加如圖的字符串便可
彷佛只要第一次啓動了,之後每次都會鏈接上,不會再出現如上問題!
上述中咱們對於EF上下文不用每次都初始化數據庫,在EF中初始化數據庫有三種策略:
鑑於此咱們在EFDbContext中採用第二種策略。建立一個初始化類的策略 EFDbContextInit
/// <summary> /// 當對象實體對象發生改變時重生建立數據庫 /// </summary> public class EntityDbContextInit : DropCreateDatabaseIfModelChanges<EntityDbContext> { protected override void Seed(EntityDbContext context) { base.Seed(context); } }
在EFDbContext靜態構造函數中進行初始化此方法:
static EntityDbContext() { Database.SetInitializer<EntityDbContext>(new EntityDbContextInit()); }
自此EFDbContext構建完畢!下面就是模型映射了,咱們假設學生和課程是1:1關係,則咱們添加的兩個實體映射以下:
StudentMap(學生類實體映射)
public class StudentMap : EntityTypeConfiguration<Student> { public StudentMap() { ToTable("Student"); HasKey(d => d.ID); //HasRequired(p => p.Course).WithRequiredDependent(i => i.Student); //HasRequired(p => p.Course).WithOptional(i => i.Student); HasRequired(p => p.Course).WithRequiredPrincipal(p => p.Student); HasOptional(p => p.Course).WithRequired(p => p.Student);
/*
對於上述映射關係不太理解的話能夠去上述給出連接文章。我只說明怎麼去很好的理解這兩組的意思,第一組 WithRequiredDependent 和第二組
WithRequiredPrincipal 一個是Dependent是依賴的意思說明後面緊接着的Student是依賴對象,而前面的Course是主體,而Principal
首先的意思,說明後面緊接着的是Student是主體,而Course是依賴對象。很顯然在這個關係中課程是依賴學生的。因此映射選第二組
*/
}
}
CourseMap(課程類映射)
public class CourseMap : EntityTypeConfiguration<Course> { public CourseMap() { ToTable("Course"); HasKey(p => p.StudentID); } }
接下來咱們進行添加數據並保存經過以下代碼:
EntityDbContext ctx = new EntityDbContext(); var s = new Student() { Name = "1", Age = 12, Course = new Course() { Name = "12" } }; ctx.Set<Student>().Add(s); ctx.SaveChanges();
數據添加和保存都已經過,接下來進行查詢數據,查詢數據有兩種方式:
如要得到上述學生數據列表集合,能夠經過以下操做:
EntityDbContext ctx = new EntityDbContext(); var list = ctx.Set<Student>().ToList(); 或者 SqlParameter[] parameter = { }; var list = ctx.Database.SqlQuery<Student>("select * from student", parameter);
因而我監視下返回的list集合中的數據類型,如圖
oh,shit!和咱們實際的實體類型不符,經過EF產生的倒是 DynamicProxies ,因而到Sytem.Data.Entity類下去看看是個什麼類型,竟然沒找到,估計看這單詞意思就是運行時產生的動態代理對象。那麼,你以爲是否是沒什麼影響了???那影響可大了,請看下面操做:
var list = ctx.Set<Student>().ToList(); var jsonString = JsonConvert.SerializeObject(list);
我嘗試將其序列化看看結果,一運行,oh,no!錯誤以下:
這意思是檢測到在Course裏面有Student屬性,而Student類裏又有Course這就至關於本身引用本身,致使了循環引用就成了死循環。(這就是由於 DynamicProxies 致使的結果)因此當前要將其代理對象轉換爲咱們的實體對象便可。
則經過Select()方法投影將其代碼進行改造後以下:
var list = ctx.Set<Student>().Include(p => p.Course).ToList().Select(entity => new Student() { ID = entity.ID, Name = entity.Name, Age = entity.Age }).ToList(); 或者 var list = ctx.Set<Student>().Include("Course").ToList().Select(entity => new Student() { ID = entity.ID, Name = entity.Name, Age = entity.Age }).ToList();
對象轉換成功,以下:
序列化成功結果以下:
【注意】你用EF得到數據集合後得 ToList() 由於此時集合對象爲代理對象,不然進行轉換將報錯,代碼以下:
var list = ctx.Set<Student>().Include(p => p.Course).Select(entity => new Student() { ID = entity.ID, Name = entity.Name, Age = entity.Age }).ToList();
報錯以下:
上述轉換也叫DTO(Data Transfer Objects)數據轉換爲對象,像這種狀況在EF中很常見。下面給出老外的用兩張圖在兩個常見的場景中來展示關於DTO的概念:
Getting Information: DAL=>BLL=>GUI
Insert Information: GUI=>BLL=>DAL
(1)當安裝了sql時則默認啓動的是此實例,那麼VS中的實例則會中止啓動,須要經過SqlLocalDB命令進行啓動。
(2)經過EF得到的數據集合對象爲代理對象,須要先轉換爲實體對象才能進行序列化或者其餘操做。
在此感謝園友中華小鷹,經其提示用上述一勞永逸配置沒法配置複雜類型!
modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly());
經過上述代碼既能配置實體類型也能配置複雜類型,用此方法更加精簡!固然若你將複雜類型做爲另外一個類的導航屬性時上述代碼也是能夠知足所需的!