Linq select from list - asp.net-mvc

I cant seem to figure this out, I'm not that new to the MVC model but perhaps my brain is just tired.
View Model
Public Class CategoriesViewModel
Public Property Categories As List(Of cihCategoryOrgDef)
Public Property Category As cihCategoryOrgDef
Public Property SelectedItem As String
Public Sub New(organizationId As Guid, codId As Guid)
Categories = lists.organizationClubCategories
Category = From x In Categories
Where x.codId = codId
Select x
End Sub
End Class
CategoriesController
Function Edit(codId As Guid) As ActionResult
Dim model As CategoriesViewModel = New CategoriesViewModel(ZenCommon.CurrentOrgId, codId)
Return View(model)
End Function
When I run this, I get a "invalid cast exception" on the Category = From x.... line
Unable to cast object of type 'WhereSelectListIterator`2[Library.cihCategoryOrgDef,Library.cihCategoryOrgDef]' to type 'Library.cihCategoryOrgDef'.
I'm trying to get a single Category to use in my View. So I can have an edit page for that specific category. Am I going about this all wrong?

Since Category is a single category, you need to make sure your LINQ returns a single result. If you're sure that the query will only return one value, use SingleOrDefault:
Category = (From x In Categories
Where x.codId = codId
Select x).SingleOrDefault()
If your query could return more than one result and you only want to take the first result, use FirstOrDefault:
Category = (From x In Categories
Where x.codId = codId
Select x).FirstOrDefault()

Your select actually returns a collection, that's why you get invalid cast exception. To get only one item you need something like this (sorry, I'm not very much familiar with vb, so here is c# code):
Category = (From x In Categories
Where x.codId = codId
Select x).DefaultIfEmpty(null).FirstOrDefault();

Related

Filtering list using linq and mvc

Below is the code in question. I receive Object reference not set to an instance of an object. on the where clause inside the Linq query. However, this only happens after it goes through and builds my viewpage.
Meaning: If I step through using debugger, I can watch it pull the correct order I am filtering for, go to the correct ViewPage, fill in the model/table with the correct filtered item, and THEN it comes back to my Controller and shows me the error.
public ActionResult OrderIndex(string searchBy, string search)
{
var orders = repositoryOrder.GetOpenOrderList();
if (Request.QueryString["FilterOrderNumber"] != null)
{
var ordersFiltered = from n in orders
where n.OrderNumber.ToUpper().Contains(Request.QueryString["FilterOrderNumber"].ToUpper().ToString())
select n;
return View(ordersFiltered);
}
return View(orders);
}
its always better to manipulate your strings and other things outside the linq query ,
please refer : http://msdn.microsoft.com/en-us/library/bb738550.aspx
from the readability point of view also its not good ,
public ActionResult OrderIndex(string searchBy, string search)
{
var orders = repositoryOrder.GetOpenOrderList();
var orderNumber = Request.QueryString["FilterOrderNumber"];
if (!string.IsNullOrEmpty(orderNumber))
{
orderNumber = orderNumber.ToUpper();
var ordersFiltered = from n in orders
where n.OrderNumber.ToUpper().Contains(orderNumber)
select n;
return View(ordersFiltered);
}
return View(orders);
}
Your query is not being executed in your Action method because you don't have a ToList (or equivalent) added to your query. When your code returns, your query will be enumerated somewhere in your view and that's the point where the error occurs.
Try adding ToList to your query like this to force query execution in your action method:
var ordersFiltered = (from n in orders
where n.OrderNumber.ToUpper().Contains(Request.QueryString["FilterOrderNumber"].ToUpper().ToString())
select n).ToList();
What's going wrong is that a part of your where clause is null. This could be your query string parameter. Try moving the Request.QueryString part out of your query and into a temporary variable. If that's not the case make sure that your orders have an OrderNumber.
You both were right. Just separately.
This fixed my problem
var ordersFiltered = (from n in orders
where !string.IsNullOrEmpty(n.OrderNumber) && n.OrderNumber.ToUpper().Contains(Request.QueryString["FilterOrderNumber"].ToUpper().ToString())
select n);

return LINQ Query result in IList and read it another class [duplicate]

This question already has answers here:
Working with C# Anonymous Types
(8 answers)
Closed 8 years ago.
I am using ASP.NET mvc 5. I have one class that holds all the LINQ which can access to another class. now i convert the LINQ query to list variable Query and returning as IList... the i create object of this class--> call the method and get result.
now i can see in debugging object names but i can't see in foreach loop. my list hold mix data types, plus result is merging from different tables...
public IList GetAllFeeZonesForFeeSchemeByID(int FeeSchemeID)
{
using (var db = new QualificationContext())
{
var Query = from a in db.FeeScheme
join b in db.FeeZoneSchema.Where(c => c.FeeSchemeID == 1) on a.FeeSchemeID equals b.FeeSchemeID
join c in db.FeeZone on b.FeeZoneID equals c.FeeZoneID
select new
{
FeeScheme = a.FeeSchemeID,
FeeZone = b.FeeZoneID,
FeeZone_Description = c.FeeZoneDescription
};
return Query.ToList();
}
}
in controller class...
foreach(var item in obj1.GetAllFeeZonesForFeeSchemeByID(1))
{
item.???? (can't access the object here....
}
many thanks
You should return Generic IList of concrete (not anonymous class):
public IList<FeeSchemeModel> GetAllFeeZonesForFeeSchemeByID(int FeeSchemeID)
{
using (var db = new QualificationContext())
{
var Query = from a in db.FeeScheme
join b in db.FeeZoneSchema.Where(c => c.FeeSchemeID == 1) on a.FeeSchemeID equals b.FeeSchemeID
join c in db.FeeZone on b.FeeZoneID equals c.FeeZoneID
select new FeeSchemeModel
{
FeeScheme = a.FeeSchemeID,
FeeZone = b.FeeZoneID,
FeeZone_Description = c.FeeZoneDescription
};
return Query.ToList();
}
}
public class FeeSchemeModel
{
public int FeeScheme{get;set;}
public int FeeZone{get;set;}
public string FeeZone_Description{get;set;}
};
But I recommend to use IEnumerable<T> instead of IList<T> and use ToArray() method instead of ToList() method if you don't use special features of List<T> (such as method Add())
IList is non-generic interface, it contains only non-generic IEnumerable definition, which enumerates objects. So type of item will be object. That's why you can see only members of System.Object class.
You should either cast item to appropriate type or use generic collection parametrized with appropriate type. But you can't use neither of these approaches while you are using anonymous objects, because you don't know anonymous type name. So, you need to create some class which you will be able to cast to:
foreach(Foo item in obj1.GetAllFeeZonesForFeeSchemeByID(1))
Or use as parameter of method return type:
public IList<Foo> GetAllFeeZonesForFeeSchemeByID(int FeeSchemeID)
One more option is usage of dynamic type, which will resolve operations on object at runtime. You still will not be able to use IntelliSense but your code will work:
foreach(dynamic item in obj1.GetAllFeeZonesForFeeSchemeByID(1))
{
// use item.FeeScheme
}

LINQ to Entities query with join inside method for use in MVC app

In my Person table is a RequestedLocation column which stores location IDs. The IDs match the LocationId column in the Locations table, the Locations table also has the text location names, in the LocatioName column.
In my view, I need to display the string LocationName in the view which has the Person model passed to it. The view will be displaying a List of people in a telerik grid. CUrrently it works great, except the RequestedLocation column is all integers.
I am populating all my grids with methods containing LINQ queries. Here is the method that currently works:
public List<Person> GetPeople()
{
var query = from p in _DB.Person.ToList()
select p;
return query.ToList();
}
Here is the regular SQL query that works, and I need to convert into LINQ:
SELECT ApplicantID
,FirstName
,LastName
,MiddleName
,DateofBirth
,Gender
,RequestedVolunteerRole
,RequestedVolunteerLocation
,l.LocationName
FROM Form.Person p
JOIN dbo.Location l ON p.RequestedVolunteerLocation = l.LocationID
Order BY ApplicantID
Here is my attempt to convert to LINQ:
public List<NewApplicantViewModel> GetPeople()
{
var query = from pl in _DB.Person.ToList()
join l in _Elig_DB.Locations.ToList() on pl.RequestedVolunteerLocation equals l.LocationID
select new
{
pl.RequestedVolunteerLocation = l.LocationName
};
return query.ToList();
The number of errors I get from this are numerous, but most are along the lines of:
Cannot convert from type Annonymous to Type List<NewAPplicantModel>
and
Invalid annonymous type declarator.
Please help, and thank you for reading my post.
Oh, and I have only been programming for a couple months, so if I am going about this all wrong, please let me know. Only thing I have to stick with is the table structure because it is an existing app that I am updating, and changing the location or person tables would have large consequences.
public List<NewApplicantViewModel> GetPeople()
{
var query = from pl in _DB.Person
join l in _Elig_DB.Locations on pl.RequestedVolunteerLocation
equals l.LocationID
select new NewApplicantViewModel
{
LocationName = l.LocationName,
otherPropery = p.Property
};
return query.ToList();
}
Beware of calling _DB.Person.ToList() it will load all persons from DB because ToList() immediately executes the query and the join would be performed in memory (not in DB).
The reason you are getting an error is you are projecting an anonymous type
select new
{
pl.RequestedVolunteerLocation = l.LocationName
};
Instead, you need to project a NewApplicantViewModel
select new NewApplicantViewModel
{
RequestedVolunteerLocation = l.LocationName
};

Extracting [] elements from form collection - mvc - should use icollection but have mix of types

have looked at Phil Haacks project on books at
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
which has been useful, but I have a mix of data types.
I use a modelview so that i can have a mix of objects, in this case: Order (ie order.id, order.date etc), Customer, SoilSamplingOrder and a list of SoilSamplingSubJobs which is like this [0].id, [0].field, [1].id, [1].field etc
Perhaps I should be using ICollection instead of List? I had problems getting UpdateModel to work so I used an extract from collection method. the first 4 method calls : orderRepository.FindOrder(id); etc give the model the original to be edited. but after this point i'm a little lost in how to update the subjobs. I hope i have delineated enough to make sense of the problem.
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Order order = orderRepository.FindOrder(id);
Customer cust = orderRepository.FindCustomer(order.customer_id);
IList<SoilSamplingSubJob> sssj = orderRepository.FindSubOrders(id);
SoilSamplingOrder sso = orderRepository.FindSoilSampleOrder(id);
try
{
UpdateModel(order, collection.ToValueProvider());
UpdateModel(cust, collection.ToValueProvider());
UpdateModel(sso, collection.ToValueProvider());
IList<SoilSamplingSubJob> sssjs = orderRepository.extractSSSJ(collection);
foreach (var sj in sssjs)
UpdateModel(sso, collection.ToValueProvider());
orderRepository.Save();
return RedirectToAction("Details", new { id=order.order_id});
}
catch
{
return View();
}
}
I think you should work on developing a view model that reflects the data that you need to get back and create display/edit templates for that model that renders the view model using Phil Haack's methods for your lists of objects -- in this case, arrays of submodel classes. Let the model binding framework build the returned model (as a parameter) to your action, then reconstitute your domain models from the view model data. Brad Wilson has an excellent series of articles on templating that should be helpful.
I use IModelBinder on my complex classes. You don't need IModelBinder, but it will make your controller post codeblock look much cleaner. I'm using VB at the moment, but my class looks something like this for example:
Public Class CombinedRulesAndXmlRules : Implements IModelBinder
Public Rules As New Rules()
Public XmlRules As New XmlRules()
Public RequiredTemplates As New List(Of RequiredTemplates)
Public SearchCriteria As New List(Of SearchCriteriaList)
Public OptionalTemplates As New List(Of OptionalTemplates)
Public Questions As New List(Of Questions)
Public QATemplates As New List(Of QATemplates)
**Public Answers As New List(Of Answers)**
Now I don't use editor templates in my views, so to have your lists appear in the formcollection you have to add something like this in your view:
#For x As Integer = 0 To Model.Answers.Count - 1
Dim incr As Integer = x
#Html.HiddenFor(Function(model) model.Answers(incr).Answer)
#Html.HiddenFor(Function(model) model.Answers(incr).AnswerId)
#Html.HiddenFor(Function(model) model.Answers(incr).AnswerTemplateTag)
#Html.HiddenFor(Function(model) model.Answers(incr).Tag)
Next
When the view is submitted/posted, the model binder takes over before hitting the first line of code in your mvc post controller method. I then iterate through the actual formcollection and strip out the [#] using regex, because your formcollection will show your list items like this: Answers[0].Answer, Answers[0]AnswerId ,etc.:
For x As Integer = 1 To request.Form.Count - 1
keyname = request.Form.Keys(x)
Debug.Write(keyname)
val = request.Form(x).ToString()
'If keyname contains [#] strip it. it's a list item.
Dim pattern As String = "\[(\d+)\]"
Dim iterpattern As String = "\d+"
Dim rgx As New Regex(pattern)
Dim rgxiter As New Regex(iterpattern)
If Regex.IsMatch(keyname, pattern) Then
Dim match As Match = rgxiter.Match(keyname)
ListIteration = CInt(match.Value)
Dim result As String = rgx.Replace(keyname, "")
keyname = result
End If
The Select Case codeblock is next. So you already know you have a strong typed class in your model, so your select can look like this:
Select Case keyname
Case "Answers.Answer"
'add code here to add to your return list. What you
'get in the post controller is a fully populated class.

How does Select statement works in a Dynamic Linq Query?

1) I've a Product table with 4 columns: ProductID, Name, Category, and Price. Here's the regular linq to query this table.
public ActionResult Index()
{
private ProductDataContext db = new ProductDataContext();
var products = from p in db.Products
where p.Category == "Soccer"
select new ProductInfo { Name = p.Name, Price = p.Price}
return View(products);
}
Where ProductInfo is just a class that contains 2 properties (Name and Price). The Index page Inherits ViewPage - IEnumerable - ProductInfo. Everything works fine.
2) To dynamicaly execute the above query, I do this:
Public ActionResult Index()
{
var products =
db.Products
.Where("Category = \"Soccer\"")
.Select(/* WHAT SOULD I WRITE HERE TO SELECT NAME & PRICE?*/)
return View(products);
}
I'm using both 'System.Lind.Dynamic' namespace and the DynamicLibrary.cs (downloaded from ScottGu blog).
Here are my questions:
What expression do I use to select only Name and Price?
(Most importantly) How do I retrieve the data in my view? (i.e. What type the ViewPage inherits? ProductInfo?)
===================
EDIT
When I write .Select("new(Name, Price)"), I'm able to pass an object to the ViewData's Model property. Unfortunately, in order to use the Viewdata object, I'm asked to cast the Viewdata to a type. But, I do not know how to determine the type to do the casting.
====================
EDIT
Instead of the ViewData's Model property, I'm using simply the ViewData["products"]. To retrieve the content, I just place a IEnumerable cast before the ViewData, like this:
<% foreach(var item in (IEnumerable)ViewData["products"]){%>
<p><% = Html.Encode(item)%><p>
<%}%>
There are 2 situations:
1) If I select only one column (for instance, Name), everything work fine.
2) If I select more than 1 more columns (Name, Price), I get something like this
{Name=Soccer, Price=19.50}
{Name=Shin Pads, Price=11.59}
Why I just don't get something like
Soccer, 19.50
Shin Pads, 11.59
=================================
EDIT April 02 - 05h47 AM
I've define the GetPropertyValue Method (as your response suggets) as static in a static Class that I called 'HelperClass'. Now, this is the way I try to access the value of Name from my object.
<% = Html.Encode(HelperClass.GetPropertyValue(ViewData["product"], "Name")) %>
I get the following Exception:"Object reference not set to an instance of an object". And, the following line from the inside GetPropertyValue() his highlight.
Line 22: return propInfo.GetValue(obj, null);
Do I need to use new keyword? (where?)
Thanks for helping
Private Sub filter()
Dim coll = db.Products.Where(Function(x) x.Category.Equals("Soccer")) _
.Select(Function(x) GetObject(x.Name, x.Price))
End Sub
Private Function GetObject(ByVal name As String, ByVal price As String) As Object
Return new ProductInfo(name, price)
End Function
1) To generate a new projection type at runtime you can:
.Select("new(Name, Price)")
2) To read values from the object, you need to use reflection:
string name = GetPropertyValue(someObject, "Name");
...
public static object GetPropertyValue(object obj, string propName)
{
System.Reflection.PropertyInfo propInfo = obj.GetType().GetProperty(propName);
return propInfo.GetValue(obj, null);
}

Resources