I have an ASP.NET MVC application. Let's suppose that I have this view:
#model IEnumerable<MyClass>
.....
On the server side, I have a linq query, to my database, using EF:
public ActionResult Index()
{
var query = from t in context.MyClass
select t;
//now comes the question
return View(query);
return View(query.AsEnumerable()); //is there any difference?
}
I think that the AsEnumerable() is not necessary because the query will automatically cast to it, so, can someone explain me when the AsEnumerable() is useful?
Thank you!
It is not necessary. The query you have declared results in a sequence, which implements the IEnumerable interface.
As you will see here, the Select extension method of types that implement the IEnumerable, returns a IEnumerable.
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
Your query
var query = from t in context.MyClass
select t;
will be compiled to
var query = context.MyClass.Select(x=>x);
hence I am refering to the Select extension method.
Regarding now the use of AsEnumerable()
The AsEnumerable(IEnumerable) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable to IEnumerable itself.
Also
AsEnumerable(IEnumerable) can be used to choose between query implementations when a sequence implements IEnumerable but also has a different set of public query methods available. For example, given a generic class Table that implements IEnumerable and has its own methods such as Where, Select, and SelectMany, a call to Where would invoke the public Where method of Table. A Table type that represents a database table could have a Where method that takes the predicate argument as an expression tree and converts the tree to SQL for remote execution. If remote execution is not desired, for example because the predicate invokes a local method, the AsEnumerable method can be used to hide the custom methods and instead make the standard query operators available.
For further documentation, please have a look here.
Related
So I have a class with a property like this:
public class Foo
{
[Column("GBBRSH")
public static string Gibberish { get; set;}
....
}
For saving data, I have it configured so that the update/insert statements use a custom function:
public static string GetTableColumnName(PropertyInfo property)
{
var type = typeof(ColumnAttribute);
var prop = property.GetCustomAttributes(type, false);
if (propr.Count() > 0)
return ((ColumnAttribute)prop.First()).Name;
return property.Name;
}
This handles fine, but I noticed that when I go to retrieve the data, it isn't actually pulling data back via the function for this particular column. I noticed that the other data present was pulled, but the column in question was the only field with data that didn't retrieve.
1) Is there a way to perhaps use the GetTableColumnName function for the retrieval part of Dapper?
2) Is there a way to force Dapper.NET to throw an exception if a scenario like this happens? I really don't want to have a false sense of security that everything is working as expected when it actually isn't (I get that I'm using mapping that Dapper.NET doesn't use by default, but I do want to set it up in that manner).
edit:
I'm looking in the SqlMapper source of Dapper and found:
private static IEnumerable<T> QueryInternal<T>(params) // my knowledge of generics is limited, but how does this work without a where T : object?
{
...
while (reader.Read())
{
yield return (T)func(reader);
}
...
}
so I learned about two things after finding this. Read up on Func and read up on yield (never used either before). My guess is that I need to pass reader.Read() to another function (that checks against column headers and inserts into objects appropriately) and yield return that?
You could change your select statement to work with aliases like "SELECT [Column("GBBRSH")] AS Gibberish" and provide a mapping between the attribute name and the poco property name.
That way, Dapper would fill the matching properties, since it only requires your POCO's to match the exact name of the column.
I have a custom model binder that inherits the DefaultModelBinder that looks like this.
Public Class GridFormBinder : Inherits DefaultModelBinder
Public Overrides Function BindModel(controllerContext As System.Web.Mvc.ControllerContext, bindingContext As System.Web.Mvc.ModelBindingContext) As Object
Dim result As Object = MyBase.BindModel(controllerContext, bindingContext)
'Code to handle special case for grid/List binding.
Return result
End Function
End Class
The reason I have this custom binder is that I am presenting various list of items in a grid (using devexpress mvc gridview) and I bind the controls in the grid to the list of items.
If I use a class derived from BusinessCollectionBase (from a very modified CSLA framework class) everything works exactly like I want. BusinessCollectionBase derives from a class that looks like...
<Serializable()> Public MustInherit Class BindableCollectionBase(Of T As IBusinessData)
Inherits CollectionBase
Implements IBindingList
Implements System.Collections.Generic.IEnumerable(Of T)
But if I bind to, say, a class that inherits from BindingList<Customer> the MyBase.BindModel(controllerContext, bindingContext) always returns nothing. I have tried various generic and non-generic BCL collections and the BindModel method always returns null.
Is there something I have to do to get the DefaultModelBinder to create and return the model for regular collections?
I looked at the source for DefaultModelBinder and figured out the problem.
In the DefaultModelBinder in the BindComplexModel method if the type is not an array and is a generic IEnumerable ( IEnumerable<>) AND it is an instance of ICollection<> it will call UpdateCollection. It cannot populate the collection for all the reasons stated. Because the count = 0 the UpdateCollection method returns null. So my classes that derive from ICollection (BindingList for example) will have this behavior.
However my custom collection actually derives from the old CollectionBase class (and it implements the generic IEnumerable seperatly). This means that BindComplexModel does not try to populate the collection and instead just binds to the object as normal.*
Personally I think this is a bug or at least an oversight. If you are binding to a collection and the form has 0 items (say the user has deleted all the rows) you would get nothing back from the default binding. But shouldn't you just get the collection with zero items? What is the reasoning behind returning nothing? This puts more work on the MVC developer because now they have to check for nothing first.
But anyway this is the reason why.
*And this is also the reason I couldn't get the examples of binding to collections to work with my classes. They are not arrays but neither are they IEnumerable<> or IDictionary<>. Yet another bug I think.
following function return List of records
public IList<T> GetAll()
{
return db.TabMasters.ToList<T>();
}
Error:
'System.Data.Objects.ObjectSet' does not contain a definition for 'ToList' and the best extension method overload 'System.Linq.Enumerable.ToList(System.Collections.Generic.IEnumerable)' has some invalid arguments
I imagine TabMasters is a strongly typed collection and therefore cannot return a list of a generic type. Have you tried db.TabMasters.ToList() instead?
The correct syntax is:
db.TabMasters.Cast<T>().ToList()
Documentation:
IEnumerable<TResult> Enumerable.Cast<TResult>(this IEnumerable source)
List<TSource> Enumerable.ToList<TSource>(this IEnumerable<TSource> source)
If you don't want to use the LINQ extension, List<T> has a public constructor that accepts a single IEnumerable<T> argument
IList<T> is the Interface that is used by List<T> and several other similar containers. You can't return an Interface itself - you have to return an object that implements IList<T>. Though I don't know exactly what your situation is, the best choice is most likely List<T>.
Also, you have a problem with the generic Type T. If you want the method to be generic, then you have to cast all the values in db.TabMasters to Type T. This gets tricky because you'll have to limit the possible Types used for T to prevent Exceptions caused by an invalid cast (see here). If you only need to return one type, then you should define that as the return type instead of using T. For example, lets say that all the values in db.TabMasters are string. Then you'd use:
public IList<string> GetAll()
{
return db.TabMasters.ToList();
}
If you really need the method to be generic, then you have to cast the values in db.TabMasters to the type you want to return:
public IList<T> GetAll<T>()
{
return db.TabMasters.Cast<T>().ToList();
}
Note that if the object type stored in db.TabMasters can't be cast to T, the method will throw an InvalidCastException.
Happy Coding!
have you looked here http://msdn.microsoft.com/en-us/library/dd412719.aspx
there appears to be a couple of methods that may be of use AsEnumerable and GetList seem like possibilities
I am using EF. This is my LINQ query
public List<Tuple<int, string>> GetList()
{
return (from c in DALContext.MST
select new Tuple<int, string>(c.CD, c.NAME)).ToList();
}
When i call GetList() it throws an exception : Only parameterless constructors and initializers are supported in LINQ to Entities
Instead when i rewrite this query:
List<Tuple<int, string>> lst = new List<Tuple<int, string>>();
var query= (from c in DALContext.MST
select new{c.CD, c.NAME});
foreach (var item in query)
{
lst.Add(new Tuple<int,string>(item.CD,item.NAME));
}
return lst;
It just works fine. Whats wrong with my first query???
The other answers are correct about what's going on, but I didn't see anyone mention the best way to make your code work: AsEnumerable()
public List<Tuple<int, string>> GetList()
{
return (from c in DALContext.MST.AsEnumerable()
select Tuple.Create(c.CD, c.NAME)).ToList();
}
The AsEnumerable method acts as a boundary between the code that should be translated into SQL and executed in the database server, and the code that should be executed in memory after we've gotten a response from the database. Putting it right after the table name tells EF to get all the records from the MST table, and then run the following code that creates tuples from the values that are returned.
I changed your new Tuple<int, string> into Tuple.Create mostly because I don't like typing generic type parameters any more than I have to.
LINQ to EF deals with queries a bit differently than LINQ to SQL. In LINQ to EF, you can not put a constructor with parameters in a LINQ expression, like you did here in the first bit of code:
from c in DALContext.MST
select new Tuple<int, string>(c.CD, c.NAME)
The constructor of Tuple is taking two parameters, and that is not allowed in LINQ to EF.
The reason is explained here:
In part this is a matter of wanting
LINQ to Entities to be more explicit
about the boundary between what parts
of your query execute on the server
and what part execute on the client.
With LINQ to SQL, for instance, it is
possible to write a LINQ query which
not only involves data from the server
and functions on the server but also
functions that can only be executed on
the client and to mix them in
together. The LINQ to SQL provider
will then do its best to untangle
things and execute the parts that it
can on the server and other parts on
the client. This is nice because it
is easy to just write whatever query
you want and if at all possible it
will work. On the other hand, it's
not so nice if you accidentally write
a query where the only part which can
execute on the server is the most
basic thing that returns all the data
in one or more tables and then have
all the filtering happen on the client
(with very nasty perf consequences).
With LINQ to Entities, the boundaries
are more explicit. When you write a
LINQ query against a LINQ to Entities
IQueryable implementation, the entire
query executes on the server, and if
some part of the query cannot be
executed on the server, then an
explicit boundary must be created with
something like ToQueryable() or
ToList(). Once that query is executed
and the data retrieved, then you can
use LINQ to Objects to further refine
the query if you so choose. This way
you explicitly know where your
boundaries are, and it's easier to
track down performance issues and the
like. One of the related limitations
is that the select statement in LINQ
to Entities can create anonymous types
or other types as long as they have a
default constructor and settable
parameters. This minimizes the chance
that the select statement has major
side effects.
Or you could just write
var query= (from c in DALContext.MST
select new{c.CD, c.NAME}).ToList().Select(x=>new Tuple(x.CD, x.NAME));
This has the advantage it only brings from the DB the two columns you need.
Your class needs to have a parameterless constructor for Linq to EF and you have to instantiate it like this:
public List<Tuple<int, string>> GetList()
{
return (from c in DALContext.MST
select new Tuple<int, string>(){CD = c.CD, Name = c.NAME}).ToList();
}
EDIT:
If you are not in the position to add a parameterless constructor to TUPLE (which is the case here as Tuple is not an class per se) then you have no choice with Linq to EF but to do this as a two step process:
public List<Tuple<int, string>> GetList()
{
List<MST> mstList = (from c in DALContext.MST
select c).ToList();
List<Tuple<int, string>> tupleList = new List<Tuple<int, string>>();
mstList.foreach(c => tupleList.add(new Tuple(c.CD, c.Name)));
return tupleList;
}
I am using EF4 CPT4 Code first and I have setup my ObjectContext to return IObjectSet so I would be able to Mock and test my repos. However, I now noticed that I am unable to use the .Include() method for eager loading like I would be able to if I used ObjectSet.
Is there a way to make this work???
Edit:
I added this extension method:
public static IQueryable<TSource> Include<TSource>(this IQueryable<TSource> source, string path)
{
var objectQuery = source as ObjectQuery<TSource>;
return objectQuery == null ? source : objectQuery.Include(path);
}
And it did add the Include() method, however I now get this error:
LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[PostHope.Core.DomainObjects.SiteAnnouncement] Include[SiteAnnouncement](System.Linq.IQueryable`1[PostHope.Core.DomainObjects.SiteAnnouncement], System.String)' method, and this method cannot be translated into a store expression.
Response by EF Team:
This is a known issue with CTP4, Include is an instance method on ObjectSet but when your set is typed as IObjectSet you are actually using an extension method on IQueryable that is included in CTP4. This extension method doesn't work with compiled queries but we will try and support this in the next release.
~Rowan