views:

44

answers:

1

I ran across this code in one of our Entity Framework applications. I know there has to be a better (more efficient) way than the three queries this code executes. Although, I can't quite get the syntax right. (I am still learing Entity Framework myself..)

There are two tables involved. This is a simple parent/child relationship.

There is a Call table that contains information about all of our Calls and there is a Units table that contains the individual serial numbers (units) assigned to each call. Note, it isn't a MANY-TO-MANY relationship. The Units table can/will contain duplicate records (Serial Numbers)!!

A call can have 0-Many children records in the Units table.

So, when a caller calls in, our Cust Rep enters a Serial Number (always creates a new record in the Units table), which associates it with this call. At that point we populate a "Call History" tab. This tab is built by the query below. (Search the Units table and finds all Units that match this Unit and then returns all Calls that are assigned to all of those Units (records).)

To summarize. The objective of the query is: based on the callID, find ANY other calls in the database that are also tied to ANY of the serial numbers that are assigned to this call.

Considering the Entity Framework created a Navigation in the tblCall table called "Categories" and in the tblCategory table called "Call", there has to be a better/more efficient way to write this query. I would really like to refactor it. :)

Here is the existing query:

    //First, get all the Serial Numbers assigned to this Call.
    var serials = from units in context.tblUnits
                where units.callID == callID
                select units.unitSerialNumber;

    List<string> serialsList = serials.ToList<string>();

    //Get all of the Call IDs that are assigned to any of the serial numbers from the list above
    var callIDs = from units in context.tblUnits
                    where serialsList.Contains(units.unitSerialNumber)
                    select units.callID;

    List<int> callIDList = callIDs.ToList<int>();

    //Return all of the calls that are in the callID list from above
    var data = from calls in context.tblCalls
                where callIDList.Contains(calls.callID)
                select calls;

    result = data.ToList<tblCall>();

Any advice is much apprecaited!

Thanks for your help Daniel. Here is the final query:

var query = (from u1 in context.tblUnits
             join u2 in context.tblUnits on u1.unitSerialNumber equals u2.unitSerialNumber
             join c in context.tblCalls on u2.callID equals c.callID
             where u1.callID == callID
             select c).Distinct();

result = query.ToList();
A: 

I think you could replace it with a query similar to this:

var query = from u1 in context.tblUnits
        join u2 in context.tblUnits on u1.unitSerialNumber equals u2.unitSerialNumber
        join c in context.tblCalls on (u2.callID ?? -1) equals c.callID
        where u1.callID == callID
        select c;

var result = query.ToList();
Daniel Pratt
I'll try that. Thanks.I thought there might also be some way to take advantage of the Navigation properties on each table too. (For instance tblCalls has a "Units" navigation property and tblUnits has a "Call" navigation property...)
Shayne
In that case, you could probably eliminate the second join (to tblCalls) and 'select u2.Call'.
Daniel Pratt
So, you think this will work?var query = from u1 in context.tblUnits join u2 in context.tblUnits on u1.unitSerialNumber equals u2.unitSerialNumber where u1.callID == 1 select u2.Call; var result = query.ToList(); What is the purpose of the "u1.callID == 1"?
Shayne
Yes, based on what information you've provided, I think that will work. The "u1.callID == 1" bit was something I forgot to change before pasting over the code. I've fixed my example to make the intention clear.
Daniel Pratt
You left the second join in your example. Did you mean to remove it and use the Navigation? Also, what does "u2.cadllID ?? -1 do? I am not familiar with "??".
Shayne
Whether you 'join' or use the navigation property, it is unlikely to affect the result, so I didn't change that part of the example. The '??' operator is referred to as the null-coalescing operator. It says, in essence, if u2.callID is NULL, substitute -1 as the value. It appears here because my example assumes tblUnits.CallID is nullable and tblCalls.CallID is not.
Daniel Pratt
callID isn't nullable. I am going to modify the original question to add more details. I am not really conveying the problem as well as I should.
Shayne
Are you unable to fashion the example query I provided into something that will work in your application? Does it generate an error? Incorrect results?
Daniel Pratt
I didnt try it once you said you assumed "tblUnits.CallID" is nullabe. I suppose after I noted that it isn't nullable you might want to suggest something else. I will give it a shot.
Shayne
I get:Operator ?? cannot be applied to operands of type 'int' and 'int'.
Shayne
Your solution helped me alot. I only had to make a couple of minor changes. I put the solution in the original post. Thanks!
Shayne