Breeze Framework - how to do an inline Subtable Count query - c#

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;
});

Related

Advanced database query

I have this method:
public async Task<ProductReport<ProductPrice>> GetCurrentProductsReport()
{
var query = (DataServiceQuery<ProductPrice>)(
from p in this.entities.Products
where !p.Discontinued
orderby p.ProductName
select new ProductPrice
{
Name = p.ProductName,
Price = p.UnitPrice ?? 0,
});
var result = await Task<IEnumerable<ProductPrice>>.Factory.FromAsync(query.BeginExecute(null, null), (ar) =>
{
return query.EndExecute(ar);
});
return new ProductReport<ProductPrice>(result);
}
This method returns an incomplete list of products. The number of items in the list is limited by the number of items that the service returns in one request.
I need to get all data.
I know I can use the GetContinuation() method, but I can't use it in this situation.
Maybe someone knows how to solve this problem?
I need to get all data.
Create a stored procedure and extract the full total data rows/columns as needed.

MVC 5 - Why is my linq code selecting an entire query instead of a single value?

Here is an example of what some locations look like in my database:
ID, DepartmentId, LocationName
8,2,Main Warehouse
12,2, Filter Cellar
When I use the following code, it grabs the entire query and puts it as the value for my HTML dropdown list.
public ActionResult GetLocations(int id)
{
List<SelectListItem> locations = new List<SelectListItem>();
//based on the input coming to this method ( product id)
var incident = new AccidentSupervisorViewModel();
incident.Locations = DB.Locations.ToList().Select(i => new SelectListItem
{
Text = i.LocationName,
Value = i.DepartmentId.ToString()
}
).Where(i => i.Value == id.ToString());
var departmentId = from loc in DB.Locations where loc.DepartmentId == id select loc.DepartmentId;
for (var x = 0; x < incident.Locations.Count(); x++) {
locations.Add(new SelectListItem {
Text = incident.Locations.ElementAt(x).Text,
Value = departmentId.ToString()
});
}
return Json(locations,JsonRequestBehavior.AllowGet);
}
This is most likely happening because I have a syntax error, but I haven't used linq much for queries so any help is appreciated.
It appears you've not 'done' anything with the IQueryable<T> that is generated by Linq. Linq generates the query but doesn't do anything with it until the IQueryable<T> or other IEnumerable is iterated over. See Deferred Execution and Classification of Standard Query Operators by Manner of Execution.
In your case, since you're looking for a single value, you'll need to pop in the following line after you first declare departmentId:
var department = departmentId.FirstOrDefault();
This will pop out the first or default value from the IQueryable<T> you made.

Entity Framework Include without query

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);

LINQ to Entities does not recognize the method 'System.String ToString()' method in MVC 4

I'm working with MVC 4 and I have to update my database using Code First Migrations. What I'm trying to do is to select records from a database table, and insert them into a dropdown list where the user can select one.
I have an error that I don't understand:
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
Controller:
public ActionResult Addnew()
{
var dba = new DefaultConnection();
var query = dba.blob.Select(c => new SelectListItem
{
Value = c.id.ToString(),
Text = c.name_company,
Selected = c.id.Equals(3)
});
var model = new Companylist
{
xpto = query.AsEnumerable()
};
return View(model);
}
You got this error because Entity Framework does not know how to execute .ToString() method in sql. So you should load the data by using ToList and then translate into SelectListItem as:
var query = dba.blob.ToList().Select(c => new SelectListItem
{
Value = c.id.ToString(),
Text = c.name_company,
Selected = c.id.Equals(3)
});
Edit: To make it more clear, Entity framework converts your use of query operators such as Select, Where ETC into an sql query to load the data. If you call a method like ToString(), which Entity Framework does not have an equivalent in sql, it will complain. SO the idea is to defer the use of such functions post data load. ToList, ToArray ETC force execution of the query thus loading of data. Once the data is loaded, any further operation (such as Select, Where ETC) is performed using Linq to Objects, on the data already in memory.
What if... you use:
Value = c.id + "",
instead of
Value = c.id.ToString(),
Edit
With this option, you are not retrieving all data from Database
just use delegate :
var query = dba.blob.Select(delegate(blob c)
{
return new SelectListItem
{
Value = c.id.ToString(),
Text = c.name_company,
Selected = c.id.Equals(3)
};
});
Following is how I do it to display as a SelectList.
public List<BlobEntity> GetBlobs()
{
List<BlobEntity> blobs = null;
using (var db = new MyDBEntities())
{
blobs = (from b in db.blobs
where b.id > 0 //Example filter
select new BlobEntity
{
ID = b.id,
CompanyName = b.name_company
}
).ToList();
}
return blobs;
}
public static SelectList GetBlobsSelectList()
{
MyBL theBL = new MyBL();
List<BlobEntity> blobEntites = theBL.GetBlobs();
var listItems = blobEntites
.Select(x => new SelectListItem { Text = x.CompanyName,
Value = x.ID.ToString()
})
.ToList();
SelectList blobsSelectList = new SelectList(listItems.AsEnumerable(), "Value", "Text");
return blobsSelectList;
}
public class BlobEntity
{
public int ID { get; set; }
public string CompanyName { get; set; }
}
The current accepted answer (by #VarunK) is okay if you are selecting all records and all columns. However, if that is not the case, it is better to do a projection with required columns and records before applying ToList().
Take a look at Why LINQ to Entities does not recognize the method 'System.String ToString()?.
Other references: Problem with converting int to string in Linq to entities

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

Categories