EF: get data with many-to-one relation - c#

I have Entity model generated from DB:
public partial class Product
{
public Product()
{
this.ProductMeta = new HashSet<ProductMeta>();
}
public int ProductID { get; set; }
public int DomainId{ get; set; }
public bool IsDeleted { get; set; }
public virtual ICollection<ProductMeta> ProductMeta { get; set; }
}
and
public partial class ProductMeta
{
public int ProductID { get; set; }
public int LanguageID { get; set; }
public string ProductName { get; set; }
public bool IsDeleted { get; set; }
public virtual Product Product { get; set; }
}
and want I want to do is get the data from the both tables. What is the best way to join Product with ProductMeta, because ProductMeta have many variants, depends on languageId. Is my where and select are efficent? When I looked into generated SQL, there are many nested SELECT FROM. This is normal?
var result =
from p in _dbContext.Set<Product >()
where (p.DomainID == domainId
&& p.IsDeleted == false
&& p.ProductMeta.Select(pm => pm.LanguageID == languageId && pm.IsDeleted == false).FirstOrDefault()
select(new
{
ProductID = p.ProductID,
ProductName = p.ProductMeta.FirstOrDefault().ProductName
});
Thanks!

You can try selecting from ProductMeta
var result =
(from pm in _dbContext.Set<ProductMeta>()
where pm.LanguageID == languageId
&& pm.IsDeleted == false
&& pm.Product.DomainID == domainId
&& pm.Product.IsDeleted == false
select pm
);
This will give you an object of type List<ProductMeta>.. these objects will have a property call Product that will have all of the Product properties. You may need to use Include if lazy loading is turned off. That would look like
from pm in _dbContext.Set<ProductMeta>().Include("Product")
Append FirstOrDefault() after the linq query to get just a single ProductMeta object
select pm).FirstOrDefault();

Related

How to select the objects where the ID of a list (of entities) equals a variable using Entity Framework?

I've got a many to many relationship between Tariff and Insurance. Here the classes:
public class Insurance
{
public int ID { get; set; }
public string Name { get; set; }
public bool Deleted { get; set; }
public virtual ICollection<Tariff> Tariffs { get; set; }
public override bool Equals(object obj)
{
Insurance ins = obj as Insurance;
if (ins != null)
{
return ins.ID == ID;
}
return false;
}
}
public class Tariff
{
public int ID { get; set; }
public string Code { get; set; }
public decimal MinPrice { get; set; }
public bool Delete { get; set; }
public virtual ICollection<Insurance> Insurances { get; set; }
}
Now I want to select all the Tariff objects where the property Insurances contains an Insurance where the ID property equals the given variable. For this I've written this code:
public IEnumerable<Tariff> GetFilteredPublished(int insuranceID)
{
return (from t in dbSet
where t.Insurances.Contains(insuranceID)
select t).ToList();
}
This doesn't work because insuranceID is not an Insurance. When I use this code:
public IEnumerable<Tariff> GetFilteredPublished(int inscuranceID, int interventionID)
{
Insurance ins = new Insurance() { ID = inscuranceID };
return (from t in dbSet
where t.Insurances.Contains(ins) && t.Intervention.ID == interventionID
select t).ToList();
}
Gives me this exception:
Unable to create a constant value of type Insurance. Only primitive types or enumeration types are supported in this context.
Notes by the code:
On code below you could read what the dbSet is.
internal Context context;
internal DbSet<Tariff> dbSet;
public TariffService() // the `TariffService` class is the place where I call
// `GetFilteredPublished`
{
context = new Context();
dbSet = context.Set<Tariff>();
}
I'm using LINQ and Entity Framework.
How could I do this?
By a comment of #Federico I've found the answer:
return (from t in dbSet
where t.Insurances.Any(i => i.ID == insuranceID) && t.Intervention.ID == interventionID
select t).ToList();
Any(): Determines whether any element of an IQueryable<T> sequence exists or satisfies a condition.
Source: learn.microsoft.com

LINQ join based on checkbox

I'm using Entity Framework 6.0 in a C# WPF app. I have this query to search the Employee entity:
var query = Context.Employees.AsQueryable();
switch (selectedColumnValue)
{
case "lastName":
query = query.Where(q => q.lastName == SearchValue);
break;
case "employeeID":
query = query.Where(q => q.personnelID == SearchValue);
break;
case "securityID":
query = query.Where(q => q.securityID == SearchValue);
break;
default:
return;
}
The parent entity Employee may have some child rows in the EmployeeStatus entity to indicate if the employee is inactive, on leave of absence, or archived. Employee entity's primary key is employeeID and is a foreign key on EmployeeStatus.
Here is the Employee entity's columns:
public int employeeID { get; set; }
public string personnelID { get; set; }
public string securityID { get; set; }
public string firstName { get; set; }
public string middleName { get; set; }
public string lastName { get; set; }
public string suffix { get; set; }
public string job { get; set; }
public string organizationalUnit { get; set; }
public string costCenter { get; set; }
public string notes { get; set; }
public System.DateTime createdDate { get; set; }
And here's the EmployeeStatus entity's columns
public int employeeStatusID { get; set; }
public int employeeID { get; set; }
public int validEmployeeStatusID { get; set; }
public Nullable<System.DateTime> exitDate { get; set; }
public System.DateTime createdDate { get; set; }
If the user clicks the checkbox "Include archived employees?" during the search, we need to do the join to the EmployeeStatus and add the condition that the EmployeeStatus column validEmployeeStatusID equals 5 (Archived). How do you do this in LINQ?
Update: I need to add the Linq join after the above switch statement in an IF statement, such as this:
if (IncludeArchived)
{
// Add to Linq query here using method syntax
}
And so the Linq statement needs to use the method syntax.
What about this query?
query = (from empl in query
join status in Context.Status on empl.employeeID equals status.employeeID
where status.employeeStatusID == 5
select empl).Distinct();
Or you can perform chain:
query = query.Join(Context.Status, x => x.employeeID, x => x.employeeID, (a, b) => new { a, b })
.Where(x => x.b.employeeStatusID == 5).Select(x => x.a).Distinct();
Actually you do not need to add it after the switch statement.
In your current implementation you are including ALL employees (even the archived ones). So you actually need to make it so that you exclude the archived ones if the checkbox is not checked.
Note: I am assuming you have the navigation properties for your entities.
var query = Context.Employees.AsQueryable();
if (!IncludeArchived)
{
query = query.Where(e => e.Status == null || e.Status.employeeStatusId != 5);
}
switch (selectedColumnValue)
{
// ...
}

How to select list class properties on the base max value in each group?

How to select list class properties on the base max value in each group?
I tried like below but not get success..
public class STUDENT_DETAIL
{
public long? Registration_Id { get; set; }
public string Admission_No { get; set; }
public long? Class_Id { get; set; }
public long? Section_Id { get; set; }
public long? Academic_Year_Id { get; set; }
public string Student_First_Name { get; set; }
public string Student_Last_Name { get; set; }
public DateTime? Date_Of_Birth { get; set; }
public string Gender { get; set; }
public string Blood_Group { get; set; }
}
List<CUSTOM_STU_DETAIL> academicYr = new List<CUSTOM_STU_DETAIL>();
public PartialViewResult ReAdmission_of_Student(long? CompanyId, long? CompanyLocationId, long AcademicId, long? ClassID, long? SectionID)
{
if (CompanyId != null && CompanyId != 0 && CompanyLocationId != null && CompanyLocationId != 0 && ClassID != null && ClassID != 0 && SectionID != null && SectionID != 0)
{
academicYr = (from s in db.Student_Re_Admission
order by s.Re_Admission_Id descending
group s by s.Registration_Id into stugrp
//let topp = stugrp.Max(x => x.Re_Admission_Id)
select new CUSTOM_STU_DETAIL
{
}).ToList();
}
ViewBag.Student_List = StuList.ToList();
return PartialView();
}
Actually I want like below sql query concept in linq..
select * from [Student_Re_Admission] where [Re_Admission_Id] in( SELECT max(Re_Admission_Id) FROM [Student_Re_Admission] group by Registration_Id )
There is no Re_Admission_Id in STUDENT_DETAIL, so use Admission_No instead of.
var maxReAdmissionIds =
(from s in db.Student_Re_Admission
group s by s.Registration_Id
into grp
select (grp.Max(p => Convert.ToInt32(p.Admission_No))).ToString())
.ToList();
Group by Registration_Id than select the Admission_No to a list at first.
var result = from s in db.Student_Re_Admission
where maxReAdmissionIds.Contains(s.Admission_No)
select s;
Then, use the list to get the result.
But another concern is if maxReAdmissionIds more than 2100 elements will cause "sql where in too much element" exception.

.Net MVC 5 querying models that have a many-to-many-relationship with ApplicationUser

I have the classes Student and Class.
public class Student : ApplicationUser
{
public DateTime DateBorn { get; set; }
public virtual ICollection<Class> Classes { get; set; }
}
public class Class
{
public int Id { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
public string ProfessorId { get; set; }
public Professor Professor { get; set; }
public short Size { get; set; }
public int Year { get; set; }
public Term Term { get; set; }
public bool Enrollable { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
I'm trying to get the Classes at which a Student is enrolled. I thought of using the method class.Students.Contains() inside of the method .Where(), but it returns me an exception.
// GET: Classes/Calendar
public async Task<ActionResult> Calendar(int? year, Term? term)
{
year = year ?? DateTime.Now.Year;
term = term ?? (Term)(DateTime.Now.Month / 4);
var student = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var classes = db.Classes
.Where(c => c.Students.Contains(student) && c.Year == year && c.Term == term);
return View(await classes.ToListAsync());
}
The exception: Unable to create a constant value of type 'Demosthenes.Core.Models.Student'. Only primitive types or enumeration types are supported in this context.
How can I do this properly without having to write an explicit join?
If primary key property of Student is Id and User.Identity.GetUserId() return type is the same with Student::Id, you can try this.
var id = User.Identity.GetUserId();
var classes = db.Classes
.Where(c => c.Students.Any(s => s.Id == id)
&& c.Year == year && c.Term == term);
You need to take a step back and change your table structure.
For a many-to-many relationship, you need to add a junction table in the middle, e.g.
Student
StudentClass
Class
You code will then become
var classes = db.Classes
.Where(c => c.StudentClasses
.Any(x=>x.StudentId == student.Id)
&& c.Year == year && c.Term == term);

Linq Find Item by Primary Key and display Name(that lives in a different table)

I am displaying a record from my database. The record pulls data from other tables and uses a Int in the main table to represent the value so Item table has a Division equal to 1 and the Division table 1 = ALL . Now that i am displaying the records i am trying to turn the 1 into all. All the ID fields show the int. Which is what my code is telling it to do. But i am trying to display the name and when i do that i get a lot of red. It cannot find the name. CatagoryID should be CategoryName.
Hope that makes sense.
if (!IsPostBack)
{
string v = Request.QueryString["ContactID"];
int itemid;
int.TryParse(v, out itemid);
var customerInfo = GetCustomerInfo(itemid);
CONTACTID.Text = customerInfo[0].ContactID.ToString();
ContactTitle.Text = customerInfo[0].ContactTitlesID.ToString();
ContactNameB.Text = customerInfo[0].ContactName;
DropDownAddCategory.Text = customerInfo[0].CategoryID.ToString();
DDLAddDivision.Text = customerInfo[0].DivisionID.ToString();
ContactPhoneBox.Text = customerInfo[0].ContactOPhone;
ContactCellBox.Text = customerInfo[0].ContactCell;
ContactEmailBox.Text = customerInfo[0].ContactEmail;
CextB.Text = customerInfo[0].Ext;
}
private List<Solutions.Models.Contact> GetCustomerInfo(int itemid)
{
using (ItemContext context = new ItemContext())
{
return (from c in context.Contacts
where c.ContactID == itemid
select c).ToList();
}
}
This is the model
public class Contact
{
[ScaffoldColumn(false)]
public int ContactID { get; set; }
public System.DateTime ContactCreated { get; set; }
public string ContactName { get; set; }
public int? ContactTitlesID { get; set; }
public string ContactOPhone { get; set; }
public bool cApproved { get; set; }
public string User { get; set; }
public string ContactCell { get; set; }
public string ContactEmail { get; set; }
public int? DivisionID { get; set; }
public int? CategoryID { get; set; }
[StringLength(5)]
public string CExt { get; set; }
public virtual Division Division { get; set; }
public virtual Category Category { get; set; }
public virtual ContactTitle ContactTitle { get; set; }
public string Ext { get; set; }
}
With Entity Framework you can include related entities in query results:
return (from c in context.Contacts.Include("Catagory")
where c.ContactID == itemid
select c).ToList();
This will return contacts with Catagory objects: customerInfo.Catagory.CategoryName
BTW instead of returning list of contacts and selecting first one by index (thus possibly having index out of range exception), modify your method to return first contact (or default, if not found):
private Solutions.Models.Contact GetCustomerInfo(int itemid)
{
return (from c in context.Contacts.Include("Catagory")
where c.ContactID == itemid
select c).FirstOrDefault();
}
And use it this way:
var customerInfo = GetCustomerInfo(itemid);
if (customerInfo != null)
{
CONTACTID.Text = customerInfo.ContactID.ToString();
// etc
}
Are you using LINQ to SQL or Entity Framework? Check your model again and make sure the relationship between the two tables are setup correctly. The relationship may be missing from the model, and causing this problem.

Categories