I have the following entities:
public class Order
{
public int Id { get; set; }
public ICollection<LineItem> LineItems { get; set; }
}
public class LineItem
{
public int Id { get; set; }
public int Qty { get; set; }
}
I would like to just check if an order has large items (qty > 50), so I would like to add a property to Order class like so:
public class Order
{
public int Id { get; set; }
public ICollection<LineItem> LineItems { get; set; }
public bool HasLargeItems
{
get
{
return LineItems.Any(l => l.Qty > 50);
}
}
}
This however, doesn't work because of lazy-loading. But if I use .Include(o => o.LineItems), it will fetch all the LineItems and then do an in-memory comparison. I would rather have this generate and execute a query against the db and just get me back a boolean value.
Is this possible to do as a property of Order entity, or do I have to write some other external query object which uses DbContext to perform these kinds of checks?
One way to do this is using anonymous select
var order = dbContext.Orders
.Where(o => o.Id == id)
.Select(o => new { Order = o, HasLargeItems = o.LineItems.Count > 50 })
.SingleOrDefault();
Try this it might help:
public bool HasItems
{
get{return LineItems.AsQueryable().Any(x => false);}
}
Related
Basically I have this class
public class Gasto
{
public int IdTienda { get; set; }
public int IdGasto { get; set; }
public System.DateTime Fecha { get; set; }
public string ConceptoDeGasto { get; set; }
public double Total { get; set; }
public string TipoDeGasto { get; set; }
public Nullable<int> IdVenta { get; set; }
public Nullable<System.DateTime> FechaVenta { get; set; }
public virtual Tienda Tienda { get; set; }
}
And I'm trying to build a ViewModelClass like this
public class CorteConVentas
{
// STILL NO ATRIBUTE -- THIS IS THE QUESTION
}
Here is the code for the controller where I will build a List of Gasto grouped by TipoDeGasto
var gastos = db.Gastos.Where(g => g.IdGasto >= corte.DesdeIdGasto && g.IdGasto <= corte.HastaIdGasto).ToList();
var GD = gastos.GroupBy(u => u.TipoDeGasto).Select(grp => new { TipoGasto = grp.Key, gastos = grp.ToList() } ).ToList();
As you can see the variable "GD" is a List of Strings (TipoGasto) with List of Gasto.
¿The issue (question) is this GD how can I declare it as an attribute of my viewModelClass?
I tried something like this for the ViewModel
public class CorteConVentas
{
public List<string, List<Gasto>> listaGastosAgrupada { get; set; }
}
But there is something wrong. The output of the error says:
Using the generic type List requires 1 type arguments
Here is the output after grouping by
Finally the solution as #Ziv Weissman said was not to use an anonymous type
So I created a class like this
public class CorteConVentas
{
public List<GastosAgrupados> listaGastosAgrupada { get; set; }
}
public class GastosAgrupados
{
public string TipoGasto { get; set; }
public List<Gasto> gastos { get; set;}
}
And then in the controller when creating the grouped list I did this
var gastos = db.Gastos.Where(g => g.IdGasto >= corte.DesdeIdGasto && g.IdGasto <= corte.HastaIdGasto).ToList();
var gd = gastos.GroupBy(u => u.TipoDeGasto).Select(grp => new GastosAgrupados { TipoGasto = grp.Key, gastos = grp.ToList()) } ).ToList();
Thanks to all for helping me.
You cannot declare a variable of anonymous type:
.Select(grp => new { TipoGasto = grp.Key, gastos = grp.ToList() } )
You must create another class which has these two props.
(or use a KeyValuePair)
Something like -
.Select(grp => new KeyValuePair<string,List<Gasto>> { Key = grp.Key, Value = grp.ToList() } )
Then you can create a strong typed prop.
public class CorteConVentas
{
List<KeyValuePair<string,List<Gasto>>> PropName {get; set;}
}
Simple answer, just look up GroupBy() in the docs, and see what it returns:
IEnumerable<IGrouping<TKey, TElement>>
which is your case would be:
public class CorteConVentas
{
public IEnumerable<IGrouping<string, Gasto>> listaGastosAgrupada { get; set; }
}
Update:
Didn't notice the select after the GroupBy(). Try this:
public class GrupoGastos // forgive my attempts at Spanish via Google Translate
{
public string TipoGasto {get; set;}
public List<Gasto> Gastos {get; set;}
}
then
var GD = gastos.GroupBy(u => u.TipoDeGasto)
.Select(grp => new GrupoGastos
{ TipoGasto = grp.Key, Gastos = grp.ToList() } )
.ToList();
and finally:
public class CorteConVentas
{
public List<GrupoGastos> listaGastosAgrupada { get; set; }
}
First, looks like you want a dictionary, not a list:
public class CorteConVentas
{
public Dictionary<string, List<Gasto>> dictaGastosAgrupada { get; set; }
}
Second, that select list with two columns is going to give you trouble because it comes out as an anonymous type, and you have no way to declare a dictionary that will contain it. Instead, just return the object:
var GD = gastos.GroupBy(u => u.TipoDeGasto).Select(grp);
So now you have GD which will let you enumerate over a list of grp objects. You can add them to your dictionary like this:
foreach (var grp in GD) dictaGastosAgrupada.Add(grp.Key, grp.ToList());
I have the following classes:
public class Seller : Entity
{
public int SellerId { get; set; }
public string Name { get; set; }
public ICollection<InventoryItem> InventoryItems { get; set; }
}
public class InventoryItem : Entity
{
public int InventoryId { get; set; }
public int SellerId { get; set; }
public string SellerSku { get; set; }
public ICollection<SiteInventoryItem> SiteInventoryItems { get; set; }
}
public class SiteInventoryItem : Entity
{
public int Id { get; set; }
public int InventoryId { get; set; }
public SiteType Site { get; set; }
}
So a Seller has a collection of InventoryItem which in turn have a collection of SiteInventoryItem.
How do I get a Seller with a list of InventoryItems that have a list of SiteInventoryItems where the SiteType == SiteType.SiteName and the Seller.SellerId == 14?
The SiteType is an Enum and the db type is int.
I have tried the following and it compiles and runs, but the result is not what is expected:
var seller = repo.Query(
x => x.InventoryItems,
x => x.InventoryItems.Select(y => y.SiteInventoryItems)
).Select(x => new
{
Seller = x,
InventoryItems = x.InventoryItems,
SiteInventoryItems = x.InventoryItems.Select(y => new
{
InventoryItem = y,
SiteInventoryItems = y.SiteInventoryItems.Where(z => z.Site == SiteType.Amazon)
})
}).Where(x => x.Seller.SellerId == 14).First();
var seller = repo.Query()
.Select(x => new
{
Seller = x,
InventoryItems = x.InventoryItems.Select(y => new
{
InventoryItem = y,
SiteInventoryItems = y.SiteInventoryItems.Where(z => z.Site == SiteType.Amazon)
}).Where(y => y.SiteInventoryItems.Any())
}).Where(x => x.Seller.Id == 14).First();
I've restructured the result type a bit, because I think it makes more sense this way. Now you see that the result only returns Sellers that have InventoryItems that have SiteInventoryItems.
Note that I also removed the arguments for repo.Query(). I assume that they serve as Includes. But I also hope that repo.Query() returns an IQueryable, so that the Where clause will be translated into the generated SQL. If so, the Includes are useless, because you're already querying the included entities because they are part of the anonymous type.
I have this scenario:
I want to make a ViewModel with the property that I only want but my issue is with the Collections. So here's an example:
Main classes:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Order> Orders { get; set; }
// remove some code for brevity
}
public class Order
{
public int Id { get; set; }
public string ItemName { get; set; }
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
// remove some code for brevity.
}
View Models:
public class OrderVM
{
public string ItemName { get; set; }
}
public class CustomerVM
{
public string Name { get; set; }
public ICollection<OrderVM> Orders { get; set; }
}
Then I tried something like this:
_customerService.Include(c => c.Orders)
.Select(x => new CustomerVM
{
Name = x.Name,
Orders = x.Orders.Select(order => new OrderVM { ItemName = order.ItemName }) // but it says cannot convert implicitly from IEnumerable to ICollection
}
)
In a nutshell, how can I populate CustomerVM's properties? I only want to select that I want. Any thoughts? Really stuck here.
Linq .Select() generates IEnumerable<T> but your property is ICollection<T>. You can append .ToList() to the query to generate List<T> which is ICollection<T>.
customerService.Include(c => c.Orders)
.Select(x => new CustomerVM
{
Name = x.Name,
Orders = x.Orders
.Select(order => new OrderVM { ItemName = order.ItemName }).ToList()
}
Alternatively, if you do not specifically need Orders to be ICollection, then you could change your property definition to
public IEnumerable<OrderVM> Orders { get; set; }
just use ToList() to convert IEnumerable to ICollection. Not tested but it should work
customerService.Include(c => c.Orders)
.Select(x => new CustomerVM
{
Name = x.Name,
Orders = x.Orders.Select(order => new OrderVM { ItemName = order.ItemName }).ToList()
}
I have a SQL Server 2012 database and I am using Entity Framework 6.1 to access data.
I have the following entities that are joined to each other with Primary Key and Foreign Key relationships. What I would like to do is to get a simple collection of values of the Quid parameter in the Questions table where the taskId matches a value that is chosen outside of this code.
Task contains Objective contains ObjectiveDetail contains SubTopic contains Problems contations
Questions
I created the following LINQ statement. It seems not to work and I need some advice on what I am doing wrong and how the statement could be made to get what I need. In particular I am not sure about the way I do the join with all the many .Selects and also the select to give me the output.
var quids = db.Tasks
.Include(e => e.Objectives
.Select(o => o.ObjectiveDetails
.Select(od => od.SubTopics
.Select(s => s.Problems
.Select(p => p.Questions)))))
.Where(t => t.TaskId == taskId)
.Select(e => e.Objectives
.Select(o => o.ObjectiveDetails
.Select(od => od.SubTopics
.Select(s => s.Problems
.Select(p => p.Questions
.Select(q => q.QuestionUId))))))
.ToList();
However this gives me a very confusing output and certainly not the simple IList of Quids
that I would like. Here are my classes for reference. I removed additional fields and hopefully just left the important ones.
public class Task
{
public Task()
{
this.Objectives = new HashSet<Objective>();
}
public int TaskId { get; set; }
public virtual ICollection<Objective> Objectives { get; set; }
}
public class Objective
{
public Objective()
{
this.ObjectiveDetails = new HashSet<ObjectiveDetail>();
}
public int ObjectiveId { get; set; }
public int TaskId { get; set; }
public virtual Task Task { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
public partial class ObjectiveDetail
{
public ObjectiveDetail()
{
this.SubTopics = new HashSet<SubTopic>();
}
public int ObjectiveDetailId { get; set; }
public int ObjectiveId { get; set; }
public virtual Objective Objective { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public class SubTopic
{
public SubTopic()
{
this.Problems = new HashSet<Problem>();
}
public int SubTopicId { get; set; }
public int Number { get; set; }
public int TopicId { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
public virtual ICollection<Problem> Problems { get; set; }
}
public class Problem
{
public Problem()
{
this.Questions = new HashSet<Question>();
}
public int ProblemId { get; set; }
public int SubTopicId { get; set; }
public virtual SubTopic SubTopic { get; set; }
public virtual ICollection<Question> Questions { get; set; }
}
public class Question
{
public int QuestionId { get; set; }
public string QuestionUId { get; set; }
public virtual Problem Problem { get; set; }
}
Please note the many-many relationship between SubTopic and ObjectiveDetail. This I think makes it more difficult.
From what I understand of your need, you can simply start from the Question DbSet instead of the task:
var uids = db.Questions.Where(q => q.Problem.SubTopic
.ObjectiveDetails
.Any(od => od.Objective.TaskId == taskId))
.Select(q => q.QuestionUId)
.ToList();
This will take all Question which has at least one objective with the TaskId equal to taskId then project the QuestionUid and put it in a list.
Each of your outer Select calls produces a sequence for each item. This gives you nested sequences and probably a confusing ToString debug output.
What you probably meant was to use SelectMany to flatten the nested sequences.
The Include in your query cannot possibly help because you are just returning a list of strings. There is nothing to include in a string. Better remove it. Who knows what code EF generates from it.
I have three SQL tables that are represented by classes and I would like to have Entity Framework 6 join these tables so I get all the details of the Exam, Test and UserTest tables where the UserTest.UserID is 0 or X.
I have already set up a respository and this works for simple queries however I am unable to join the UserTest class in the LINQ at the bottom of the question.
Here's my classes:
public class Exam
{
public int ExamId { get; set; }
public int SubjectId { get; set; }
public string Name { get; set; }
public virtual ICollection<Test> Tests { get; set; }
}
public class Test
{
public int TestId { get; set; }
public int ExamId { get; set; }
public string Title { get; set; }
public virtual ICollection<UserTest> UserTests { get; set; }
}
public class UserTest
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int TestId { get; set; }
public int QuestionsCount { get; set; }
}
What I would like to do is to have a query that looks something like this:
var exams = _examsRepository
.GetAll()
.Where(q => q.SubjectId == subjectId)
.Include(q => q.Tests )
.Include(q => q.Tests.UserTests) // Error on this line
.ToList();
But it's not letting me include UserTests in VS2013.
Update:
Here is the query I first tried:
var userTests = _userTestsRepository
.GetAll()
.Include(t => t.Test)
.Include(t => t.Test.Exam)
.Where(t => t.UserId == "0" || t.UserId == userId);
This one seemed to work however when I looked at the output I saw something like this:
[{"userTestId":2,
"userId":"0",
"testId":12,
"test":{
"testId":12,"examId":1,
"exam":{
"examId":1,"subjectId":1,
"tests":[
{"testId":13,"examId":1,"title":"Sample Test1",
"userTests":[
{"userTestId":3,
"userId":"0",
Note that this starts to repeat and bring back a lot more data than I expected
That's because Tests is a collection and not just a single object, so it doesn't have a UserTests property. You use a lambda to specify grandchildren of multiple children rather than a single child:
var exams = _examsRepository
.GetAll()
.Where(q => q.SubjectId == subjectId)
.Include(q => q.Tests.Select(t => t.UserTests))
.ToList();
Note that there's no need for two Include calls because the children are implicitly included if you're including the grandchildren.