使用JSON数据和EF Core种子数据库

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

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

私有静态列表BuildWeatherEvents() {var events = new List {WeatherEvent.Create(date . time . now,WeatherType.Sun,新建列表
         
          {new []{"Julie","Oh so sunny!"}}),weatherEvent.create(datetime.now.adddays(-2),weatherType.rain),WeatherType.Sun WeatherEvent.Create (DateTime.Now.AddDays (3),新建列表
          
           {“朱莉”,“哦,可爱的夏日阳光!”太糟糕了,我在电脑上。新的[]“佛蒙特州的每个人”,“万岁,我们去玩吧!”}新[]{“桑普森”,“我想去游泳,请!”},}),WeatherType.Cloudy WeatherEvent.Create (DateTime.Now.AddDays (4)),weatherEvent.create(datetime.now.adddays(-5),weatherType.rain),weatherEvent.create(datetime.now.adddays(-6),weatherType.sun);var lastEvent = WeatherEvent.Create(date . 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数据)。

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

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

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

现在当然是魔法的时候了JSON.NET这使得将这些数据导入EF变得简单而甜蜜成为可能。

这是我使用EF从JSON中播种数据库的完整代码。

我在configure方法内的ASP.NET核心Web API中从startup.cs调用它。这里我只是用system.io.file.readalltext读取文件,然后将该文本传递到seedit方法中。还要注意ConfigureServices,我在其中设置了DbContext及其所需的连接字符串。

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

public void ConfigureServices(IServiceCollection services) {services. adddbcontext
         
          (options=>options.usenpgsql(configuration[“data:postgreconnection:connectionString”]);services.AddMvc ();} public void Configure(IApplicationBuilder app,IHostingEnvironment env,{loggerFactory. addconsole (Configuration.GetSection("Logging"));loggerFactory.adddebug();app.UseMvc ();var datatext=system.io.file.readalltext(@“weatherdataset.json”);Seeder.Seedit (dataText app.ApplicationServices);
          }
         

这是Seedit方法,它使用json.net将json反序列化为weatherEvent对象列表。契约序列化程序将克服WeatherEvent类中的私有setter。我是从Daniel Wertheim的Github回购。然后,我使用ASP.NET核心服务提供程序获取在启动时设置的服务,该服务将实例化weatherContext以及在启动中指定的连接字符串。

使用系统;使用system.linq;使用system.collections.generic;使用Microsoft.Extensions.DependencyInjection;使用efcorewebapi;使用newtonsoft.json;使用JsonNet.PrivateSettersContractResolvers;公共静态类seeder公共静态void seedit(string jsondata,iServiceProvider ServiceProvider)JSonserializerSettings settings=new JSonserializerSettings ContractResolver=new privateSetContractResolver()列表
         
          事件= JsonConvert.DeserializeObject
          
           > (jsonData设置);使用(var serviceScope = serviceProvider .GetRequiredService
           
            ().createScope())var context=serviceScope.serviceProvider.getService
            
             ();if (!context.WeatherEvents.Any()) {context.AddRange(events);context.SaveChanges ();}}}}
            
           
          
         

在实例化上下文之后,我要检查一下数据库中是否有天气事件,这样我就不会重新播种了。(这是我在我的演示期间想要播种的逻辑,因此您可能对播种有不同的规则。)现在,我在WeatherEvent对象列表中调用Context.AddRange传递。我可以直接使用DbSet或上下文来调用AddRange。最后,SaveChanges。

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

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

注册我的新闻稿所以你不会错过我的会议和多元化视野课程公告!

关于“使用JSON数据和EF Core种子数据库

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

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

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

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

    1. 你好,Dom,
      我认为这本大书(编程ef第二版)对于理解ef的一些内部原理有一定的价值。DbContext & Code First非常相关。其他作者还有其他较新的书。然而,我把所有的努力都放在了创造多元内容上,而不是写书。所以你可以从我这里找到关于英孚的同样深度的信息。从…开始multuralsight.com/authors/julie-lerman

  3. 很高兴看到这个解决方案的发展历程,因此我对ASP.NET核心中的DI和作用域有了更多的了解,谢谢!

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

    创建表的合同(
    圆锥内景,
    符号varchar(4…)

    插入合同
    选择合同。*
    从OPENROWSET(批量C:\Data\Contract.json'),SINGLE_CLOB j)
    交叉应用OPENJSON (BulkColumn)
    用(锥形int,
    象征varchar (4),……
    )作为合同
    如果不存在(从合同C中选择conid,其中c.conid=conid)

    从合同中选择*

    1. 嗨,尼克,

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

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

  5. 你好,很酷的文章。

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

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

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

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

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

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

    公共静态IEnumerable AddRange(
    这IDbSet dbset,
    IEnumerable EntitiesToAdd)其中Tentity:类
    {
    返回((dbset)dbset).addrange(entitiestoadd);
    }

    谢谢你所做的一切。

    杰夫

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

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

    但我认为,至少取消您第一次使用“GetService”是安全的。seedIt方法可以使用IServiceScopeFactory,它可以安全地注入配置参数。

    公共void配置(IApplicationBuilder应用程序,IHostingEnvironment env,我的工厂日志工厂,IServiceScopeFactory aServiceScopeFactory)

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

  8. 广播: .NET博客

留下答复肖恩Wildermuth取消答复

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

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