I have an interface like what u see below and I want it to return me a Property Called "ProductionCostId" which has a type of 'int' from a table in the database
int Get(Guid productionLineId, string productionCode)
In the implementation as you can see I want to get this value from a child entity called "ProductionAssignmetnt"
public int Get(Guid productionLineId, string productionCode)
{
return GetAll()
.Where(x => x.ProductionAssignments
.Where(x => x.ProductionLine.Id == productionLineId && x.ProductionCode == productionCode)
.Select(x => x.ProductionCostId);
}
But I dont know how to get this int value
First, you need to include the productionassignments table to this query i.e. join it.
In your GetAll() method where you return in you can join a table using code first by _context.ProductionLines.Include(x => x.ProductionAssignments), if you're on Database First then read this on how to join tables
Now since you haven't posted any model, this is how you'd select if your ProductionCostId is nested inside the assignments
GetAll()
.FirstOrDefault(pLine => pLine.Id == productionLineId)
?.ProductionAssignments.FirstOrDefault(assignment => assignment.ProductionCode == productionCode)?.ProductionCostId)
This query will get the production line with the id, and then select the first productionCostId from the assignments where the code matches. This query assumes the Model
public class ProductionLine
{
public int Id {get;set;}
public List<ProductionAssignment> ProductionAssignments {get;set;}
}
public class ProductionAssignment
{
public int ProductionCode {get;set;}
public int ProductionCostID {get;set;}
public ProductionLine ProductionLine {get;set;}
}
Beware of null reference exceptions.
Related
I have a domain class like below :
public class Employee
{
public int EmployeeId { get; set; }
public int DeptId { get; set; }
}
public class Transaction
{
public int TRID { get; set; }
public int EmployeeId { get; set; }
public string Status { get; set; }
}
Now I want to get all employees from the EmployeeTable for DeptId = 100. I want to calculate Pending status for those employees whose transactions are pending.
So if employee records are found in Transactions table then just want to return a column saying whether employee has any pending transactions or not)
Query :
var t = (from e in _employeeRepository.GetAll() //returns IQueryable<Employee>
where e.DeptId == 100
from t in _transactionRepository.GetAll().Where(t => t.EmployeeId == e.EmployeeId)
select new
{
IsPendingTransaction = (t != null && t.Status != "Done") ? true : false,
}).ToList();
Error : LINQ to Entities does not recognize the method
'System.Linq.IQueryable`1[Transaction] GetAll()' method, and this
method cannot be translated into a store expression."}
Sql Query :
SELECT e.*
(CASE WHEN (t.EmployeeId is not null and t.Status <> 'Done')
THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT)
End) as IsPendingTransaction
FROM Employee e OUTER APPLY
(SELECT t.*
FROM Transactions t
WHERE e.EmployeeId = t.EmployeeId
) t
WHERE e.DeptId = 100;
The issue is that when you work within IQueryable, every statement inside that Linq expression must be understood by EF to be able to be translated to SQL.
Your first repository call returns an IQueryable<Employee> which you are trying to extend by telling it to join on some code called "_transactionRepository.GetAll()" EF doesn't know what this is, it doesn't correlate to mapped DbSets or properties on entities...
If your Transaction entity has a navigation property back to Employee (which it should) you should be able to accomplish what you want using just the TransactionRepository with something like:
var t = _transactionRepository.GetAll()
.Where(t => t.Employee.DeptId == 100)
.Select(t => new
{
IsPendingTransaction = (t != null && t.Status != "Done") ? true : false
}).ToList();
Using IQueryable<TEntity> in a repository pattern can be quite powerful, however I don't recommend adopting a Generic repository pattern as it just serves to fragment your thinking when working with entities and their relationships with one another, allowing EF to manage the resulting SQL without you resorting to pre-emptively trying to do the joining yourself, often causing conflicts with what EF is capable of working out itself.
Edit: Ok, from your description to get a list of employees with a flag if they have a pending transaction: That would be back at the Employee level with a query something like:
var employees = _employeeRepository.GetAll()
.Where(e => e.DeptId == 100)
.Select(e =>
{
Employee = e,
HasPendingTransaction = e.Transactions.Any(t => t.Status != "Done")
}).ToList();
Or projected to a ViewModel to embed the HasPendingTransaction alongside the Employee details:
var employees = _employeeRepository.GetAll()
.Where(e => e.DeptId == 100)
.Select(e => new EmployeeDetailsViewModel
{
EmployeeId = e.EmployeeId,
Name = e.Name,
// include other relevent details needed for the view...
HasPendingTransaction = e.Transactions.Any(t => t.Status != "Done")
}).ToList();
The advantage of projection is you can build more efficient / faster queries that reduce the amount of data sent over the wire and avoid issues like lazy load trips if you try to serialize entities to the view.
Fix Transaction class
public class Transaction
{
public int TRID { get; set; }
public string Status { get; set; }
public int EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
}
It is not the best idea to have a separate repository for each entity since query usually consists from several entities. It is better to make a join using dbcontext then several repository queries as you trying to do. Don't try to create a base generic repository also. Sooner or later you will see that is is not very helpfull. So add in one of your repositories (probably EmployeeRepository) query like this
var employees= dbcontext.Transactions
.Where(t=> t.Employee.DeptId == 100 && t.EmployeeId==employeeId)
.Select (t=> new {
EmployeeName= t.Employee.Name,
IsPendingTransaction = (t.Status != null && t.Status != "Done") ? true : false}).ToList()
I'm trying to retrieve a few records from a table given a certain condition... this is my code:
var areas = _context.Set<T>()
.Where(p => (int)p.GetType().GetProperty(campoOrdem).GetValue(p) >= indexMin &&
(int)p.GetType().GetProperty(campoOrdem).GetValue(p) <= indexMax).ToList();
I am getting this error :
'The LINQ expression 'DbSet<RH_Cargos>
.Where(r => (int)r.GetType().GetProperty(__campoOrdem_0).GetValue(r) >=
__indexMin_1 && (int)r.GetType().GetProperty(__campoOrdem_0).GetValue(r) <=
__indexMax_2)' could not be translated.
All of my variables are getting the correct values.. campoOrdem is the string which contains the name of the field, indexMin and indexMax is my values of order in the database, in the example, indexMin is 1, and indexMax is 2...
what is happening?
Reflection won't work for what you are trying to do, but if the property always has the same name, you could use generic constraints (if you can add that interface to all relevant entities):
public interface IEntityWithCampoOrdem
{
public int CampoOrdem { get; } // property name should always be the same
}
This assumes that entities like RH_Cargos can be defined like so:
public class RH_Cargos : IEntityWithCampoOrdem
{
// other properties
public int CampoOrdem { get; set; }
}
Now you can create a generic method like so:
public void GetAreas<T>() where T : IEntityWithCampoOrdem
{
var areas = _context.Set<T>()
.Where(p => p.CampoOrdem >= indexMin &&
p.CampoOrdem <= indexMax).ToList();
}
I have an object with two objects as properties (User, PrimaryNode), both could potentially be null, see below:
public class Item
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
public Node PrimaryNode { get; set; }
public User User { get; set; }
}
I'm using Entity Framework 6 to populate the Item object and using chained includes to populate the PrimaryNode and User objects within it.
When the first chained Include has a null object then the whole object returns as null, for example:
using (var db = new MyContext())
{
var item = db.Items.Include(i => i.User).Include(n => n.PrimaryNode).FirstOrDefault(i => i.ItemId == id);
}
If in the above example i.User is null then the item variable is null. Whats the best way of populating both the sub-objects in a way that if a sub-object is null then the parent object and the other sub-object will still be populated?
I don't think your issue is due to the Include calls. According with the documentation:
This extension method calls the Include(String) method of the
IQueryable source object, if such a method exists. If the source
IQueryable does not have a matching method, then this method does
nothing.
In other words is going to be translated to:
var item = db.Items.Include("User").Include("PrimaryNode").FirstOrDefault(i => i.ItemId == id);
My question is, are you sure you have an Item with that id properly related with existing rows in Users and PrimaryNodes tables in your DB?. When you call Include method at the end is going to be translated to a join, so if the FK of your relationship doesn't match with the PK that reference, your query should not return what you are expecting.
Anyways, if you want to try another variant to load related properties you can use Explicit Loading:
var item = db.Items.FirstOrDefault(i => i.ItemId == id);
context.Entry(item).Reference(p => p.PrimaryNode).Load();
context.Entry(item).Reference(p => p.User).Load();
I think it would be better if you use Lazy loading int his situation. Just make the User and PrimaryNode virtual:
public class Item
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
public virtual Node PrimaryNode { get; set; }
public virtual User User { get; set; }
}
And then:
var db = new MyContext();
var item = db.Items.FirstOrDefault(i => i.ItemId == id);
As others have mentioned, I think your issue is not due to the Includes. However, I think the following method has value. It is functionally equivalent to what you are already doing with the chained includes, but I think it has several benefits including making the intention of the code clear to the user.
The includes can be placed in Extension methods:
using System.Data.Entity;
using System.Linq;
namespace Stackoverflow
{
public static class EntityExtensions
{
public static IQueryable<Item> IncludePrimaryNode(this IQueryable<Item> query)
{
// eager loading if this extension method is used
return query.Include(item => item.PrimaryNode);
}
public static IQueryable<Item> IncludeUser(this IQueryable<Item> query)
{
// eager loading if this extension method is used
return query.Include(item => item.User);
}
}
}
Then, you can use the extensions as follows:
using (var db = new MyContext())
{
var itemQuery = db.Items.IncludeUser();
itemQuery = itemQuery.IncludePrimaryNode();
var item = itemQuery.FirstOrDefault(i => i.Id == 1);
}
It's just another way of doing the same thing, but I like the clarity it adds to the code.
I am trying to build a select query to a table while only contains concatenated Primary Key values and no attributes. I managed to get the select query alright, but I can't quite figure out how to get the select statement. Basically I am trying to get a list of DegreeID's, from the DegreeRelationship table, (which is mapped and not an entity) from a ProgramID in DiplomaCertificate entity. Then I want to get the Degree name as well.
My context with the mapped tables looks like this:
modelBuilder.Entity<Degree>()
.HasMany(e => e.DiplomaCertificates)
.WithMany(e => e.Degrees)
.Map(m => m.ToTable("DegreeRelationship").MapLeftKey("DegreeID").MapRightKey("ProgramID"));
and basically, I am trying to put values into this object:
public class DegreeRelationshipInfo
{
public int ProgramID { get; set; }
public int DegreeID { get; set; }
public string LinkedProgramName { get; set; }
}
I am trying a method something like this, but I am not sure how to write this exactly (and this is completely wrong):
[DataObjectMethod(DataObjectMethodType.Select, false)]
public List<DegreeRelationshipInfo> Select_DegRel(int programID)
{
using (Pathway_Model context = new Pathway_Model())
{
var results = from data in context.Degrees
where data.DiplomaCertificates.Where(x => x.ProgramID == programID)
select new DegreeRelationshipInfo
{
ProgramID = data.ProgramID,
// no idea how to get this value....
};
return results.ToList();
}
}
Any help would be appreciated!
Select the entities by SelectMany and collect their key values:
from data in context.Degrees
from cert in data.DiplomaCertificates
select new DegreeRelationshipInfo
{
ProgramID = data.ProgramID,
DegreeID = cert.DegreeID,
LinkedProgramName = data.Name // I guess...
}
This from - from construction is compiled into SelectMany, but the syntax is much better readable.
Can I make my EF objects retrieve only specific columns in the sql executed?
If I have a column that contains a large amount of data that really slows down the query, how can I have my objects exclude that column from the sql generated?
If my table has Id(int), Name(int), Data(blob), how can I make my query be
select Id, Name from TableName
instead of
select Id, Name, Data from TableName
From the suggestion below, my method is
public List<T> GetBy<T>(DbContext context,Expression<Func<T, bool>> exp, Expression<Func<T,T>> columns) where T : class
{
return dbContext.Set<T>().Where(exp).Select<T,T>(columns).ToList();
}
And I'm calling it like so
List<CampaignWorkType> list = GetBy<CampaignWorkType>(dbContext, c => c.Active == true, n => new { n.Id, n.Name });
i got an error like below.
Cannot implicitly convert type 'AnonymousType#1' to 'Domain.Campaign.CampaignWorkType'
how i can solve this?
The solution is:
First, define a surrogate type:
public class CampaignWorkTypesSimpleList
{
public int Id { get; set; }
public string Name { get; set; }
}
Then change generic method like this:
public List<U> GetBy<T,U>(DbContext context,Expression<Func<T, bool>> exp, Expression<Func<T,U>> columns)
where T : class
where U : class
{
return dbContext.Set<T>().Where(exp).Select<T, U>(columns).ToList();
}
Finally, execute it.
List<CampaignWorkTypesSimpleList> list = this.GetBy<CampaignWorkType, CampaignWorkTypesSimpleList>(dbContext, c => c.Active == true, n => new CampaignWorkTypesSimpleList { Id = n.Id, Name = n.Name });
Assuming you are using the EF designer, remove the [Data] column from the Entity Model using the Entity Model design surface.