I am working on getting the results of this sql query in LINQ
SELECT DISTINCT(Type)
FROM Product
WHERE categoryID = #catID
this is my repository query:
public IQueryable<ProdInfo> GetProdInfo()
{
var data = from u in db.Prod
select new ProdInfo
{
PID = u.PID,
CatID = u.CatID,
LastChanged = u.LastChanged,
ChangedBy = u.ChangedBy,
Type = u.Type,
};
return data;
}
filter:
public static IQueryable<ProdInfo> GetDistinctProdType(this IQueryable<ProdInfo> qry,int CatID)
{
return from p in qry
where p.CatID.Equals(CatID)
select p;
}
I need the filter to return the distinct prod type? How can i do this?
Simply like this:
public static IQueryable<ProdType> GetDistinctProdType(
this IQueryable<ProdInfo> query,
int categoryId)
{
return (from p in query
where p.CatID == categoryId
select p.Type).Distinct();
}
Note that I've changed the return type - it should match whatever the type of ProdInfo.Type is.
You may find it more readable to use the extension methods for the whole query if the query expression itself is reasonably simple:
public static IQueryable<ProdType> GetDistinctProdType(
this IQueryable<ProdInfo> query,
int categoryId)
{
return query.Where(p => p.CatID == categoryId)
.Select(p => p.Type)
.Distinct();
}
return (from p in qry
where p.CatId.Equals(CatID)
select p.Type).Distinct();
This matches what your provided SQL Query should be doing.
Related
I have a table with orders(around 8000 records),
The table takes a few seconds to load.
The reason for that is because one of the field shown on the page is being retrieved from another table
(returnProductName).
when removing this function the table loads fast.
When loading the records I'm using Skip and Take but when retrieving the product name i'm iterating all the Orders since if the user wants to search by product name it will show all results with this product.
The product table is not big (around 70 records)
I can't figure out why the function will make the page load so slow.
I know i can just add the product name column to the table and populate it when ever adding new orders,
but this doesn't sounds right,
Can anyone tell me the reason for this delay?
returnProductName Function :
public string returnProductName(int productId)
{
return (_unitOfWork.Product.GetAll().Where(q => q.Id == productId).Select(q =>
q.ProductName)).FirstOrDefault();
}
Function that loads the page data:
[HttpPost]
public ActionResult GetList()
{
//Server Side parameters
int start = Convert.ToInt32(Request.Form["start"].FirstOrDefault());
int length = Convert.ToInt32(Request.Form["length"].FirstOrDefault());
string searchValue = Request.Form["search[value]"].FirstOrDefault();
string sortColumnName = Request.Form["columns["+Request.Form["order[0][column]"]+"][name]"].FirstOrDefault();
string sortDirection = Request.Form["order[0][dir]"].FirstOrDefault();
List<Order> orderList = new List<Order>();
orderList = _unitOfWork.Order.GetAll().ToList();//Working Fast
int totalRows = orderList.Count;
foreach (Order order in orderList)
{
order.ProductName = returnProductName(order.ProductId);
}
if (!string.IsNullOrEmpty(searchValue))
{
orderList = orderList.Where(x => x.FullAddress.ToLower().Contains(searchValue.ToLower())
x.Id.ToString().Contains(searchValue.ToLower()) ||
x.OrderStatus.ToLower().Contains(searchValue.ToLower()) ||
x.ProductName.ToLower().Contains(searchValue.ToLower()) |||
x.Quantity.ToString().Contains(searchValue.ToLower()) ||
x.Cost.ToString().Contains(searchValue.ToLower()) ||
(!string.IsNullOrEmpty(x.TrackingNumber) && x.TrackingNumber.ToString().Contains(searchValue.ToLower()))
).ToList<Order>();
}
int totalRowsAfterFiltering = orderList.Count;
orderList = orderList.Skip(start).Take(length).ToList<Order>();
return Json(new { data = orderList, draw = Request.Form["draw"], recordsTotal = totalRows ,
recordsFiltered = totalRowsAfterFiltering});
}
I would perhaps consider updating the GetAll() method or creating another one which returns a dictionary.
In this case GetAllById() and then updating returnProductName which I would rename to GetProductName():
// Or whatever your type is
public Dictionary<int, List<Product>> GetAllById()
{
// your code..
return data
.GroupBy(x => x.Id)
.ToDictionary(x => x.Key, x => x.ToList());
}
public string GetProductName(int productId)
{
var products = _unitOfWork.Product.GetAllById();
return products[productId].FirstOrDefault(q => q.ProductName);
}
I'm trying to create a generic repository. For some reason, having a parameter in the Match() clause does not seems to work. Anyone know what the problem might be? My code looks like this (I'm a C#/.net Developer):
private IEnumerable<T> GetNodCollectionByLabel<T>(string nodLabel)
{
IEnumerable<T> entityList = new List<T>();
entityList = _graphClient.Cypher
.Match("(entity:{nodLabel})")
.WithParam("nodLabel", nodLabel)
.Return(entity => entity.As<T>())
.Results;
return entityList.ToArray();
}
The exception is:
SyntaxException: Invalid input '{': expected whitespace or a label name (line 1, column 15 (offset: 14)) "MATCH (entity:{nodLabel})" ^
.withParam() is for the Cypher parameters as they meant, not for the labels.
http://neo4j.com/docs/stable/cypher-parameters.html
You cannot use parameters for labels.
One approach for you is to use String.Format();
private IList<string> GetAllLabels()
{
return _graphClient.Cypher
.Match("(n)")
.Return<List<string>>("DISTINCT labels(n)")
.Results;
}
private bool isValidNode(string name)
{
return GetAllLabels().Contains(name.Trim());
}
private IEnumerable<T> GetNodCollectionByLabel<T>(string nodLabel)
{
IEnumerable<T> entityList = new List<T>();
if (!isValidNodeLabel(nodLabel))
{
return Enumerable.Empty<T>();
}
var statement = String.Format("(entity:{0})", nodLabel);
entityList = _graphClient.Cypher
.Match(statement)
.Return(entity => entity.As<T>())
.Results;
return entityList.ToArray();
}
Try this:
private IEnumerable<T> GetNodCollectionByLabel<T>(string nodLabel)
{
IEnumerable<T> entityList = new List<T>();
entityList = _graphClient.Cypher
.Match("(entity:nodLabel)")
.WithParam("nodLabel", nodLabel)
.Return(entity => entity.As<T>())
.Results;
return entityList.ToArray();
}
If you really have a param named "nodLabel" it will return some data.
The problem here was that you were looking for a node like this:
MATCH (entity:{nodLabel}) ...
but you have to specify the node label if you are using :, so you got a parsing error, because you did not specified a label for the node you were matching.
This code has been simplified for this example.
The query is actually returned from a service, which is why I would prefer to write the method this way.
[HttpGet]
public PageResult<ExceptionLog> Logging(ODataQueryOptions<ExceptionLog> options)
{
var query = from o in _exceptionLoggingService.entities.ExceptionDatas
select new ExceptionLog {
ExceptionDataId = o.ExceptionDataId,
SiteId = o.SiteId,
ExceptionDateTime = o.ExceptionDateTime,
StatusCode = o.StatusCode,
Url = o.Url,
ExceptionType = o.ExceptionType,
ExceptionMessage = o.ExceptionMessage,
Exception = o.Exception,
RequestData = o.RequestData
};
var results = options.ApplyTo(query) as IEnumerable<ExceptionLog>;
var count = results.LongCount();
return new PageResult<ExceptionLog>(results, Request.GetNextPageLink(), count);
}
The above code errors on "results.LongCount()" with the following Exception:
SqlException: The text, ntext, and image data types cannot be compared or sorted, except when using IS NULL or LIKE operator.
It appears that I'm getting an exception with when trying to page, like this "$top=2". Everything works fine if my querystring is like this "$filter=ExceptionDataId gt 100".
Since ExceptionData (the Entity) matches ExceptionLog (business model) I can do something like this as a workaround:
[HttpGet]
public PageResult<ExceptionLog> Logging(ODataQueryOptions<ExceptionData> options)
{
var query = from o in _exceptionLoggingService.entities.ExceptionDatas
orderby o.ExceptionDateTime descending
select o;
var results = from o in options.ApplyTo(query) as IEnumerable<ExceptionData>
select new ExceptionLog {
ExceptionDataId = o.ExceptionDataId,
SiteId = o.SiteId,
ExceptionDateTime = o.ExceptionDateTime,
StatusCode = o.StatusCode,
Url = o.Url,
ExceptionType = o.ExceptionType,
ExceptionMessage = o.ExceptionMessage,
Exception = o.Exception,
RequestData = o.RequestData
};
return new PageResult<ExceptionLog>(results, Request.GetNextPageLink(), results.LongCount());
}
But this doesn't completely work for me because it's a little hackish and I can't use the service's method which already gives me an IQueryable.
Another thing to note, is if the Logging method is converted to IQueryable, everything works correctly. But I need to return the Count with the query so I have to return a PageResult.
This is the workaround I'm using. I only apply the filter from the ODataQueryOptions and I manually apply the Top and Skip.
First I created some extension methods:
using System;
using System.Collections.Generic;
using System.Linq;
namespace System.Web.Http.OData.Query
{
public static class ODataQuerySettingsExtensions
{
public static IEnumerable<T> ApplyFilter<T>(this IQueryable<T> query, ODataQueryOptions<T> options)
{
if (options.Filter == null)
{
return query;
}
return options.Filter.ApplyTo(query, new ODataQuerySettings()) as IEnumerable<T>;
}
public static IEnumerable<T> ApplyTopAndTake<T>(this IEnumerable<T> query, ODataQueryOptions<T> options)
{
IEnumerable<T> value = query;
if (options.Top != null)
{
value = value.Take(options.Top.Value);
}
if (options.Skip != null)
{
value = value.Skip(options.Skip.Value);
}
return value;
}
}
}
Now my method looks like this:
[HttpGet]
public PageResult<ExceptionLog> Logging(ODataQueryOptions<ExceptionLog> options)
{
// GetLogs returns an IQueryable<ExceptionLog> as seen in Question above.
var query = _exceptionLoggingService.GetLogs()
.ApplyFilter(options);
var count = query.Count();
var results = query.ApplyTopAndTake(options);
return new PageResult<ExceptionLog>(results, Request.GetNextPageLink(), count);
}
Below is the code from the DotNetNuke Sample module that gets a collection of items from the database that belong to a particular module. What I want is add a second parameter for it filter by. I'm guessing this has something to do with modifying the scope item.cs class but am not sure how exactly.
public IEnumerable<Item> GetItems(int moduleId)
{
IEnumerable<Item> t;
using (IDataContext ctx = DataContext.Instance())
{
var rep = ctx.GetRepository<Item>();
t = rep.Get(moduleId);
}
return t;
}
Any ideas?
Another way to do it in DAL2 is using the .Find() method. This is good if you want to query on an indexed field in your table and you don't care about caching scope:
public IEnumerable<Item> GetItemByName(int moduleId, string itemname)
{
IEnumerable<Item> t;
using (IDataContext ctx = DataContext.Instance())
{
var rep = ctx.GetRepository<Item>();
t = rep.Find("WHERE ModuleId = #0 AND ItemName LIKE #1", moduleId, itemname);
}
return t;
}
Here's some sample code from my SignalRChat module that uses DAL2 (http://signalrchat.codeplex.com/SourceControl/changeset/view/71473#1272188)
public IEnumerable<Message> GetRecentMessages(int moduleId, int hoursBackInTime, int maxRecords)
{
var messages = (from a in this.GetMessages(moduleId) where a.MessageDate.Subtract(DateTime.UtcNow).TotalHours <= hoursBackInTime select a).Take(maxRecords).Reverse();
return messages.Any() ? messages : null;
}
That is one approach, you can also use a SQL statement within the controller as well (http://signalrchat.codeplex.com/SourceControl/changeset/view/71473#1272186)
public ConnectionRecord GetConnectionRecordByConnectionId(string connectionId)
{
ConnectionRecord t;
using (IDataContext ctx = DataContext.Instance())
{
var connections = ctx.ExecuteQuery<ConnectionRecord>(CommandType.Text,
string.Format(
"select top 1 * from {0}{1}SignalRChat_ConnectionRecords where ConnectionId = '{2}'",
_databaseOwner,
_objectQualifier,
connectionId)).ToList();
if (connections.Any())
{
t = connections[0];
}
else
return null;
}
return t;
}
What is the most efficient way to retrieve distinct column values. I have a table with fields FormsID and ProductLineDescription along with other columns. ProductLine table has a one to many relationship, the sample data in the table would be like:
FormID ProdculineDesc
1 abc
2 abc
1 xyz
2 def
3 abc
3 xyz
I want the dropdown to have just the distinct values of ProductLineDesc. Here is the code,
private void LoadProductLines(Models.SearchModel Model, xyzEntities Context)
{
Model.ProductLine = Context.PRODUCTLINEs
.OrderBy(T => T.FormsGuid).ToSelectList().Distinct();
}
This still gives me every ProductLineDesc, how do I retrieve just the Distinct values.
Here's a version with a custom quality comparer (with example):
public class UniqueProductLineDesc : IEqualityComparer<Product>
{
public Boolean Equals(Product a, Product b)
{
if (Object.ReferenceEquals(a, b))
return true;
if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(b, null))
return false;
return String.Compare(a.ProductLineDesc, b.ProductLineDesc, true) == 0;
}
public Int32 GetHashCode(Product product)
{
if (Object.ReferenceEquals(product, null))
return 0;
return product.ProductLineDesc == null ? 0 : product.ProductLineDesc.GetHashCode();
}
}
And to implement:
private void LoadProductLines(Models.SearchModel Model, xyzEntities Context)
{
Model.ProductLine = Context.PRODUCTLINEs
.OrderBy(T => T.FormsGuid)
.ToSelectList()
.Distinct(new UniqueProductLineDesc());
}
How about something like:
var distinctInfo = (from c in Context.PRODUCTLINEs
group by c.ProductLineDesc into result
select new Model.ProductLine {
ProductLineDesc = result.Key,
FormID = result.Min(d=>d.FormID)
});