I have a service function to query a SQL db like so:
public IQueryable<MyModel> getAll()
{
IQueryable<MyModel> models = (from f in db.MyModel select f);
return models;
}
When I implement it in my controller it works when I chain the Take():
var models = myModelEntities.getAll().Take(5);
return View(models); // returns 5 rows to the view
But like this it doesn't:
var models = myModelEntities.getAll();
models.Take(5);
return View(models); // returns thousands of rows to the view
Why is Take() ignored if it's not chained? I have Lazy Loading enabled on my model...
It does work, but you're not assigning the result of Take() to anything. Take() doesn't mutate the models variable, it returns the top 5 items in the enumerable.
The following would work:
var models = myModelEntities.getAll();
models = models.Take(5);
return View(models); // returns five rows to the view
It's because Take doesn't mutate the current IEnumerable; it returns a new one. This will work:
models = models.Take(5);
You aren't assigning the result of the second Take() call to any variable.
Related
I need to unit test the controllers method results with the following code
StoreController test = new MvcMusicStore.Controllers.StoreController();
ActionResult result = test.Index();
ViewResult p = (ViewResult)result;
result and p shows 10 items in the model when debugging but it seems that I can not do Count or foreach to check how many records are returned as both objects are dictionary.
How can this be done?
Thanks
ViewResult.Model is of type object you have to cast it to the collection type first to get a count.
For example, if you're passing the list of integers as a model to your view, you can get the count this way:
var model = p.Model;
var list = model as List<int>();
var count = list.Count;
Assuming you have a model of IEnumerable<DataType> and you want to get this model in a unit test, you have to cast the result.ViewData.Model to your type and then do your Assertions:
var test = new StoreController();
var result = test.Index() as ViewResult;
var data = (IEnumerable<ModelType>) result.ViewData.Model;
Assert.AreEqual(10, data.Count());
Reference: https://www.asp.net/mvc/overview/older-versions-1/unit-testing/creating-unit-tests-for-asp-net-mvc-applications-cs (Listing 4)
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);
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
}
Using ASP MVC5 and EF6.
I had a curious case the other day when I was looking to have different behaviour when a foreach-loop got to the last element.
The loop wouldn't enter if-condition comparing the object with the result from .Last()-method on the collection.
The collection I was iterating over was something like:
public class CollectionClass{
IEnumerable<TestClass1> CollectionA
IEnumerable<TestClass2> CollectionB
}
My code was something like:
DbContext db = new DbContext(); //just for illustration, not actual code
CollectionClass cc = new CollectionClass {
CollectionA = db.TestClasses1,
CollectionB = db.TestClasses2
};
//(TestClasses1 and TestClasses2 are DbSet<T> properties of my DbContext.
foreach(TestClass1 tc1 in cc.CollectionA)
{
if (tc1 == cc.CollectionA.Last()){ //<---NEVER enters in here!!
//doStuff
}
else{
//doOtherStuff
}
}
With the code above, the loop never entered into the if-condition, even for the last element, which one would expect.
But when changed my CollectionClass to:
public class CollectionClass{
List<TestClass1> CollectionA
List<TestClass2> CollectionB
}
and instantiated the CollectionClass-object like this:
CollectionClass cc = new CollectionClass {
CollectionA = db.TestClasses1.ToList(),
CollectionB = db.TestClasses2.ToList()
}; //Added .ToList()
the loop entered into the first if-condition at the last iteration as I expected.
Why this difference? Why did the equals-operator (==) evaluate to TRUE when the object had been stored in a List and FALSE when the object was stored in an IEnumerable?
I know that IEnumerable is an interface -- is that what makes the difference?
I even did an explicit test in the sorts of:
var obj1 = cc.CollectionA.Last();
var obj2 cc.CollectionA.Last();
bool result = obj1 == obj2; //result = FALSE
and the result was FALSE.
I think it's because in first example you get two objects from database. First from iteration and second from call to Last().
In second example all objects are created at the time you assign collections to CollectionA and CollectionB (you call ToList()).
It is because you are not allowed to use Last and LastOrDefault on DbSet objects. Instead you should use OrderByDescending(t=>t.ID).First()
I am having an issue with ZF2 trying to use the table gateways and getting result sets:
I am trying to query two result sets (from two different tables/two different gateways) and send them to the view to be iterated through and placed on the screen.
(Simplified example):
function viewAction() {
$table1 = $this->getServiceLocator()->get('Model\Table\Table1');
$table2 = $this->getServiceLocator()->get('Model\Table\Table2');
return new ViewModel([
'table1' => $table1->fetchAll(),
'table2' => $table2->fetchAll()
]);
}
With the Model\Table\Table1 and Model\Table\Table2 having a fetch all:
public function fetchAll() {
return $this->tableGateway->select();
}
Then in my view:
...
<?php
foreach($table1 as $row) {
echo "<tr><td>{$row['col1']}</td><td>{$row['col2']}</td></tr>";
}
?>
...
<?php
foreach($table2 as $row) {
echo "<tr><td>{$row['col1']}</td><td>{$row['col2']}</td></tr>";
}
?>
...
The problem is, $table1 will have no data when looping. However, if I instead do something like this (in the controller, instead of passing the result set to the view, passing $results1 and $results2 to the view):
$fetchAll = $table1->fetchAll();
$results1 = [];
foreach($fetchAll as $row) {
$results1[] = $row;
}
$fetchAll = $table2->fetchAll();
$results2 = [];
foreach($fetchAll as $row) {
$results2[] = $row;
}
Then all works fine. I don't want to have to loop through the same set of data twice. So why does ZF2 prevent me from using two different ResultSets before all the data in a ResultSet has been accessed?
Since I was querying to different record sets one after the other, the database was still waiting for action to take place on the first query. I could only have one action record set at a time.
There first solution was what I presented in the question, get a record set and loop through all the rows that are available before trying to query again. This frees the record set as we have all the data.
The second solution (the one I have adapted), is to enable Multiple Active Record Sets (MARS) for the database connection.
I was able to do this by adding MARS_Connection=yes to the dsn connection string for MSSQL.