使用json数据和ef核心为数据库种子

我已经习惯了使用标准代码(C和EFAPI)让EF帮助我创建数据库。你知道的,实例化一个对象,填充其字段,可能向相关列表添加对象。然后将整个工具包添加到DbContext并调用SaveChanges。

我给杰弗里·格罗森巴赫展示了一些英孚核心代码,当时我们正在谈论在我放松下来做一个更严肃的课程之前,在英孚核心上一个一个接一个的游戏来实现多元化。杰弗里查看了所有用于构建我的对象以种子数据库的代码,并说“哇,这是很多代码。你就不能用JSON之类的吗?”(注意:我已经厌倦了在WordPress中对代码进行漂亮的格式化,但你明白我的意思吧?)

private static list buildWeatherEvents()var events=new list weatherEvent.create(datetime.now,weatherType.sun,新建列表
         
          {new []{"Julie","Oh so sunny!"}}),weatherEvent.create(datetime.now.adddays(-2),weatherType.rain),weatherEvent.create(datetime.now.adddays(-3),weatherType.sun,新建列表
          
           {“朱莉”,“哦,可爱的夏日阳光!”太糟糕了,我在电脑上。新的[]“佛蒙特州的每个人”,“万岁,我们去玩吧!”}新[]{“桑普森”,“我想去游泳,请!”},}WeatherType.Cloudy WeatherEvent.Create (DateTime.Now.AddDays (4)),WeatherType.Rain WeatherEvent.Create (DateTime.Now.AddDays (5)),WeatherEvent.Create (DateTime.Now.AddDays (6)、WeatherType.Sun)};var lastevent=weatherevent.create(datetime.now.adddays(-1),WeatherType.Snow,新建列表
           
            {new[] {"Julie",“雪?在七月?好吧,这对VT来说也是荒谬的!”});lastEvent.Reactions.FirstOrDefault().Comments.Add       (new Comment { Text = "Get over it,朱莉!“(});events.Add (lastEvent);返回事件;}
           
          
         

哦,那该多漂亮啊。下面是我用EF Core做的,但是你也可以用同样的概念来做EF6。

我的域是weatherevent,它是我的efcore演示中的域,位于https://github.com/julielerman/efcore-aspnetcore-webapi-rtm,(我还没有更新它来演示如何使用JSON数据)。

这是我存储在一个名为weatherdataset.json的文件中的JSON。

[{"date": "2016-07-27T00:00:00",“time”:“22:09:13.8216230”,“类型”:5,“reactions”:[“name”:“朱莉”,“引述”:“哦,阳光明媚!”,“评论”:[]},“mostCommonWord”:零},“日期”:“2016-07-25t00:00:00”,“时间”:“22:09:13.8237230”,“类型”:1、“反应”:[],“mostCommonWord”:零},{“日期”:“2016 - 07 - 24 t00:00:00”,“time”:“22:09:13.8238740”,“类型”:5,“reactions”:[“name”:“朱莉”,引文:“哦,可爱的夏日阳光!”太糟糕了,我在电脑上。“comments”:[],“name”:“佛蒙特州的每个人”,“引号”:“万岁,我们去玩吧!”,“comments”:[],“name”:“sampson”,引文:“我想去游泳,请!”“评论”:[]},“mostCommonWord”:零},“日期”:“2016-07-23t00:00:00”,“time”:“22:09:13.8239130”,“类型”:6,“反应”:[],“mostCommonWord”:零},“日期”:“2016-07-22t00:00:00”,“time”:“22:09:13.8239210”,“类型”:1、“反应”:[],“mostCommonWord”:零},“日期”:“2016-07-21t00:00:00”,“时间”:“22:09:13.8239290”,“类型”:5,“反应”:[],“mostCommonWord”:零},{“日期”:“2016 - 07 - 26 t00:00:00”,“时间”:“22:09:13.8239360”,“类型”:2“reactions”:[“name”:“朱莉”,“引用”:“雪吗?在七月?好吧,这对VT来说也是荒谬的!”“comments”:[“text”:“克服它,朱莉!“}]}],请“最常用词”:null}]

所以这不仅仅是数据,但是具有三个层次关系的层次数据。用json表示它要简单得多、漂亮得多可读的而不是用c#构建所有这些,创建对象,等。

现在当然是魔法的时候了json.net网站这样就可以将这些数据简化为ef。

这是我用来从使用ef的JSON中为数据库种子的完整代码。

I'm calling it from startup.cs in an ASP.NET Core Web API inside the Configure method.这里我只是用system.io.file.readalltext读取文件,然后将该文本传递到seedit方法中。还要注意我在其中设置dbContext的配置服务及其所需的连接字符串。

注意:这篇文章中的代码有一些变动肖恩Wildermuth我和戴夫·福勒在Twitter上学习过,aspnet团队的核心领导者之一。我根据肖恩的一个伟大建议修改了我的原始代码以简化它,但是Dave指出了其中的一些范围问题。所以现在样本又回到了我原来的版本,其中seedit方法负责创建一个新的ServiceScope并实例化上下文。

public void配置服务(IServiceCollection Services)services.adddbContext
         
          (options=>options.usenpgsql(configuration[“data:postgreconnection:connectionString”]);services.AddMvc ();}公共void配置(IApplicationBuilder应用程序,环境卫生部,iloggerFactory)loggerFactory.addconsole(configuration.getsection(“logging”));loggerFactory.AddDebug ();app.UseMvc ();var dataText = System.IO.File.ReadAllText (@“weatherdataseed.json”);Seeder.Seedit (dataText app.ApplicationServices);
          }
         

这是Seedit方法,它使用json.net将json反序列化为weatherEvent对象列表。契约序列化程序将克服WeatherEvent类中的私有setter。我从Daniel Wertheim的Github回购.Then I use the ASP.NET Core ServiceProvider to get service I set up in startup which will instantiate a WeatherContext along with the connection string specified in startup.

使用制度;使用来;使用System.Collections.Generic;使用Microsoft.Extensions.DependencyInjection;使用efcorewebapi;使用newtonsoft.json;使用JsonNet.PrivateSettersContractResolvers;公共静态类seeder公共静态void seedit(string jsondata,{ContractResolver = new PrivateSetterContractResolver()};清单
         
          事件= JsonConvert.DeserializeObject
          
           > (jsonData设置);使用(var serviceScope = serviceProvider .GetRequiredService
           
            ().createScope())var context=serviceScope.serviceProvider.getService
            
             ();if (!context.WeatherEvents.Any()) {context.AddRange(events);context.saveChanges();}}}}
            
           
          
         

在实例化上下文之后,我用它来检查数据库中是否有任何天气事件,所以我没有重新播种。(This is logic I want for seeding during my demo so you may have different rules for the seeding.) Now I call context.AddRange passing in the list of WeatherEvent objects.我可以直接使用dbset或上下文调用addrange。最后,保存更改。

所以这里的钥匙,无论您使用的是efcore还是ef6(所以服务的使用是特定于我的应用程序是aspnet核心API这一事实)。只是读取json文件,反序列化数据并将其添加到上下文中。这比强制创建所有数据要简单得多。

它可能并不总是答案,这取决于您的数据的形状,但对于这个特定的模型和我需要的种子数据来说,它是完美的。

报名参加我的通讯所以你不要错过我的会议和多元化课程公告!

关于“使用json数据和ef核心为数据库种子

  1. 既然你在aspnet core,实际上是避免使用静态类,而仅仅依赖di来创建类和依赖项,而不需要服务提供者实例。

    1. 看起来我需要进一步了解如何使用aspnet核心DI。静态类(播种器)中没有构造函数。所以我得换个班。但是,我必须在启动时实例化播种机,然后使用服务以某种方式传递WeatherContext实例吗?我遗漏了一些东西,因为我不知道如何神奇地将上下文实例获取到播种器类,而且它看起来更复杂(因为我不知道什么)。我知道你在悉尼国家数据中心,所以不急,但有趣和好奇。

      1. 肖恩,你太棒了。谢谢你的帮助,并教我如何做到这一点。我很高兴有一个ASPNET核心专家。.

    1. 西蒙,你的犯罪伙伴在蒙特利尔向我展示了这一点。太酷了。我必须开始使用它!!

  2. 两个笔记:
    1)请记住,如果您有私人设置者,您需要使用JSONNET契约解析器。我会确定这在回购协议中,我会加上这个。我需要那个!
    2)从ASP.NET团队的David Fowler那里得到一些关于twitter的评论,并希望在我发布到Github之前确保这个解决方案让他高兴。

  3. 嗨,朱莉,自从EF最初发布以来已经发生了很大的变化,开发人员会不会觉得读一本你在英孚写的书很有用?如果是这样,你会推荐哪本书?谢谢

    1. 嗨Dom,
      我认为这本大书(编程ef第二版)对于理解ef的一些内部原理有一定的价值。dbContext&code first非常相关。其他作者还有其他较新的书。然而,我把所有的努力都放在了创造多元内容上,而不是写书。这就是为什么你能从我这里找到关于EF同样深入的信息。开始pluralsight.com/authors/julie-lerman

  4. Was great to see the journey of this solution – and I've learnt even more about DI and scopes in ASP.NET Core as a result,谢谢!

  5. 当然是作弊,但您可以使用SQL Server 2016 JSON支持。你仍然需要在web应用程序中编写所有的EF来CRUD数据库。

    创建表协定(
    圆锥内景,
    符号varchar(4…)

    插入合同
    选择合同。*
    从OPENROWSET(批量C:\Data\Contract.json'),SINGLE_CLOB j)
    交叉应用OpenJSON(BulkColumn)
    用(锥形int,
    象征varchar (4),……
    )作为合同
    WHERE NOT EXISTS(SELECT ConId FROM Contract c WHERE c.ConId = ConId)

    从合同中选择*

    1. 嗨,尼克,

      是的,作弊和老派,大声笑。它只适用于SQL Server。我刚刚在一些单元测试中使用了这种技术。很有帮助。我们正在使用PostgreSQL,因此您的建议不是一个选项。

      希望一切都好。如果你还在这个地方工作,我们应该见面喝杯啤酒。

  6. 你好,很酷的文章。

    几周前我发布了一个名为cherryseed的.NET库,您可以用它从任何源(csv,XML,小黄瓜,SpecFlow,等)到任何目的地。这是你解决问题的下一步。

    在SpecFlow中,我们还必须将许多数据植入数据库。所以我在Cherryseed周围创建了一个易于使用的包装器,针对specFlow的使用。

    如果你检查一下Cherryseed,问我你对图书馆的看法,那就太好了。

    这里是介绍文章的链接https://medium.com/@michael_altmann/why-seeding-test-data-into-database-using-cherryseed-net-c-529da467ffd9.t29hgtvlt

    文章中有一些链接到wiki,github,等。

    1. 好,是啊,这是使用英孚核心。然而,addrange在ef6中的dbset上(和ef core,)。你能用EF6吗?

      1. 可能是EF6中的IDbSet没有公开AddRange(或RemoveRange)。我不知道为什么。我必须创建一个变通方法。

  7. 嗨,朱莉。我真不敢相信你总是这么快地回复新的帖子。你们肯定有十七个人。不管怎样,是的——我创建了一个静态方法,并使用它代替context.AddRange():

    公共静态IEnumerable AddRange(
    这个idbset数据库集,
    IEnumerable EntitiesToAdd)其中Tentity:类
    {
    返回((DbSet) DbSet) .AddRange (entitiesToAdd);
    }

    谢谢你所做的一切。

    杰夫

  8. 朱莉再次感谢你。我正在实现IdentityServer4,并试图避免使用服务定位器模式。(http://odetocode.com/blogs/scott/archive/2016/02/18/avoiding-the-service-locator-pattern-in-asp-net-core.aspx)

    所以“GetService”是我要检查的代码气味。我现在明白了,在应用程序级别上,获得一个有作用域的项实际上是一个单例,因为我们不在请求作用域中。

    但我认为,至少取消您第一次使用“GetService”是安全的。在这里,seedit方法只需要一个ISeviceScopeFactory,它可以安全地注入到配置参数中。

    公共void配置(IApplicationBuilder应用程序,环境卫生部,ILoggerFactory loggerFactory,ISeviceScopeFactory服务ScopeFactory)

    我认为工厂的单一模式是安全的。

  9. 广播: net的博客

留下答复史蒂文·T。克莱默取消答复

您的电子邮件地址将不会发布。已标记必需字段*

这个站点使用Akismet来减少垃圾邮件。了解如何处理评论数据.