Entity Framework Include without query - c#

Can I include a related object without a query?
With query:
Item item = _db.Items
.Include(i=>i.Supplier)
.Where(....)
Without query:
var item = new Item { Name = "Test", SupplierId = 1 };
item.Include(i => i.Supplier); //something like that...

I don't really understand your question...
First of all Where return multiple objects
IQueryable<Item> items = _db.Items
.Include(i=>i.Supplier)
.Where(....)
Then the result is an IQueryable, the items and suppliers objects are not materialized for the moment. You have to use ToList() for example to enable materialization and query the database.
For the Include method, it's just a join to query Items and Suppliers relationship.
But the Include extension method is only available in IQueryable and not for the entity.
Supplier is normal a simple navigation property on item entity
class Item
{
public virtual Supplier Supplier {get; set;}
}
so you can access using
var item = new Item { Name = "Test", SupplierId = 1 };
item.Supplier = ....
if you want establish a relation with you have to get the supplier
item.Supplier = _db.Suppliers.First(s => s.SupplierId = 1);

Related

Select Multiple Values from a Collection using Lambda Expression

How do I select two or more values from a collection into a list using a single lambda expression? Here is what I am trying:
List<Prodcut> pds=GetProducts();
List<Product> pdl = new List<Product>();
foreach (Product item in pds)
{
pdl.Add(new Product
{
desc = item.Description,
prodId = Convert.ToInt16(item.pId)
});
}
GetProducts() returns a list of Products that have many (about 21) attributes. The above code does the job but I am trying to create a subset of the product list by extracting just two product attributes (description and productId) using a single lambda expression. How do I accomplish this?
What you want to do is called projection, you want to project each item and turn them into something else.
So you can use a Select:
var pdl = pds.Select(p => new Product
{
desc = p.Description,
prodId = Convert.ToInt16(p.pId)
}).ToList();

Breeze Framework - how to do an inline Subtable Count query

I have a table Supplier, with subtable 1 to many Product. I would like to replicate the results of this query in Breeaze:
SELECT *,
(select count(*) from Product where Supplier.id = Product.SupplierId) ProductCount
FROM Supplier
Basically, I'd like to have an output of the Supplier data columns, with an appended column of that supplier's product count.
I currently have this query in Breeze which gives me suppliers, but I don't see a way to add the count column to the results. I already have a field in the Entity ProductCount in place and NonMappable to contain it:
var query = _repository.Suppliers.AsQueryable();
if (supplierIds.Length > 0)
{
query = query.Where(supplier => supplierIds.Contains(supplier.Id));
var result = query.ToList();
}
return query;
What am I missing? Is there a way to do this in Breeze or no?
Thanks for the assistance!
Have a look at the inlineCount property in Breeze query structure :
successFunction([data]) {
....
}
you can get :
results : fields of the query (array)
inlineCount :
Only available if 'inlineCount(true)' was applied to the query. Returns the count of items that would have been returned by the query before applying any skip or take operators, but after any filter/where predicates would have been applied.
For instance :
var query = new EntityQuery("Clients")
.where("ClientName", "startsWith", "B")
.take(20)
.orderBy("ClientName")
.inlineCount(true);
The result
query.execute().then( function(data) {
data.results
data.inlineCount
}
The data.inlineCount column will return 12 if your query contains 12 clients name started with "B" even if the total records returned may totalized 20.
If you already have a ProductCount property on your Supplier entity, you just need to return it from the server. If ProductCount is on the server-side entity, you can populate the property on the server and the client side will just work:
[HttpGet]
public IQueryable<Supplier> SuppliersWithProductCount()
{
var query = _repository.Suppliers.AsQueryable();
// ...add server-side params to the query, if desired...
// Get objects with the supplier and the count
var list = query.Select(s => new { Supplier = s, Count = s.Products.Count }).ToList();
// Set the count on the Supplier
list.ForEach(o => o.Supplier.ProductCount = o.Count);
// Extract the Suppliers
var suppliers = list.Select(o => o.Supplier);
return suppliers.AsQueryable();
}
On the other hand, if the ProductCount property only exists on the client, you will need to pass it to the client separately, and set the property on the client-side entities in your query result handler.
On the server:
[HttpGet]
public IQueryable<Object> SuppliersWithProductCount()
{
var query = ContextProvider.Context.Suppliers.AsQueryable();
// ...add server-side params to the query, if desired...
// Get objects with the supplier and the count
return query.Select(s => new { Supplier = s, Count = s.Products.Count });
}
On the client:
EntityQuery.from("SuppliersWithProductCount")
.using(myEntityManager).execute()
.then(function (data) {
// results are { Supplier, Count } objects
var results = data.results;
var suppliers = [];
results.forEach(function (r) {
r.Supplier.ProductCount = r.Count;
suppliers.push(r.Supplier);
});
return suppliers;
});

LINQ aggregate multiple tables

I have the following database. A list of companies. Each company has multiple employees and multiple contractors.
dbo.Companies (CompanyId, Name)
dbo.Employees (Id, CompanyId, Name ...)
dbo.Contractors(Id, CompanyId, Name...)
I want to get output like so
CompanyName #Employees #Contractors
abc 0 10
xyz 25 999
I am trying to avoid doing 2 queries, one to get contractors and one to get employees and then merging them. Is there a way to get it done in one go?
n.b. i have
class CompanySummary{
string Name {get; set;}
int EmpCount {get; set;}
int ConCount {get; set;}
}
so I can use a collection of this type as result
If you have defined navigation properties (and if you haven't, may be it's a good time to do that), the query should be quite simple:
var query = from c in db.Companies
select new CompanySummary
{
Name = c.Name,
EmpCount = c.Employees.Count(),
ConCount = c.Contractors.Count(),
};
Of course you can do that manually, but the above is the preferred way with EF:
var query = from c in db.Companies
select new CompanySummary
{
Name = c.Name,
EmpCount = db.Employees.Count(e => e.CompanyId == c.Id),
ConCount = db.Contractors.Count(cc => cc.CompanyId == c.Id),
};
In both cases you'll get a single SQL query.
If you are using Entity Framework to communicate with the database and have the tables linked with foreign keys you can probably do it in one query. It would look something like this:
IEnumerable<CompanySummary> companySummary = null;
using (CompanyEntities dbContext = new CompanyEntities())
{
companySummary = dbContext.Companies
.Include(company => company.Employees)
.Include(company => company.Contractors)
.Select(company => new CompanySummary
{
Name = company.Name,
EmpCount = company.Employees.Count(),
ConCount = company.Contractors.Count()
});
}

Combining Tables With Different Data Using Linq in MVC?

I have Two classes Named OfflineOrderLineItem.cs and OnlineOrderLineItem.cs both have diff Order table named offline and Online
In that i want to Combine the two tables data to search and Display the Fields from both tables
How to do that using linq in mvc4 ??? any idea.....
public virtual IPagedList<OnlineOrderLineItem> SearchOrderLineItems(string PoNumber)
{
var query1 = (from ol in _offlineOrderLineItemRepository.Table
select new
{
ol.Name
}).ToList();
var query2 = (from opv in _onlineOrderLineItemRepository.Table
select new
{
opv.Name
}).ToList();
var finalquery = query1.Union(query2);
if (!String.IsNullOrWhiteSpace(Name))
finalquery = finalquery.Where(c => c.Name == Name);
var orderlineitems = finalquery.ToList(); //its not working it throw a error
return new PagedList<OnlineOrderLineItem>(orderlineitems);//error
}
Error
cannot convert from 'System.Collections.Generic.List<AnonymousType#1>'
to 'System.Linq.IQueryable<Nop.Core.Domain.Management.OnlineOrderLineItem>'
to 'System.Linq.IQueryable<Nop.Core.Domain.Management.OnlineOrderLineItem>'
query1 and query2 are lists of an anonymous type with a single property of type string. (I assmume the ol.Name and opv.Name are strings.) Hence finalQuery and orderlineitems are collections of this anonymous as well. By specifying PagedList<T> you require that the collection passed into the constructor is an enumeration of type T. T is OnlineOrderLineItem, but the enumeration passed into the constructor is the anonymous type which is a different type. Result: compiler error.
To solve the problem I suggest that you define a named helper type that you can use to union the two different types OfflineOrderLineItem and OnlineOrderLineItem:
public class OrderLineItemViewModel
{
public int Id { get; set; }
public string PoNumber { get; set; }
public string Name { get; set; }
// maybe more common properties of `OfflineOrderLineItem`
// and `OnlineOrderLineItem`
}
Then your SearchOrderLineItems method should return a paged list of that helper type:
public virtual IPagedList<OrderLineItemViewModel> SearchOrderLineItems(
string PoNumber)
{
var query1 = from ol in _offlineOrderLineItemRepository.Table
select new OrderLineItemViewModel
{
Id = ol.Id,
PoNumber = ol.PoNumber,
Name = ol.Name,
// maybe more properties
};
// don't use ToList here, so that the later Union and filter
// can be executed in the database
var query2 = from opv in _onlineOrderLineItemRepository.Table
select new OrderLineItemViewModel
{
Id = opv.Id,
PoNumber = opv.PoNumber,
Name = opv.Name,
// maybe more properties
};
// don't use ToList here, so that the later Union and filter
// can be executed in the database
var finalquery = query1.Union(query2);
// again no ToList here
if (!string.IsNullOrWhiteSpace(PoNumber))
finalquery = finalquery.Where(c => c.PoNumber == PoNumber);
var orderlineitems = finalquery.ToList(); // DB query runs here
return new PagedList<OrderLineItemViewModel>(orderlineitems);
}
It is important to use ToList only at the very end of the query. Otherwise you would load the whole tables of all OnlineOrderLineItems and all OfflineOrderLineItems into memory and then filter out the items with the given PoNumber in memory which would be a big overhead and performance desaster.
Instead of
var orderlineitems = finalquery.ToList();
Try
var orderlineitems = finalquery.AsQueryable();
From https://github.com/TroyGoode/PagedList/blob/master/src/PagedList/PagedList.cs, PagedList takes a IQueryable<T>
Queryable.AsQueryable<TElement> Method

How to add to linq2sql entity set for master detail if I can't explicitly define the detail?

Maybe I'm going about this the wrong way...
I have an Order table and an OrderItem table. I create a new Order using linq2sql generated classes.
I then attempt to get all orderable items out of my database using a query that goes after various tables.
I try to then create a new list of OrderItem from that query, but it squawks that I can't explicitly create the object.
Explicit construction of entity type OrderItem in query is not allowed.
Here is the query:
return (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new OrderItem()
{
OrderItemId = im.ItemId
});
The idea is to populate the database with all orderable items when a new order is created, and then display them in a grid for updates. I'm taking the results of that query and attempting to use AddRange on Order.OrderItems
Is there a proper strategy for accomplishing this using linq2sql?
Thanks in advance for your help.
From my understanding of L2S, I don't think you can use explicit construction (in other words new SomeObj() { ... }) in a query because you aren't enumerating the results yet. In other words, the query has just been built, so how are you supposed to do this:
SELECT new OrderItem() FROM MasterItems im JOIN Categories c on c.CATEGORY1 = im.CATEGORY
This is what you're trying to do, which doesn't work because you can't return a POCO (unless you join the OrderItem back somehow and do OrderItem.* somewhere). Ultimately, what you would have to do is just enumerate the collection (either in a foreach loop or by calling ToList()) on the query first and then build your OrderItem objects.
var query = (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new { MasterItem = im, Category = c});
List<OrderItem> returnItems = new List<OrderItem>();
foreach(var item in query)
{
returnItems.Add(new OrderItem() { OrderItemId = item.MasterItem.ItemId });
}
return returnItems;
OR
return (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new { MasterItem = im, Category = c})
.ToList()
.Select(tr => new OrderItem() { OrderItemId = tr.MasterItem.ItemId });
Try that out and let me know if that helps.
Expand the order class by creating a partial file where that class OrderItem now has property(ies) which lend itself to business logic needs, but don't need to be saved off to the database.
public partial class OrderItem
{
public int JoinedOrderItemId { get; set; }
public bool HasBeenProcessed { get; set; }
}

Categories