【问题标题】:C# build taking a very long time because of large array of data由于大量数据,C# 构建需要很长时间
【发布时间】:2020-04-08 20:58:08
【问题描述】:

我有一个包含三个项目的解决方案。其中一个项目需要很长时间才能构建,我已将其范围缩小到初始化数据,尤其是大量郊区对象。

下面是 APISuburbInitialiser 类,尤其是郊区数组 - 这是问题所在:

using JobsLedger.API.InitalisationData.Interfaces;
using JobsLedger.DATA;
using JobsLedger.DATA.Entities;
using System.Linq;

namespace JobsLedger.API.InitalisationData {
    public class APISuburbInitialiser : IAPISuburbInitialiser {
        public void InitialiseStaInitialiseStatesAndSuburbs(APIDbContext context) {
            InitialiseStates(context);
            InitialiseSuburbs(context);
        }

        private void InitialiseStates(APIDbContext context) {
            // Look for any students.
            if (context.States.Any()) {
                return; //DB table data already created.
            }

            var states = new State[]
            {
                new State { StateName = "New South Wales", StateShortName = "NSW" },
                new State { StateName = "Victoria", StateShortName = "VIC" },
                new State { StateName = "Queensland", StateShortName = "QLD" },
                new State { StateName = "South Australia", StateShortName = "SA" },
                new State { StateName = "Western Australia", StateShortName = "WA" },
                new State { StateName = "Tasmania", StateShortName = "TAS" },
                new State { StateName = "Nothern Territory", StateShortName = "NT" },
                new State { StateName = "Australian Captial Territory", StateShortName = "ACT" }
            };

            foreach (State s in states) {
                context.States.Add(s);
            }

            context.SaveChanges();
        }

        private void InitialiseSuburbs(APIDbContext context) {

            // Look for any suburbs.
            if (context.Suburbs.Any()) {
                return; //DB table data already created.
            }

            // Upgraded due to database arbritarily assigning Id keys to  states in different order.
            var stateID = context.States.FirstOrDefault(s => s.StateShortName == "ACT").Id;

            var suburbs = new Suburb[] {
                new Suburb { PostCode = "200", SuburbName = "Australian National University", StateId = stateID, Latitude = -35.2777, Longditude = 149.1189 },
                new Suburb { PostCode = "221", SuburbName = "Barton", StateId = stateID, Latitude = -35.3049, Longditude = 149.14124 },
                new Suburb { PostCode = "2540", SuburbName = "Hmas Creswell", StateId = stateID, Latitude = -35.028, Longditude = 150.55013 },
                new Suburb { PostCode = "2540", SuburbName = "Jervis Bay", StateId = stateID, Latitude = -35.028, Longditude = 150.55014 },
                new Suburb { PostCode = "2540", SuburbName = "Wreck Bay", StateId = stateID, Latitude = -35.0169, Longditude = 150.63193 },
                new Suburb { PostCode = "2600", SuburbName = "Duntroon", StateId = stateID, Latitude = -35.3, Longditude = 149.16674 },
                new Suburb { PostCode = "2600", SuburbName = "Russell", StateId = stateID, Latitude = -35.2977, Longditude = 149.1514 },
                new Suburb { PostCode = "2600", SuburbName = "Harman", StateId = stateID, Latitude = -35.3053, Longditude = 149.13654 },
                new Suburb { PostCode = "2600", SuburbName = "Hmas Harman", StateId = stateID, Latitude = -35.31, Longditude = 149.13853 },
                new Suburb { PostCode = "2600", SuburbName = "Deakin", StateId = stateID, Latitude = -35.3193, Longditude = 149.10314 },
                new Suburb { PostCode = "2600", SuburbName = "Parliament House", StateId = stateID, Latitude = -35.3126, Longditude = 149.12783 },
                new Suburb { PostCode = "2600", SuburbName = "Yarralumla", StateId = stateID, Latitude = -35.2998, Longditude = 149.10584 },
                    ...

郊区如下:

using JobsLedger.CATALOG.Entities.Interfaces;
using System.Collections.Generic;

namespace JobsLedger.CATALOG.Entities
{
    public class Suburb : IEntityBase
    {
        public Suburb()
        {
            Tenants = new List<Tenant>();
            Users = new List<User>();
        }

        public int Id { get; set; }
        public string SuburbName { get; set; }
        public string PostCode { get; set; }

        public double Latitude { get; set; }
        public double Longditude { get; set; }

        // One state to many suburbs
        public int StateId { get;  set; }
        public State State { get; set; }


        public virtual ICollection<Tenant> Tenants { get; set; }
        public virtual ICollection<User> Users { get; set; }
    }
}

有 16000 个郊区,我使用这个数组在数据库中填充所有郊区的表。

我刚刚发现,这个郊区数组(以及另一个初始化类中的 1000 个客户端)导致该项目的构建需要很长时间 - 实际上是几分钟。去掉这些类,构建几乎不需要时间。

有没有办法在项目中使用它(或者在运行时以另一种方式加载这些数据)以免导致构建花费几分钟..

更新

我最初选择创建两个新项目 - 一个用于初始化(由所有数据库使用 - 它是一个租户系统),另一个用于 testdata - 仅用于一个数据库。然后我进一步创建了另一个授权项目。通过这种方式,我可以分割出所有其他项目(或其中一些项目)正在访问的那些代码部分,并且它们保持停滞状态,需要一次构建。这样做确实帮助我理解了我的项目之间的依赖关系是如何工作的。

【问题讨论】:

  • 您可以serialize the suburb array into a file(json、二进制、xml等)并将其存储为资源,而不是在代码中初始化数组,您可以通过从资源中读取文件来初始化它。
  • 您使用的是什么类型的数据库?您正在使用将您的类映射到数据库的实体。取决于数据库的类型会影响性能。需要查看连接字符串来判断是否有更快的方法。
  • 您可以在您的解决方案中创建另一个项目,并将您的数组初始化放入该项目中。因此,不会在您每次构建解决方案时都构建它。

标签: c# visual-studio asp.net-core visual-studio-2019


【解决方案1】:
  • 将郊区初始化移动到另一个项目中。然后将编译一次,但不是每次您对其他项目进行更改时都会编译一次。
  • 或将邮政编码/姓名信息移至资源文件。在启动时阅读它,这应该可以加快构建速度。

【讨论】:

    【解决方案2】:

    这是一个很好的数据播种用例。

    你可以创建一个静态类:

    public static class Seeds
    {
        public static IEnumerable<State> States = new State[]
        {
            new State { StateName = "New South Wales", StateShortName = "NSW" },
            ⋮
        }
    
        public static IEnumerable<Suburb> Suburbs = new Suburb[]
        {
            new Suburb { PostCode = "200", SuburbName = "Australian National University", StateId = stateID, Latitude = -35.2777, Longditude = 149.1189 },
            ⋮
        }
    }
    

    如果您可以将其添加到您的迁移中:

    public class APIDbContext : DbContext
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            ⋮
            modelBuilder.Entity<State>().HasData(Seeds.States);
            modelBuilder.Entity<Suburb>().HasData(Seeds.Suburbs);
        }
    }
    

    在这里,您可以在 Seeds 类中硬编码您需要的任何 ID。只要您从不更改这些 ID,您的种子数据就会在所有迁移中持续存在。

    这种类型的种子数据由迁移管理,并且需要在不连接到数据库的情况下生成更新数据库中已有数据的脚本。这施加了一些限制:

    • 即使通常由数据库生成,也需要指定主键值。它将用于检测迁移之间的数据更改。
    • 如果主键发生任何变化,之前的种子数据将被删除。

    因此,此功能对于预计不会在迁移之外更改且不依赖于数据库中的任何其他内容(例如邮政编码)的静态数据最为有用。

    参考:Data Seeding

    【讨论】:

    • 虽然我确实将这一切转移到另一个项目中,但我很可能会重构以反映这一点,因为我现在已将我的种子分为两个不同的项目。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-24
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-07
    • 2013-05-02
    相关资源
    最近更新 更多