ORMLite Service stack Таблицы собственных ссылок - PullRequest
0 голосов
/ 09 декабря 2018

У меня есть класс компаний и дочерних компаний.Они могут быть вложены в любой уровень и отображены в виде дерева.Я пытаюсь выяснить, как создать собственную ссылку в ormlite для построения иерархии с использованием приведенного ниже DTO.С приведенным ниже я получаю неоднозначные ошибки имени столбца.Является ли такой подход плохой идеей для стека служб?

public class Company : DTOServiceStackBase
{
    [AutoIncrement]
    [PrimaryKey]
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    public string Address { get; set; }

    [References(typeof(Company))]
    public int ParentId { get; set; }
}

Приведенный ниже DTO отлично работает в ORMLite.Я бы предпочел более чистую реализацию выше.

public class Company : DTOServiceStackBase
    {
        [AutoIncrement]
        [PrimaryKey]
        public int Id { get; set; }

        [Required]
        public string Name { get; set; }

        public string Address { get; set; }

        [Reference] // Save in SubCompanies table
        public List<SubCompany> SubCompanies { get; set; }
    }

    public class SubCompany : Company
    {
        [References(typeof(Company))]
        public int ChildCompanyId { get; set; }

        [References(typeof(Company))]
        public int ParentCompanyId { get; set; }
    }

РЕДАКТИРОВАТЬ На основе ответа Mythz Вот мой рабочий код для тех, кто хочет его использовать.

[Route("/Company/{Id}", "GET")]
public class GetCompaniesById : IReturn<GetCompaniesFlatTree>
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public int? ParentId { get; set; }
}

[Route("/CompaniesFlatTree", "GET")]
public class GetCompaniesFlatTree : IReturn<GetCompaniesFlatTree>
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public int? ParentId { get; set; }
}

[Route("/CompaniesTree", "GET")]
public class GetCompaniesTree : IReturn<Company>{}


    public class DTOServiceStackBase {
public ResponseStatus ResponseStatus { get; set; } //Automatic exception handling
    }

  public class Company : DTOServiceStackBase
    {
        [AutoIncrement]
        [PrimaryKey]
        public int Id { get; set; }

        [Required]
        public string Name { get; set; }

        public string Address { get; set; }

        public int? ParentId { get; set; }

        [IgnoreDataMember]
        public List<Company> SubCompanies { get; set; }
    }

[Authenticate]
    public class CompanyService : Service
    {
        /// <summary>
        /// Calling SQL directly and casting to the GetCompaniesFlatTree object
        /// Don't do this methond of direct SQL unless you cannot do it any other way
        /// Why?? Becuase the SQL is not automatically updated when we updated the database schema
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public object Get(GetCompaniesFlatTree request)
        {
            //This retun uses the DB.Select and works correctly
            //return Db.Select<GetCompaniesFlatTree>($"SELECT SC.* FROM Company C Join Company SC ON SC.ParentId = C.Id Where C.ID = {request.Id}");
            //This query uses Db.Query due to the BEGIN and CTE Usage
            //This does not work with SQL in Memory because it does not support CTE Statements
            return Db.Query<GetCompaniesFlatTree>("BEGIN WITH q AS ( SELECT * FROM [Company] WHERE ParentId IS NULL UNION ALL SELECT  m.* FROM [Company] m JOIN q ON m.parentId = q.Id) SELECT * FROM q END;");
        }

        /// <summary>
        /// Table Alias is required in this Select due to the self join on company.
        /// Table Alisa allows the join to specify which table to return the data from.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public object Get(GetCompaniesById request)
        {
            var q = Db.From<Company>(Db.TableAlias("c1"))
                .Join<Company>((ChildComp, ParentCompany) =>
                    ChildComp.Id == ParentCompany.ParentId
                    && ParentCompany.Id == request.Id, Db.TableAlias("c2")).Select<Company>(p => new {Id = Sql.TableAlias(p.Id, "c2"), Name = Sql.TableAlias(p.Name, "c2")});

            var results = Db.Select<GetCompaniesById>(q);
            //See the SQL that was generated
            var lastSql = Db.GetLastSql();
            return results;
        }

        /// <summary>
        /// Get all Compaines and build the hierarchy
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public object Get(GetCompaniesTree request)
        {
            //Get all companies
            var results = Db.Select<Company>();
            //Get Top node
            Company topCompany = results.Single(x => x.ParentId == null);
            //Find all children
            var allChildrenRecursive = GetAllChildrenRecursive(topCompany, results);

            return allChildrenRecursive;
        }

        /// <summary>
        /// Populates a Companies collection of child companies
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="results"></param>
        /// <returns></returns>
        private Company GetAllChildrenRecursive(Company parent, List<Company> results)
        {
            List<Company> retVal = new List<Company>();
            retVal.Add(parent);
            //Get Children
            var children = results.Where(x => x.ParentId == parent.Id).ToList();
            parent.SubCompanies = children;
            foreach (var child in children)
            {
                GetAllChildrenRecursive(child, results);
            }

            return parent;
        }
}

1 Ответ

0 голосов
/ 10 декабря 2018

Чтобы поддерживать древовидную связь, вам просто нужно иметь значение int? ParentId, которое можно обнулять в таблице Company, где Company с NULL ParentId является корневой компанией, в то время как в других компаниях выполняется перебор для заполнения Dictionary<int,List<Company>>индексируется родительским идентификатором.

Это не связано с OrmLite Self Reference , который просто означает сохранение ссылки FK на другую таблицу в таблице, содержащей ссылку.

...