views:

239

answers:

3

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.

+3  A: 

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.

Anthony Pegram
Wow! Thanks for taking the time to write all this out. I greatly appreciate it. I will not be able to test this out until tomorrow, but it looks very solid. I am new to all this stuff, so I did not consider/know to solve the problem in this manner. Once again thanks. Have a great day!
PFranchise
@PFranchise, Hopefully it gives you an idea, I obviously do not anticipate that it maps entirely to your data model. But with a little adaptation, you should be able to do something similar (if a better solution does not come along).
Anthony Pegram
The only question I have now, is about returning the results that match the search. Would I do that prior to this foreach (Employee employee in employees) { employee.Underlings = employees.Where(e => e.BossId == employee.Id).ToList(); }I guess rather than doing a foreach employee in employees (which would be the table?), should that employees be a query object that holds all employees who match the search?
PFranchise
That's the million-dollar question, and I'm afraid I only have a buck-and-a-quarter answer at the moment. Given that this is the result of a search and not just a plain employee hierarchy, my sample starts to tumble like a house of cards. From your original question, it seems the search should match at the top, not the underlings. So on one hand, you need access to matching employees, but on the other you need to access to all employees that may work *for* the matching employees.
Anthony Pegram
(continued) You may need to make repeated calls to the database, particulary depending upon how you would like to frame your output. *Get matches -> recursively get underlings.* The question then is what if you search on Smith, and you return Tom Smith and John Smith. Then you search for their underlings, and it turns out that John works for Sally who works for Tom. So now you have Tom and John as your initial search results *and* you have John under Tom's hierarchy. So how would you intend to display these results?
Anthony Pegram
Hm, I see what you are saying. I think what would happen in that case it would display all of Tom's hierarchy, which would include John, and then display John, who as a third tier would not have any underlings. I think that would be acceptable and I would not have to worry about that special case. This question might not make sense, or be the correct thing to ask, but I ultimately need to display these results in a datagrid. I assume I can do that using the list in the manner you did, but if I use multiple queries would i just concat them and then use your list idea to sort them? THANKS again!
PFranchise
@PFranchise, *concat* probably isn't the right term, but yes, you would still build a single list, similar to what I presented in my answer.
Anthony Pegram
ok, sounds great. I really appreciate the time you spent helping me and tomorrow I will hopefully get all this working. Have a great day!
PFranchise
@PFranchise, I've updated my answer in light of these comments. Again, this is in-memory, so you'll need to adapt it to your needs and to go against the database. But I hope it gives you a good idea of one possible course of action.
Anthony Pegram
Thanks Anthony. I appreciate it.
PFranchise
+1  A: 

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.

shahkalpesh
Thanks for tip. Haha, like I said I am new to this, so I am not sure what a stored procedure is or how it would work. I assume there is information online, so I will look into it as a possible solution. I agree that LINQ should not be used for everything, but I guess it is the only way I half understand atm, but hopefully that will change soon.
PFranchise
It would be nice, though, if perhaps you suggested *how* to solve it more than merely *where* to solve it.
Anthony Pegram
+1  A: 

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;
}
PFranchise