0

I am trying to translate SQL code that I wrote with a union into linq code in C#, but I don't understand what I am doing wrong.

I have two tables one is tTask and another one is tTask_Users.

I tried joinning them together by using a model, because I thought it was giving me errors because the data didn't have same number amount of columns.

Here is my SQL code:

SELECT PK_Task, TaskName, Enabled, FK_TaskTeam, 'No Owner' AS Owners
FROM tTask
WHERE hasOwner = 0 
  UNION
SELECT PK_Task, TaskName, Enabled, FK_TaskTeam, Login
FROM tTask
INNER JOIN tTask_User ON tTask.PK_Task = tTask_User.FK_Task
INNER JOIN tOfficeUsers on tTask_User.FK_OfficeUser = tOfficeUsers.PK_OfficeUser
WHERE FK_Taskrecipienttype = 5 AND Enabled = 1

Here is what I tried

private OfficeEntities db = new OfficeEntities();

//GET: api/MainGrid
public IQueryable<MainGridDto> GetGridModelDtos()
{
    var query2 = db.tTask_User.Where(task =>
        task.tTaskRecipientType.pk_taskrecipienttype == 5
    ); 
    var query = from items in query2                            
                select new MainGridDto()
                {
                    PK_Task = items.FK_Task,
                    TaskName = items.tTask.TaskName,
                    TaskRecipientType = items.tTaskRecipientType,
                    Owner = items.tOfficeUser.Login,
                    TaskDescription = items.tTask.TaskDescription,
                    Enabled = items.tTask.Enabled,
                    tTaskTeam = items.tTask.tTaskTeam,
                    EmailBody = items.tTask.EmailBody,
                    EmailSubject = items.tTask.EmailSubject
                };

    var taskquery = db.tTasks.Where(items =>
        items.hasOwner == 0
    );
    var querytask = from items in taskquery
                    select new MainGridDto()
                    {
                        PK_Task = items.PK_Task,
                        TaskName = items.TaskName,
                        TaskRecipientType = null,
                        Owner = "no owner",
                        TaskDescription = items.TaskDescription,
                        Enabled = items.Enabled,
                        tTaskTeam = items.tTaskTeam,
                        EmailBody = items.EmailBody,
                        EmailSubject = items.EmailSubject
                    };

    query2.Union(querytask);
    query.Union(taskquery);
}

Here are the errors I am getting:

Severity    Code    Description Project File    Line    Suppression State
Error   CS1929  'IQueryable<tTask_User>' does not contain a definition for 'Union' and the best extension method overload 'ParallelEnumerable.Union<MainGridDto>(ParallelQuery<MainGridDto>, IEnumerable<MainGridDto>)' requires a receiver of type 'ParallelQuery<MainGridDto>'    ReportingWebAPI \source\repos\ReportingWebAPI\ReportingWebAPI\Controllers\MainGridController.cs 56  Active

Severity    Code    Description Project File    Line    Suppression State
Error   CS1929  'IQueryable<MainGridDto>' does not contain a definition for 'Union' and the best extension method overload 'ParallelEnumerable.Union<tTask>(ParallelQuery<tTask>, IEnumerable<tTask>)' requires a receiver of type 'ParallelQuery<tTask>'   ReportingWebAPI \repos\ReportingWebAPI\ReportingWebAPI\Controllers\MainGridController.cs    57  Active
elquesogrand
  • 197
  • 4
  • 14
  • Not sure `union` is the way to go for just 1 column. Consider something like `Owner = hasOwner ? items.tOfficeUser.Login : "no owner", ...` – Steve Greene May 01 '19 at 15:58
  • Jusdt use a Join instead of a union : https://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b – jdweng May 01 '19 at 16:16
  • well, I need the results from those two different queries because it gets me task that have owners and task that don't have owners at all will be blank. But I can't seem to combine the two things together because it gives me errors – elquesogrand May 01 '19 at 17:41
  • @jdweng -- what to you mean Join -- join is not a set operator it does something totally different. – Hogan May 01 '19 at 17:47
  • `Union` returns a result - you are not doing anything with the result of calling `Union` in your sample code. It is like you have `a+b;` as a statement - `a` and `b` are unaffected. Also neither union makes sense - in both you are combining a partial subquery with a finished subquery. Perhaps my [SQL to Linq Recipe](https://stackoverflow.com/questions/49245160/sql-to-linq-with-multiple-join-count-and-left-join/49245786#49245786) could help you. – NetMage May 01 '19 at 18:13
  • @NetMage I am having a hard time understanding how linq works. I am not trying to do anything with the results, I just want to return the results after combining the two queries together to populate my grid. I don't understand why it's a partial subquery? Can you explain that please? – elquesogrand May 01 '19 at 18:55
  • So the first time you union `query2` with `querytask`. `query2` is the partial sub-query that gets `tTask_User` with RecipType == 5 but doesn't project to a `MainGridDto`. The second time you union `query` with `taskquery`. `taskquery` is the partial sub-query that gets `tTasks` with `hasOwner == 0` but doesn't project to `MainGridDto`. Why didn't you do `var ans = query.Union(querytask);` ? – NetMage May 01 '19 at 21:47
  • oh wow... You are right. I didn't even see that @why don't do var ans= query.union(querytask) I was using the wrong query that's why it wasn't working.... Anyways, that makes sense and I understand why you mean about partial sub-query and projecting. – elquesogrand May 02 '19 at 15:41

3 Answers3

1

Just one query is enough. Notice the change in Where and in the Owner property.

var query2 = db.tTask_User.Where(task => task.hasOwner == 0 || (task.Enabled && task.tTaskRecipientType.pk_taskrecipienttype == 5)); 

var query = from items in query2                            
            select new MainGridDto()
            {
                 PK_Task = items.FK_Task,
                 TaskName = items.tTask.TaskName,
                 TaskRecipientType = items.tTaskRecipientType,
                 Owner = items.hasOwner == 0 ? "no owner" : items.tOfficeUser.Login,
                 TaskDescription = items.tTask.TaskDescription,
                 Enabled = items.tTask.Enabled,
                 tTaskTeam = items.tTask.tTaskTeam,
                 EmailBody = items.tTask.EmailBody,
                 EmailSubject = items.tTask.EmailSubject
            };
M. Ruiz
  • 406
  • 4
  • 11
  • Yes, that gets me closer to what I need. The only thing left to do to get me the same results as the sql query I wrote above is, if the task does not have an owner only show one task. Some task have other types like bcc, to, error, ect... and for each type the task has it will give me all those objects on my grid. Like if one task has 5 types and that task doesn't have an owner, it will give me 5 task showing "no owner." How can I get it to only show me just one task if it doesn't have an owner at all instead of telling me 5 times that it doesn't have an owner. I think that the union – elquesogrand May 01 '19 at 18:45
  • was getting rid of those duplicates and only giving me task telling me there is no owner. IF that makes sence... I know it's hard to know when you can't see the structure or the data. – elquesogrand May 01 '19 at 18:45
  • I really appericate it though. This got me a lot futher then I was before. I was showing 2000+ records and now it's down to 635. That should improve things – elquesogrand May 01 '19 at 18:46
0

I could be wrong, but it looks like an issue with types. The general rule is:

  1. both types should have exactly the same property names
  2. both types should have exactly the same property types (int vs a smallint or double vs a decimal will cause a build error)

Looks like query2 is type of IQueryable<tTask_User> and querytask is type of IQueryable<MainGridDto> that's why you can't do a union here:

query2.Union(querytask);
Pavel Kovalev
  • 7,521
  • 5
  • 45
  • 67
  • That's why I tried to select new MainGridDto on both of them so they would have the same model data types. I just still don't understand how to be able to join both of them together. In my sql code I put I am getting the exact results I need, but translating that to linq I am find a bit tricky. – elquesogrand May 01 '19 at 18:38
  • @elquesogrand maybe you can try to select MainGridDto from query2 ... make them the same type, or same anonymous type, it should work too – Pavel Kovalev May 01 '19 at 18:54
0

Following my SQL to LINQ Recipe, first you do the two sub-queries from SQL:

var query1 = from items in db.tTasks
             where items.hasOwner == 0
             select new MainGridDto() {
                 PK_Task = items.PK_Task,
                 TaskName = items.TaskName,
                 TaskRecipientType = null,
                 Owner = "no owner",
                 TaskDescription = items.TaskDescription,
                 Enabled = items.Enabled,
                 tTaskTeam = items.tTaskTeam,
                 EmailBody = items.EmailBody,
                 EmailSubject = items.EmailSubject
             };
var query2 = from items in db.tTask_User
             where items.tTaskRecipientType.pk_taskrecipienttype == 5 && items.tTask.Enabled
             select new MainGridDto() {
                 PK_Task = items.FK_Task,
                 TaskName = items.tTask.TaskName,
                 TaskRecipientType = items.tTaskRecipientType,
                 Owner = items.tOfficeUser.Login,
                 TaskDescription = items.tTask.TaskDescription,
                 Enabled = items.tTask.Enabled,
                 tTaskTeam = items.tTask.tTaskTeam,
                 EmailBody = items.tTask.EmailBody,
                 EmailSubject = items.tTask.EmailSubject
             };

Then you combine the sub-queries for the final answer:

var ans = query1.Union(query2);
NetMage
  • 26,163
  • 3
  • 34
  • 55
  • I got this error " The text data type cannot be selected as DISTINCT because it is not comparable. " – elquesogrand May 02 '19 at 15:42
  • Do you happen to know what this causing that? I tried checking to see if the owner = "no owner " was causing that and I took it out on both queries and I still go an error. – elquesogrand May 02 '19 at 15:44
  • I figured that one out... Seems like the database was having issues with the data type text and I changed it to varchar instead. It is working!! Thank you – elquesogrand May 02 '19 at 17:53
  • NOTE: In LINQ, `Union` guarantees distinct answers - if you don't need that guarantee, you can use `Concat` instead which is the proper translation of a SQL `UNION`. – NetMage May 02 '19 at 19:41