简体   繁体   中英

Complex orderby question (entity framework)

Ok, so I will start by saying that I am new to all this stuff, and doing my best to work on this project. I have an employee object, that contains a supervisor field. When someone enters a search on my page, a datagrid displays employees whose name match the search. But, I need it to display all employees that report to them and a third tier of employees that report to the original employee's underlings. I only need three tiers. To make this easier, employees only come in 3 ranks, so if rank==3, that employee is not in charge of others. I imagine the best method of retrieving all these employees from my employee table would be something like

from employee in context.employees
where employee.name == search || employee.boss.name == search || 
employee.boss.boss.name == search

But I am not sure how to make the orderby appear the way I want to. I need it to display in tiers. So, it will look like: Big Boss- Boss- underling- underling- Boss- underling- Boss- Boss- Big Boss-

Like I said, there might be an easier way to approach this whole issue, and if there is, I am all ears. Any advice you can give would be HIGHLY appreciated.

This seems a difficult requirement to solve using any particular ORM framework, at least not in one easy step. A multi-step process is likely to be necessary.

Something roughly similar can be accomplished via an approach to iterate through the search results and finding their children (and children's children), and flattening the hierarchy into a single list. An example implementation here, this one done using a plain in-memory list:

class Employee
{
    public int Id { get; private set; }
    public int? BossId { get; private set; }
    public string Name { get; private set; }

    public Employee(int id, int? bossId, string name)
    {
        Id = id;
        BossId = bossId;
        Name = name;
    }
}

Sample data:

List<Employee> employees = new List<Employee>();
employees.Add(new Employee(1, null, "Tom Smith"));
employees.Add(new Employee(2, null, "Susan Jones"));
employees.Add(new Employee(3, 1, "Sally Davis"));
employees.Add(new Employee(4, 1, "Robert Roberts"));
employees.Add(new Employee(5, 3, "John Smith"));
employees.Add(new Employee(6, 2, "Tonya Little"));
employees.Add(new Employee(7, 3, "Ty Bell"));
employees.Add(new Employee(8, 4, "Helen Andrews"));
employees.Add(new Employee(9, 2, "Matt Huang"));
employees.Add(new Employee(10, 6, "Lisa Wilson"));

Process:

string searchTerm = "Smith";

var searchResults = employees.Where(e => e.Name.Contains(searchTerm));

List<Employee> outputList = new List<Employee>();

Action<IEnumerable<Employee>, List<Employee>> findUnderlings = null;
findUnderlings = (input, list) =>
{
    foreach (Employee employee in input)
    {
        list.Add(employee);
        var underlings = employees.Where(e => e.BossId == employee.Id);
        findUnderlings(underlings, list);
    }
};

findUnderlings(searchResults, outputList);

Show output:

foreach (Employee employee in outputList)
{
    Console.WriteLine("{0}\t{1}\t{2}", employee.Id, employee.Name, employee.BossId);
}

Results:

1       Tom Smith
3       Sally Davis     1
5       John Smith      3
7       Ty Bell 3
4       Robert Roberts  1
8       Helen Andrews   4
5       John Smith      3

And you can see it follows the top result, underling, underling's underlings, next result, any underlings, etc. It works for any number of tiers.

I am not sure how that can be accomplished in an "order by" within Linq or even regular SQL, but that could only mean I'm not smart enough to do it rather than it just isn't possible.

Hey, I thought I would post the way I ended up solving this issue, in case it might come in handy for someone else.

var list = productQuery.ToList();
var productList = Functions.sortResultsList(list);




public static List<SolutionsModel.Version> 
   sortResultsList(List<SolutionsModel.Version> list)
{
    var productList = new List<SolutionsModel.Version>();

    int total = list.Count();
    int solutions = 0;
    int objects = 0;

    for (int length = 0; length < list.Count(); length++)
    {
        if (list[length].Product.TypeID == 1)
        {
            ++solutions;
        }
        else if (list[length].Product.TypeID == 2)
        {
            ++objects;
        }
    }

    //These nested for-loops create a list that is 
    //correctly ordered to fit correctly into the grid. 
    //Perhaps consider more efficient improvision at a later time.

    //These for loops can't be used if there are not any solutions 
    //in the results
    if (solutions != 0)
    {
        for (int x = 0; x < list.Count; x++)
        {
            if (list[x].Product.TypeID == 1)
            {
                productList.Add(list[x]);
                for (int y = 0; y < list.Count; y++)
                {
                    if (list[y].Product.TypeID != 1)
                    {
                        if (list[y].Product.Product2.ID == list[x].Product.ID && list[y].VersionNumber == list[x].VersionNumber)
                        {
                            productList.Add(list[y]);
                            for (int j = 0; j < list.Count; j++)
                            {
                                if (list[j].Product.TypeID == 3)
                                {
                                    if (list[j].Product.Product2.ID == list[y].Product.ID && list[j].VersionNumber == list[y].VersionNumber)
                                    {
                                        productList.Add(list[j]);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    //This can't be used if the results do not contain any objects.
    if (objects != 0 && productList.Count != total)
    {
        for (int y = 0; y < list.Count; y++)
        {
            if (list[y].Product.TypeID == 2)
            {
                productList.Add(list[y]);
                for (int j = 0; j < list.Count; j++)
                {
                    if (list[j].Product.TypeID == 3)
                    {
                        if (list[j].Product.Product2.ID == list[y].productID && list[j].VersionNumber == list[y].VersionNumber)
                        {
                            productList.Add(list[j]);
                        }
                    }
                }
            }
        }

    }

    //If the results contain only modules, no sorting is required and the original list can be used.
    if (productList.Count != total)
    {
          return list;
    }

    return productList;
}

I don't know much of LINQ. But, why can't this be done using a stored procedure, which can be referred by your EF Model?

I think, one shouldn't use LINQ for everything :)

EDIT: The reason I say that use of stored procedure makes sense is because EF will have to generate SQL query to do what you want & what you want to do, can be better expressed & controlled in SQL.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM