how can i pass parameter to linq query - linq-to-sql

i want to pass parameter to linq query...
public IEnumerable GetPhotos()
{
PhotoDBDataContext db = new PhotoDBDataContext();
var query = from p in db.Photos
orderby p.PhotoId descending
select new { p.Album, p.AlbumId, p.Description, p.Photographer,
p.PhotographerId, p.PhotoId, p.Tags, p.Thumbnail,
p.Url };
return query;
}
in above example "orderby p.PhotoId descending" is used, i want to use parameter in place of p.PhotoId
is it possible...

public IQueryable<Photo> GetPhotos(PhotoDBDataContext db, string orderBy)
{
var query = from p in db.Photos select p;
switch (orderBy) {
case "PhotoId":
return query.OrderBy(p => p.PhotoId);
case "AlbumId":
return query.OrderBy(p => p.AlbumId);
default:
// Error handling.
}
}
Note that you should not return objects with an anonymous type.

With Dynamic Linq, you can write .OrderBy("ColumnName").

You could do it like this if you had two order-by criteria
public static IQueryable<Photo> GetPhotos(string OrderBy)
{
return db.Photos.OrderBy(p => ( (OrderBy == "PhotoId") ? (p.PhotoId) : (p.AlbumId) ));
}

You could use an extension. This helped me:
public static class OrderExt
{
public static IOrderedQueryable<T> Order<T>(this IQueryable<T> source, string propertyName, SortDirection descending, bool anotherLevel = false)
{
var param = Expression.Parameter(typeof(T), string.Empty);
var property = Expression.PropertyOrField(param, propertyName);
var sort = Expression.Lambda(property, param);
var call = Expression.Call(
typeof(Queryable),
(!anotherLevel ? "OrderBy" : "ThenBy") +
(descending == SortDirection.Descending ? "Descending" : string.Empty),
new[] { typeof(T), property.Type },
source.Expression,
Expression.Quote(sort));
return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(call);
}
}
For full explanation you could go to: http://how-to-code-net.blogspot.ro/2014/04/how-to-call-for-dynamic-orderby-method.html

Related

.net mvc linq jsonresult

Please look at the Code: how do i split the m.Division ("Divone,a") as object like ["DivOne","a"]
public JsonResult GetData()
{
var series = (from m in db.Fields.AsEnumerable()
where m.tstatus == true
select new
{
name = m.Name,
data = new [] { m.Division }
}).ToArray();
return Json(series, JsonRequestBehavior.AllowGet);
}
The Result
[{"name":"fff","data":["DivOne,a"]},{"name":"a","data":["a"]}]
I want the result Like
[{"name":"fff","data":["DivOne","a"]},{"name":"a","data":["a"]}]
image here:
enter image description here
Change m.Division to m.Division.Split(',')
public JsonResult GetData()
{
var series = (from m in db.Fields.AsEnumerable()
where m.tstatus == true
select new
{
name = m.Name,
data = new[] { m.Division.Split(',') }
}).ToArray();
return Json(series, JsonRequestBehavior.AllowGet);
}
#Sajid
Thank you.... that is a result i wanted
public JsonResult GetData()
{
var series = (from m in db.Fields.AsEnumerable()
where m.tstatus == true
select new
{
name = m.Name,
data = m.Division.Split(',')
}).ToArray();
return Json(series, JsonRequestBehavior.AllowGet);
}
[{"name":"fff","data":["DivOne","a"]},{"name":"a","data":["a"]}]
just use Split(',') for division shown in below.
public JsonResult GetData()
{
var series = (from m in db.Fields.AsEnumerable()
where m.tstatus == true
select new
{
name = m.Name,
data = m.Division.Split(',')
}).ToArray();
return Json(series, JsonRequestBehavior.AllowGet);
}

Passing Laravel Collection to Vue

I am trying to return customers from model Customer where id < 10. I do have some in the database.
$customers = Customer::where('id', '<', 10)->get();
return json_encode(['customers' => $customers]);
The above code will error out "Trailing data"
and when I try to dd($customers), I get a list of the collection Customer
I have no idea why I keep getting that and why the model is returning raw collection and not an object of the list of customers???
It seems like a null date "updated_at" field is causing the error. don't know why!
Solved By:
on the Customer model I had to do:
//ask Laravel to not manage default date columns
public $timestamps = false;
also I to mutate those columns:
public function setDateRemindedAttribute($value)
{
if (strlen($value)) {
$this->attributes['date_reminded'] = Carbon::createFromFormat('d/m/Y', $value);
} else {
$this->attributes['date_reminded'] = null;
}
}
public function setUpdatedAtAttribute($value)
{
if (strlen($value)) {
$this->attributes['updated_at'] = Carbon::createFromFormat('d/m/Y', $value);
} else {
$this->attributes['updated_at'] = $_SERVER['REQUEST_TIME'];
}
}
Use response()->json() like this
$customers = Customer::where('id', '<', 10)->get();
return response()->json(['customers' => $customers]);

$.getJSON returns array,help needed with $.each

i have this code in my view which is in a loop which gives me two rows for console.log
var jqxhr = $.getJSON("<%= Url.Action("GetTrainingModulePoints" , "Home") %>", function (data) {
console.log(JSON.stringify(data));
});
<%: Html.GetQTip("training-module-id-" + module.TrainingModuleId , "With assesment" , "training-module-id-" + module.TrainingModuleId , Zinc.Web.Extensions.QTipPosition.Bottom, true, "Module Points") %>
in my controller:
public JsonResult GetTrainingModulePoints()
{
var currentUser = ZincService.GetUserForId(CurrentUser.UserId);
IEnumerable<DataModels.Training.UserTrainingPointsDataModel> modulePoints = ZincService.TrainingService.GetTrainingModulePoints(currentUser.UserId);
return Json(new { result = modulePoints}, JsonRequestBehavior.AllowGet);
}
each console.log gives:
LOG: {"result":[{"InteractionType":6,"Points":50,"Name":"China Incentive Program"},{"InteractionType":8,"Points":1,"Name":"China Incentive Program"},{"InteractionType":6,"Points":50,"Name":"India - Q2 Incentive "},{"InteractionType":8,"Points":100,"Name":"India - Q2 Incentive "},{"InteractionType":6,"Points":50,"Name":"China - Q2 Incentive"},{"InteractionType":8,"Points":3,"Name":"China - Q2 Incentive"}]}
LOG: {"result":[{"InteractionType":6,"Points":50,"Name":"China Incentive Program"},{"InteractionType":8,"Points":1,"Name":"China Incentive Program"},{"InteractionType":6,"Points":50,"Name":"India - Q2 Incentive "},{"InteractionType":8,"Points":100,"Name":"India - Q2 Incentive "},{"InteractionType":6,"Points":50,"Name":"China - Q2 Incentive"},{"InteractionType":8,"Points":3,"Name":"China - Q2 Incentive"}]}
how can i get the interactiontype,name and points seperate?
public static MvcHtmlString GetQTip(this HtmlHelper htmlHelper, string propertyName, string message, string propertyNameOverride = "", QTipPosition position = QTipPosition.Right, bool includeEvents = true, string title = "")
{
string qtipPosition = String.Empty;
switch (position)
{
case QTipPosition.Right:
qtipPosition = "my: 'left center', at: 'right center'";
break;
case QTipPosition.Left:
qtipPosition = "my: 'right center', at: 'left center'";
break;
case QTipPosition.Top:
qtipPosition = "my: 'top middle', at: 'bottom middle'";
break;
case QTipPosition.Bottom:
qtipPosition = "my: 'bottom middle', at: 'top middle'";
break;
}
if (!String.IsNullOrWhiteSpace(propertyNameOverride))
propertyName = propertyNameOverride;
if (String.IsNullOrWhiteSpace(title))
title = htmlHelper.Resource(Resources.Global.Title.Information);
StringBuilder sb = new StringBuilder();
sb.Append(String.Concat("$('#", propertyName, "').removeData('qtip').qtip({content: {text:"));
sb.Append(String.Concat("'", message, "', title: { text: '", title, "', button: false }}, position: { ", qtipPosition, " }"));
if (includeEvents)
sb.Append(", show: { event: 'focus mouseenter', solo: true, ready: false }, hide: 'blur'");
sb.Append(", style: { classes: 'ui-tooltip-shadow ui-tooltip-yellow' } });");
return new MvcHtmlString(sb.ToString());
}
}
public sealed class MvcHtmlString : HtmlString
{
}
Simply use the $.each() function:
var url = '<%= Url.Action("GetTrainingModulePoints" , "Home") %>';
var jqxhr = $.getJSON(url, function (data) {
$.each(data.result, function() {
var interactionType = this.InteractionType;
var name = this.Name;
var points = this.Points;
// do something with those variables ...
});
});
Here we are looping over the data.result collection in which each element represents an object having InteractionType, Points and Name properties according to the log you have shown. The $.each will obviously be executed for each element of the result collection.
UPDATE:
After the small discussion we had in the comments section it seems that you are doing something fundamentally wrong here. You are attempting to pass to a server side helper values that you have retrieved on the client using AJAX. That's impossible nr it makes any sense.
So you should be binding on the server. You shouldn't be doing any AJAX requests at all. You should simply call your server side helper and pass it the required parameters:
<%: Html.GetQTip(
"training-module-id-" + module.TrainingModuleId,
Model.Points,
"training-module-id-" + module.TrainingModuleId,
Zinc.Web.Extensions.QTipPosition.Bottom,
true,
"Module Points"
) %>
Now all you have to do is add this Points property to your view model:
public string Points { get; set; }
and inside the controller action that is rendering this view simply set this property. You would first query your data layer to retrieve an IEnumerable<UserTrainingPointsDataModel> and then perform some transformation on this array to convert it to a string that you want to be displayed:
MyViewModel model = ... get the view model from somewhere
var currentUser = ZincService.GetUserForId(CurrentUser.UserId);
var modulePoints = ZincService.TrainingService.GetTrainingModulePoints(currentUser.UserId);
model.Points = ... transform the original points collection to some string that you want to pass to the helper;
return View(model);
Remark: I don't know where you took this Html.GetQTip helper but looking at its source code I am horrified. This helper doesn't encode anything. Your site is vulnerable to XSS attacks. Never use any string concatenations to build up javascript and pass variables to functions.

Local sequence cannot be used in LINQ to SQL implementation

I'm getting an error, see below, when I try to generate a list of the class MappedItem. In short the code example below tries to find products by category, date range and SKU. The requirement I have is that the user should be able to enter a comma separated list of SKUs and the search is to find any product whos SKU starts with one of the SKUs entered by the user. When I run the code, I get.
Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.
The abbreviated sequence is this:
Convert the comma separated string of SKUs into a list of strings.
string sku = TextSKU.Text;
List<string> skuList = sku.Split(new char[] { ',' }).ToList();
Define elsewhere in the code the class that will accept the search results.
public class MappedItem
{
public string ItemDescription { get; set; }
public int ItemCount { get; set; }
public MappedItem()
{
}
public MappedItem(string itemDescription, int itemCount)
{
ItemDescription = itemDescription;
ItemCount = itemCount;
}
}
Here's the query that I generate my results from
List<MappedItem> widgetItems = (from c1 in db.CCRCodes
join pac in db.widgetAssignedCodes on c1.code_id equals pac.code_id
join ph in db.widgetHistories on pac.history_id equals ph.history_id
where ph.contact_dt.Value.Date >= startDate && ph.contact_dt.Value.Date <= endDate &&
(string.IsNullOrEmpty(baanCatFam) || ph.baan_cat_family_code == baanCatFam) &&
(string.IsNullOrEmpty(baanCat) || ph.baan_cat_code == baanCat) &&
(string.IsNullOrEmpty(baanSubCat) || (ph.baan_sub_cat_code == baanSubCat)) &&
(string.IsNullOrEmpty(sku) || skuList.All(sl => ph.product_mod.StartsWith(sl)))
group c1 by c1.code_desc into ct
select new MappedItem
{
ItemDescription = ct.Key.ToUpper(),
ItemCount = ct.Count()
}).OrderByDescending(m => m.ItemCount)
.ToList();
I believe that the culprit is the line of code that I've extracted and displayed below.
skuList.All(sl => ph.product_mod.StartsWith(sl))
This identifies all skus that start with an element from the skuList which is derived from a comma delimited lists of skus entered by the user. My question is, what causes this error, and given the code examples, what do I do to get around them.
First - logically you want Any, not All.
Second, this is a poor way to build up a query filter. All of those operations are sent into the database, while the information to determine which filters should be applied is already local. The explicit joins are also bad (association properties could be used instead).
IQueryable<WidgetHistory> query = db.widgetHistories
.Where(ph => ph.contact_dt.Value.Date >= startDate
&& ph.contact_dt.Value.Date <= endDate);
if (!string.IsNullOrEmpty(baanCatFam))
{
query = query.Where(ph => ph.baan_cat_family_code == baanCatFam);
}
if (!string.IsNullOrEmpty(baanCat))
{
query = query.Where(ph => ph.baan_cat_code == baanCat);
}
if (!string.IsNullOrEmpty(baanSubCat))
{
query = query.Where(ph => ph.baan_sub_cat_code == baanSubCat);
}
//TODO sku filtering here.
List<MappedItem> widgetItems =
from ph in query
let c1 = ph.widgetAssignedCode.CCRCode
group c1 by c1.code_desc into g
select new MappedItem
{
ItemDescription = g.Key.ToUpper(),
ItemCount = g.Count()
}).OrderByDescending(m => m.ItemCount)
.ToList();
Third: the answer to your question.
what causes this error
LinqToSql's query provider cannot translate your local collection into sql. There's only a limitted set of scenarios where it can translate... .Where(ph => idList.Contains(ph.Id)) is translated into an IN clause with 1 parameter per int in idList.
To get around this limitation, you need to convert the local collection into an expression. Start by tranforming each item in the collection into a filtering expression:
List<Expression<Func<WidgetHistory, bool>>> skuFilters =
skuList.Select<string, Expression<Func<WidgetHistory, bool>>>(skuItem =>
ph => ph.ProductMod.StartsWith(skuItem)
).ToList();
Next, a helper method:
public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(
this IEnumerable<Expression<Func<T, bool>>> filters)
{
Expression<Func<T, bool>> firstFilter = filters.FirstOrDefault();
if (firstFilter == null)
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
var body = firstFilter.Body;
var param = firstFilter.Parameters.ToArray();
foreach (var nextFilter in filters.Skip(1))
{
var nextBody = Expression.Invoke(nextFilter, param);
body = Expression.OrElse(body, nextBody);
}
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
return result;
}
And now putting it all together:
if (skuFilters.Any()) //this part goes into where it says "TODO"
{
Expression<Func<WidgetHistory, bool>> theSkuFilter = skuFilters.OrTheseFiltersTogether()
query = query.Where(theSkuFilter);
}

Linq - pulling a value from a null query result

I have a linq query that needs to pull a date column out of a row. The expression currently looks like this
myObject.OrderByDescending(s=> s.MyDate).Where(s => s.CRAStatus.Description == "CheckedOut").FirstOrDefault().MyDate)
The problem is that if there are no rows that are "CheckedOut", the query will return a null and attempting to get "MyDate" will throw an exception. We have some verbose solutions, like:
.ForMember(dest => dest.CheckOutDate, opt => opt.MapFrom(src => {
var temp = src.CRAStatusChangeEvents.OrderByDescending(s=> s.MyDate).Where(s => s.CRAStatus.Description == "CheckedOut").FirstOrDefault();
return temp == null ? temp.MyDate : null;
}));
But it would be nice to find something a little more concise. Any Ideas?
Why not
myObject.OrderByDescending(s=> s.MyDate)
.Where(s => s.CRAStatus.Description == "CheckedOut")
.Select(s => s.MyDate as DateTime?)
.FirstOrDefault();
or
myObject.Where(s => s.CRAStatus.Description == "CheckedOut")
.Max(s => s.MyDate as DateTime?);
One option is to set the default if empty to an "empty" instance (think of string.Empty--its a known instance that represents an empty result):
var date = (myObject
.OrderByDescending(s=> s.MyDate)
.Where(s => s.CRAStatus.Description == "CheckedOut")
.DefaultIfEmpty(MyObject.Empty)
.FirstOrDefault()).MyDate;
Here's a snippet that shows how it works:
var strings = new string[]{"one", "two"};
var length =
(strings.Where(s=>s.Length > 5)
.DefaultIfEmpty(string.Empty)
.FirstOrDefault()).Length;
run that and length is 0. Remove the DefaultIfEmpty line and you get a NRE.
var checkedOut = myObject.Where(s => s.CRAStatus.Description == "CheckedOut");
if (checkedOut.Count() > 0) {
var result = checkedOut.Max(s=> s.MyDate).MyDate;
}
How about an extension method?
static class MyObjectEnumerableExtensions
{
public static TMember GetMemberOfFirstOrDefault<TMember>(this IEnumerable<MyObject> items, Func<MyObject, TMember> getMember)
{
MyObject first = items.FirstOrDefault();
if (first != null)
{
return getMember(first);
}
else
{
return default(TMember);
}
}
}
Sample usage:
List<MyObject> objects = new List<MyObject>();
objects.Add(new MyObject { MyDate = DateTime.MinValue });
var filteredObjects = from s in objects where s.MyDate > DateTime.MinValue select s;
DateTime date = filteredObjects.GetMemberOfFirstOrDefault(s => s.MyDate);
Console.WriteLine(date);