20131002 EF ON THE ROAD

  前些日子吵吵着要学EF,找了好多博客,发现学着不爽。想去买书,压根也没有中文版的。实在逼急了,用新浪的资源搜了两本英文的,一本是Code First,一本是DbContext,发现巨清楚无比。从此开始了看英文原著的历程。这历程很艰辛,我有时候会想象别人问我的情景(尤其是家里长辈):“这英文的你看得懂么?” 我会很装逼的说:“压根看不懂!(这是装逼么?...)看不懂楞看!”。确实是,网上没有系统的资料的情况下,虽然看得慢点,我还是选择了它。不过这也真够慢的,这些日子明显放在看书上的时间越来越多了,但一天也就只看10多页而已,现在还频繁的要用手机配合翻译。现在Code First已经看了80多页了。

  其实看书看得多了,还有个原因是因为IPAD的感觉实在好。我惊喜的发现我买的书大多数都能在网上找到PDF,精挑细选一番,也会找到相对扫描得不错的。平板电脑确实是阅读学习的好工具,这种学习过程比读纸质书要普适多了,关灯,出门都行,而且看烦了这本立即切换另一本。

  说说工作的事。上个星期我并没有被分派做新项目,而是给我最腻味的那个Leader维护老项目去了。一个星期心情可谓起起伏伏,我忍住了一次次怒火,尽量自己做到最后,而且我也觉得最后结果很好。上班时间比之前还要忙,还要全神贯注,基本上没有时间去研究些什么了,都是忙里偷闲。我一次次用最后的Task的结果堵住了这家伙的嘴。下星期完成一个小活,也许就要去跟另一个Member去做新项目了,但愿如此吧。

  只有在零碎时间里,才偷偷摸摸的用测试项目验证一下自己所学的。

  第一个遇到的问题是无论我怎么配置我的model的关系, 总是不能连通数据库里已存在的表。即使简单到只有如下两个字段:

  

public class Person{
  public int Id{get;set;}
  public string Name{get;set;}
}

  我的配置大概就是使用 Fluent API 中HasKey, MaxLength一类。这么简单也不行,每次都是抛出那个 "The model backing the 'xxxxx' context has changed......balabalabala......"的异常。所以我就好奇到底EF低层机制是如何的,Code First能不能这样连通已经存在的表?

  最后查到了这个http://stackoverflow.com/questions/3600175/the-model-backing-the-database-context-has-changed-since-the-database-was-crea 里面有人引用了scottGu的一个post:http://weblogs.asp.net/scottgu/archive/2010/08/03/using-ef-code-first-with-an-existing-database.aspx#7579835

  有种如获至宝的感觉,existing database?这不就是我这种情况么?看的时候和小葵说我要睡觉,结果连文章带回复看了1小时也没睡觉,才发现原文压根没给我解惑的内容,我的情况其实是existing table...然后下面有EF Team的Jeff和其他网友所提到的各种解释和办法。COPY如下:

# re: Using EF “Code First” with an Existing Database

Friday, August 06, 2010 11:28 AM by Jeff

@Mark

For those who are seeing this exception:

"The model backing the 'Production' context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance."

Here is what is going on and what to do about it:

When a model is first created, we run a DatabaseInitializer to do things like create the database if it's not there or add seed data. The default DatabaseInitializer tries to compare the database schema needed to use the model with a hash of the schema stored in an EdmMetadata table that is created with a database (when Code First is the one creating the database). Existing databases won’t have the EdmMetadata table and so won’t have the hash…and the implementation today will throw if that table is missing. We'll work on changing this behavior before we ship the fial version since it is the default. Until then, existing databases do not generally need any database initializer so it can be turned off for your context type by calling:

Database.SetInitializer<Production>(null);

Jeff

Database.SetInitializer<Production>(null); 是Jeff自己推荐的方案,适合用在一个类似Application_Start的地方,全局在启动时设置一次即可。实际上我还看到了另外两种方案(返回头看我的第一个链接):

public class AddressBook: DbContext
{
   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
    modelBuilder.IncludeMetadataInDatabase = false;
   }
}

  和

modelBuilder.Conventions.Remove<IncludeMetadataConvention>();

  第二种在dudu的博客中也有出现过。

  但实际上这两种在第一个链接的下面回复中已经被许多人否定了,大概就是no longer works了。我还想搜搜EdmMetaTabel到底是什么,是不是在master数据库中?到底存的是什么?结果搜到了一篇我正在看的书的翻译,第七章就要讲这个了。那我就不着急了,暂时搁一边。

 

  至此我终于能正常迈出学习EF的第一步了。在我读书,看博文,进行思考时我发现现在所做的并不是简单的学一个ORM的Framework而已。我在慢慢适应DDD的思维,规范如何去设计一系列类和关系的思考方式,甚至有时候还要重新思考一下数据库的设计。

  

public class Site{
  public int SiteID{get;set;}
  public string SiteName{get;set;}
}

public class User{
  public int UserID{get;set;}
  public string UserName{get;set;}
  public int SiteID{get;set;}
}

  这是最简单的一个例子,也是我现在工作中系统一致的写法。就比如USPS中有许多Site,每个用户只能对应一个Site。这是一个典型的一对多的,不是聚合而是组合的关系。相信不少人遇到这种情况都会这么设计类。

  但在书中却更多的这样写:

public class Site{
  public int SiteID{get;set;}
  public string SiteName{get;set;}
}

public class User{
  public int UserID{get;set;}
  public string UserName{get;set;}
  public Site Site{get;set;}
}

  User类中的Site属性被称作Navigation Property。两种方法孰优孰略一定会有争论,而且这种争论一定看上去更多像是扯淡...但我下一步EF的学习,就是要采用后者的风格,尝试一下其优劣,把Conductor的数据访问层修改了。

  另外在看dudu 2011年介绍EF的关系的文章时,对其表设计有些疑问(只看了第一篇)http://www.cnblogs.com/dudu/archive/2011/07/07/entity_framework_one_to_one.html 。我觉得这种对象设计算不得是一对一,但下面回复没有人提出异议。只是从DB的角度BlogSite用了BlogUser的主键作为主键,符合我们平时那种所谓的“分表”的一贯的设计。但从Domain Model的角度呢?单看两个类的写法,更像是一种1对多的关系。如果在BlogUser中同时加上BlogSite会不会好些(Naviagtion或是外键)? 可能有人觉得这样就多增加了依赖,同时也循环引用了呢?但我确实看到EF many to many关系中两个Model存在着相互依赖,循环引用的写法。也许在领域设计,在ORM中就暂时不应该考虑这些?

  以上只是我的胡思乱想,等着下一步学习为我解惑吧。

原文地址:https://www.cnblogs.com/apodemakeles/p/3349161.html