0

I have been trying for a while to do this in every possible way. I have a table name employees and other table name qualifications

Every employee has qualifications, but on initial basis qualification of every employee has not been filled into database.

I have tried VIA EF and LINQ and raw SqlConnection and SqlCommand as well but still not getting good results.

Employees WITH PHD is retrieved by

ViewData["lecturer_phd"] = _context.TblQualification
                                   .Count(q => q.QualificationType == 4 && 
                                               q.Employee.DesignationCode == 3);

and NON PHD should get back with

ViewData["lecturer_nphd"] = _context.TblEmployees
                                    .Count(e => e.DesignationCode == 3 && 
                                                e.EmployeeQualifications.Any(q => q.QualificationType != 4));

But this is not working and I am not familiar with LINQ as well but I tried that as well not any result.

The raw SQL query is using this code:

SqlConnection con = new SqlConnection(_context.Database.GetConnectionString());

DbCommand cmd = new SqlCommand("SELECT Count(*) FROM [DbBzuCC].[dbo].[tblEmployees] LEFT JOIN tblQualifications ON tblEmployees.Employee_Code = tblQualifications.Employee_Code AND tblQualifications.qualification_type != 4  WHERE tblEmployees.Designation_Code = 3",
                               con);

con.Open();
ViewData["lecturer_nphd"] = (int) cmd.ExecuteScalar();
con.Close();

But all in vain. Any help will be appreciated. I will be thankful for any help from community. Thanks in advance

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • As an aside consider using Enums for `qualification_type` instead of direct integers. See [Enum Support - Code First](https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/data-types/enums) – Kwiksilver Apr 07 '21 at 19:41
  • See this post [How to COUNT rows within EntityFramework without loading contents?](https://stackoverflow.com/questions/890381/how-to-count-rows-within-entityframework-without-loading-contents). – Kwiksilver Apr 07 '21 at 19:45
  • @Kwiksilver Do you think my code needs enums ? I am using database first approach and i want to perform a simple query to get all employees with phd in qualification table and non phd as well in qualification table if they don't exists any phd record – Humza Malik MindFreak Apr 07 '21 at 19:47

2 Answers2

0

Actually qualification_type was null where it was not entered for any employee so In sql query we need to check

ISNULL like this

SELECT COUNT(*)
  FROM [DbBzuCC].[dbo].[tblEmployees]
  Left join tblQualifications ON tblEmployees.Employee_Code = tblQualifications.Employee_Code
  WHERE tblEmployees.Designation_Code = 3 AND ISNULL(tblQualifications.qualification_type,1) != 4

And in entity framework you can do it like this

_context.TblEmployees
                .Count(e => e.DesignationCode == 3 && e.EmployeeQualifications.All(q => q.QualificationType != 4));

Thanks everyone for your precious time.

Regards,

0

So you have Employees and Qualifications, and a one-to-many relation: every Employee has zero or more Qualifications, every Qualification is the Qualification of exactly one Employee, namely the Employee that the foreign key EmployeeCode refers to.

If you have followed the entity framework conventions, you will have classes similar to:

class Employee
{
    public int Id {get; set;}
    public string Name {get; set;}
    ...

    // Every Employee has zero or more Qualifications (one-to-many)
    public virtual ICollection<Qualification> {get; set;}
}

class Qualification
{
     public int Id {get; set;}
     public string Description {get; set;}
     ...

     // Every qualification is the qualification of exactly one Employee using foreign key
     public int EmployeeId {get; set;}
     public virtual Employee Employee {get; set;}
}

This would be enough for entity framework to detect the tables, the columns in the tables, and the relations between the tables using the primary keys and foreign keys.

In entity framework the columns of the tables are represented by the non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many)

The foreign key Qualification.EmployeeId is a real column, hence it it non-virtual. Property Qualfication.Employee represents the relation, hence it is virtual.

There is only need for attributes nor fluent API if you deviate from the conventions, like you did in your foreign key. In DbContext.OnModelCreating:

modelBuilder.Entity<Employee>().HasKey(employee => employee.EmployeeCode);
modelBuilder.Entity<Employee>().HasMany(employee => employee.Qualifications)
    .WithRequired(qualification => qualification.Employee)
    .HasForeignKey(qualification => qualification.EmployeeCode);

In words: every Employee has a primary key in property EmployeeCode. Every Employee has zero or more Qualifications in property Employee.Qualifications. Every Qualification has exactly one Employee in property Qualification.Employee, using foreign key Qualificatioin.EmployeeCode.

Back to your question

Requirement: count the number of qualifications with qualification_type != 4 for Employees that have Designation_Code == 3

Usig the virtual ICollection this is straightforward:

int qualificationTypeToIgnore == 4;
int employeeDesignationCode == 3;

var result = dbContext.Qualifications.Where(qualification =>
        qualification.QualificationType != qualificationTypeToIgnore &&
        qualification.Employee.DesignationCode == employeeDesignationCode)
    .Count();

In words: from all Qualifications, keep only those Qualifications that have a QualificationType unequal to qualificationTypeToIgnore that also belongs to an Employee that has a DesingationCode equal to employeeDesignationCode. Count the number of remaining Qualifications.

Entity framework knows the one-to-many relation, and will do the property Join for you.

Some people don't like to use the virtual properties, they prefer to do the join themselves.

var eligibleEmployees = dbContext.Employees
    .Where(employee => Employee.DesignationCode == employeeDesignationCode);
var eligibleQualifications = dbContext.Qualifications
    .Where(qualification => qualification.QualificationType != qualificationTypeToIgnore);

var result = eligibleQualifications.Join(eligibleEmployees,

qualification => qualification.EmployeeCode, // from every Qualification take the foreign key
employee => employee.EmployeeCode,           // from every Employee take the primary key

// parameter resultSelector: from every (qualification, employee) combination make one new
(qualification, employee) => new {qualification, employee})

.Count();

Since you will not be using the join result, you could simplify parameter resultSelector:

(qualification, employee) => 1

Or let entity framework do the join:

var result = eligibleQualifications.Where(qualification => eligibleEmployees
    .Any(employee => employee.EmployeeCode == qualification.EmployeeCode))
.Count();

From all eligible qualifications, count the number of qualifications that have an EmployeeCode that is also an EmployeeCode in the eligible Employees.

IMHO the solution using the virtual properties is the most elegant one.

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • You solution in the end is great and thanks for help but instead of any we need to use All method when condition on join becuase some employees have no record in qualification so they were missing i want to count non phd's who are not education type 4. I hope now question is clear. Thanks for your too much help but resolution is what i have answered myself. – Humza Malik MindFreak Apr 09 '21 at 09:34
  • I really appreciate all the helps but you mis understood at one place that All the employees don't have associated qualification. So virtual property method is not gonna work here because all the employees are not present in qualification table and we can't match them against the qualification condition – Humza Malik MindFreak Apr 09 '21 at 15:25