Is it possible to serialize a grouped list? - linq

I want to serialize grouped list. but I am getting error. Is it possible to serialize a grouped list? if yes then How?
Error :
Cannot serialize interface System.Linq.IGrouping`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[MyProject.MyNamespace.Elements, MyProject.MyNamespace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].
Code :
MemoryStream memoryStream = new MemoryStream();
List<IGrouping<string, Elements>> lstGroupedElements = listElements.GroupBy(member=> member.Type).ToList();
XmlSerializer objXmlSerializer = new XmlSerializer(typeof(List<IGrouping<string, Elements>>));
objXmlSerializer.Serialize(memoryStream, lstGroupedElements);

You can’t serialize Interfaces, because there is no way to restore them. For the Deserialization something like new IGrouping() would be needed and that’s not possible. So you have to build your own Grouping Structure, which holds the group name and its elements.
listElements.GroupBy(member=> member.Type)
.Select(g => new MyGrouping() {GroupName = g.Key, Elements = g.ToList()})
.ToList();
Edit:
MyGrouping could look like this:
public class MyGrouping
{
public string GroupName { get; set; }
public List<Element> Elements { get; set; }
}
or when you want a flattened XML implement some Interfaces:
public class MyGrouping : Collection<Element>, IGrouping<string, Element>
{
…
}

Related

Is there a way I can paginate the included list in a linq query

Hope you're doing well,
I was trying to optimize my reads with entity framework, where I arrived at a position, where I get a record from database by id, and I want to include a one-to-many related list, but I don't want to get all data of the list, just a few, so I want to kind of paginate it.
I want to do this process as long as data is in IQueryable state, I don't want to load all data of list in memory and that paginate it as enumerable.
Let's say the query is like below:
var author = await _dbContext.Authors.Where(x => x.Id == id)
.Include(x => x.Books) // <-- paginate this !!??
.FirstOrDefaultAsync();
Entities represent Data state. Pagination and presentation concerns are View state. Entity Framework can help bridge that gap, but it does so by enabling projection so that you can build View state from Data state. Don't pass entities to views, instead build and pass ViewModels to represent the data in accordance to translations and limitations you want for the view.
For instance if you want to pass Author details with their 5 most recent books:
public class AuthorViewModel
{
public int Id { get; set; }
public string Name { get; set; }
// Any other relevant fields...
public ICollection<BookViewModel> RecentBooks = new List<BookViewModel>();
}
public class BookViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime PublishedDate { get; set; }
// Any other relevant fields...
}
var author = await _dbContext.Authors
.Where(x => x.Id == id)
.Select( x => new AuthorViewModel
{
Id = x.Id,
Name = x.Name,
RecentBooks = x.Books
.OrderByDescending(b => b.PublishedDate)
.Select(b => new BookViewModel
{
Id = b.Id,
Name = b.Name,
PublishedDate = b.PublishedDate
}).Take(5)
.ToList()
}).SingleOrDefault();
This gives you the benefit of structuring the data how you want to present it, while generating efficient queries against the database. You can configure tools like Automapper to perform this kind of mapping to use it's ProjectTo<AuthorViewModel>() as a more succinct alternative.

Create Linq Expression<Func<TModel, TDataType>> for the given property name

How do I create the proper Linq expression for the following property with navigation: m.Model.Property1?
I have a model like this:
public class ViewModel
{
public object Model { get; set; } //=Model is acutally the EntityModel
}
public class EntityModel
{
public string Property1
}
I have now something like this but can't find the Property1.
For the 2 last lines below I can't find a proper solution to get this, so I can send it to the HtmlHelper
var parameter = Expression.Parameter(typeof(ViewModel), "m"); //=ViewModel
var baseType = Html.ViewData.Model.GetType(); //=typeof(ViewModel)
var navExpr = Expression.Convert(Expression.Property(parameter, "Model"), typeof(EntityModel));
var exprProp = Expression.Property(navExpr , "Property1"); //This should create {m.Model.Property1}
var navExpr2 = Expression.Convert(exprProp, typeof(object));
return Expression.Lambda<Func<EditViewModel, object>>(navExpr2, parameter);
You need to convert object to EntityModel. This can be achieved with Expression.Convert. Try changing your navExpr to this:
var navExpr = Expression.Convert(Expression.Property(parameter, "Model"), typeof(EntityModel));
In your code sample in question Property1 is actually a field, not a property (you can use Expression.PropertyOrField or Expression.Field if it is so).

Expression.Property(param, field) is "trolling" me [System.ArgumentException] = {"Instance property 'B.Name' is not defined for type A"}

Once again, I am facing an issue, this time with LINQ Expression builder and this time I am even struggling to find the reason why it's not working. I have a Database-First EF project with quite a few tables. For this specific case, I have to use 2 of them - DocHead and Contragent. MyService.metadata.cs looks like this:
[MetadataTypeAttribute(typeof(DocHead.DocHeadMetadata))]
public partial class DocHead
{
// This class allows you to attach custom attributes to properties
// of the DocHead class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class DocHeadMetadata
{
// Metadata classes are not meant to be instantiated.
private DocHeadMetadata()
{
}
public string doc_Code { get; set; }
public string doc_Name { get; set; }
public string doc_ContrCode { get; set; }
//...
[Include]
public Contragent Contragent { get; set; }
}
}
[MetadataTypeAttribute(typeof(Contragent.ContragentMetadata))]
public partial class Contragent
{
// This class allows you to attach custom attributes to properties
// of the Contragent class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class ContragentMetadata
{
// Metadata classes are not meant to be instantiated.
private ContragentMetadata()
{
}
public string Code { get; set; }
public string Name { get; set; }
//...
I take some docHeads like this:
IQueryable<DocHead> docHeads = new MyEntities().DocHead;
Then I try to sort them like this:
docHeads = docHeads.OrderByDescending(x => x.Contragent.Name);
It is all working like I want it. I get those docHeads sorted by the name of the joined Contragent. My problem is that I will have to sort them by a field, given as a string parameter. I need to be able to write something like this:
string field = "Contragent.Name";
string linq = "docHeads = docHeads.OrderByDescending(x => x." + field + ")";
IQueryable<DocHead> result = TheBestLinqLibraryInTheWorld.PrepareLinqQueryable(linq);
Unfortunately, TheBestLinqLibraryInTheWorld does not exist (for now). So, I have set up a method as a workaround.
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(param, SortField); // normally returns x.sortField
var exp = Expression.Lambda(prop, param); // normally returns x => x.sortField
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); // normally returns sth similar to q.OrderBy(x => x.sortField)
return q.Provider.CreateQuery<T>(mce);
}
Normally... yes, when it comes to own properties of the class DocHead - those prefixed with doc_. The disaster strikes when I call this method like this:
docHeads = docHeads.OrderByField<DocHead>("Contragent.Name", true); // true - let it be Ascending order
To be more specific, the exception in the title is thrown on line 2 of the method OrderByField():
var prop = Expression.Property(param, SortField);
In My.edmx (the model), the tables DocHead and Contragent have got a relation already set up for me, which is the following: 0..1 to *.
Once again, I have no problem writing "static" queries at all. I have no problem creating "dynamic" ones via the method OrderByField(), but only when it comes to properties of the class DocHead. When I try to order by a prop of the joined Contragent class - the disaster strikes. Any help will be greatly appretiated, thank you!
The problem is that Expression.Property method does not support nested properties. It does exactly what it says - creates expression that represents a property denoted by propertyName parameter of the object denoted by the expression parameter.
Luckily it can easily be extended. You can use the following simple Split / Aggregate trick anytime you need to create a nested property access expression:
var prop = SortField.Split('.').Aggregate((Expression)param, Expression.Property);

how to pass selectmany combine with groupby to MVC view

i have the following lambda expression in MVC application.
var toprating= _db.Movie.SelectMany(m => m.Rating.Select(r=> new
{
movieID=r.MovieID,
MovieTitle= m.Title
})).GroupBy(m=>m.movieID).ToList();
ViewBag.TopMovie = toprating;
}
i want to pass this to my view.
i try writing the following in my view
IEnumerable<Movie> TopMovies = ViewBag.TopMovie;
but got this error
Cannot implicitly convert type 'object' to 'System.Collections.Generic.IEnumerable<Movie>'. An explicit conversion exists (are you missing a cast?)
any help will be appriciated.
i would recommend that you make a class that depicts your domain entity (Movie) like
public class MyMovie{
public int movieID { get; set; }
public string MovieTitle { get; set; }
}
and modify your linq query as
var toprating= _db.Movie.SelectMany(m => m.Rating.Select(r=> new MyMovie
{
movieID=r.MovieID,
MovieTitle= m.Title
})).GroupBy(m=>m.movieID).ToList();
ViewBag.TopMovie = toprating;
IEnumerable<MyMovie> TopMovies = ViewBag.TopMovie;
i have assumed that the Movie is your Domain entity, use view models to pass on to the views.
you may find this helpful

Databinding error with DropDownListFor in ASP.NET MVC 3?

I have a database table called Genre with the following fields:
Id (Seeded Primary Key)
Name (Name of Genre - Romance, Western, etc...)
I have a Model class called GenreDropDownModel which contains teh following code:
public class GenreDropDownModel
{
private StoryDBEntities storyDB = new StoryDBEntities();
public Dictionary<int,string> genres { get; set; }
public GenreDropDownModel()
{
genres = new Dictionary<int,string>();
foreach (var genre in storyDB.Genres)
{
genres.Add(genre.Id, genre.Name);
}
}
}
My Controller Write action is declared as:
public ActionResult Write()
{
return View(new GenreDropDownModel());
}
Finally, my view Write.cshtml is where I get the following error:
DataBinding: 'System.Collections.Generic.KeyValuePair`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' does not contain a property with the name 'Id'.
#model Stories.Models.GenreDropDownModel
#{
ViewBag.Title = "Write";
}
<h2>Write</h2>
#Html.DropDownListFor(model => model.genres,new SelectList(Model.genres,"Id","Name"))
Since Model.genres is a dictionary, you should do this with
#Html.DropDownListFor(model => model.genres,new SelectList(Model.genres,"Key","Value"))
This is because when enumerated, an IDictionary<TKey, TValue> produces an enumeration of KeyValuePair<TKey, TValue>. That's the type you need to look at to see how the properties are named.

Resources