How can i do recursive linq function? - c#

I want to make a menu, and this is my code. I need recursive function.
var newTopMenu = new TopMenuVm
{
TopMenus = (from amp in allMasterPages
orderby amp.DisplayOrder
select new TopMenuVm
{
DisplayOrder = amp.DisplayOrder,
Url = string.IsNullOrEmpty(amp.Url) ? GenerateUrl(amp.Page1.OrderBy(x => x.DisplayOrder).FirstOrDefault()) : amp.Url,
PageId = amp.PageId,
PageTitle = amp.PageTitle,
TopMenus = (from submenu in amp.Page1
orderby amp.DisplayOrder
select new TopMenuVm
{
DisplayOrder = submenu.DisplayOrder,
PageTitle = submenu.PageTitle,
PageId = submenu.PageId,
Url = string.IsNullOrEmpty(submenu.Url) ? GenerateUrl(submenu) : submenu.Url,
}).ToList()//Here is go on infinite...,
}).ToList()
};
How can i do that with linq, please help...

Is something like this what you are looking for?
// allMasterPages is type List<MyPage> - change to whatever it really is
Func<List<MyPage>, List<TopMenuVM>> funcTopMenuVM = null;
funcTopMenuVM = ((pages) => {
return
(from amp in pages
orderby amp.DisplayOrder
select new TopMenuVM()
{
DisplayOrder = amp.DisplayOrder,
Url = string.IsNullOrEmpty(amp.Url) ? GenerateUrl(amp.Page1.OrderBy(x => x.DisplayOrder).FirstOrDefault()) : amp.Url,
PageId = amp.PageId,
PageTitle = amp.PageTitle,
TopMenus = funcTopMenuVM(amp.Page1)
}).ToList();
});
var newTopMenu = new TopMenuVM()
{
TopMenus = funcTopMenuVM(allMasterPages)
};
Of course, it doesn't have to be a Func<>, it could just be a regular method that returns a List<TopMenuVM> and takes a List<MyPage> (or whatever allMasterPages is).

Related

C# exclude duplicate code from two method returning expression

I have two functions, returning expression translated to EF.:
public static Expression<Func<TripLanguage, TripViewModel>> ToSearchModel(ILookup<int, TagViewModel> tags)
{
return tripLanguage => new TripViewModel()
{
From = tripLanguage.From,
To = tripLanguage.To,
Annotation = tripLanguage.Description.Truncate(Strings.TRUNCATE_ANOTATION),
Level = tripLanguage.Trip.Level,
BicycleType = tripLanguage.Trip.BicycleType,
UrlId = tripLanguage.UrlId,
Distance = tripLanguage.Trip.Distance,
Tags = tags[tripLanguage.TripId], //This is only different and in function args of course
MainImage = tripLanguage.Trip.Images.OrderBy(s => s.Date).Select(i => new ImageViewModel
{
Filename = i.Filename,
Id = i.Id,
Title = i.Title
}).Take(1)
};
}
public static Expression<Func<TripLanguage, TripViewModel>> ToSearchModel()
{
return tripLanguage => new TripViewModel()
{
From = tripLanguage.From,
To = tripLanguage.To,
Annotation = tripLanguage.Description.Truncate(Strings.TRUNCATE_ANOTATION),
Level = tripLanguage.Trip.Level,
BicycleType = tripLanguage.Trip.BicycleType,
UrlId = tripLanguage.UrlId,
Distance = tripLanguage.Trip.Distance,
MainImage = tripLanguage.Trip.Images.OrderBy(s => s.Date).Select(i => new ImageViewModel
{
Filename = i.Filename,
Id = i.Id,
Title = i.Title
}).Take(1)
};
}
The only different is Tags collection. Is possible something like call method, without arguments and add Tags attribute which exclude duplicate code? Or Use some inheritance expression?
Thank you for your time.
Change the first method to work with null tags, and provide a null default for it:
public static Expression<Func<TripLanguage,TripViewModel>> ToSearchModel(ILookup<int, TagViewModel> tags = null) {
return tripLanguage => new TripViewModel() {
From = tripLanguage.From,
To = tripLanguage.To,
Annotation = tripLanguage.Description.Truncate(Strings.TRUNCATE_ANOTATION),
Level = tripLanguage.Trip.Level,
BicycleType = tripLanguage.Trip.BicycleType,
UrlId = tripLanguage.UrlId,
Distance = tripLanguage.Trip.Distance,
Tags = tags?[tripLanguage.TripId], // <<== Note the question mark
MainImage = tripLanguage.Trip.Images.OrderBy(s => s.Date).Select(i => new ImageViewModel
{
Filename = i.Filename,
Id = i.Id,
Title = i.Title
}).Take(1)
};
}

Listing in WCF Entity

I have a problem with LINQ query (see comment) there is a First method and it only shows me the first element.
When I write in the console "Sales Representative" it shows me only the first element of it as in
I would like to get all of data about Sales Representative. How can I do it?
public PracownikDane GetPracownik(string imie)
{
PracownikDane pracownikDane = null;
using (NORTHWNDEntities database = new NORTHWNDEntities())
{
//Employee matchingProduct = database.Employees.First(p => p.Title == imie);
var query = from pros in database.Employees
where pros.Title == imie
select pros;
// Here
Employee pp = query.First();
pracownikDane = new PracownikDane();
pracownikDane.Tytul = pp.Title;
pracownikDane.Imie = pp.FirstName;
pracownikDane.Nazwisko = pp.LastName;
pracownikDane.Kraj = pp.Country;
pracownikDane.Miasto = pp.City;
pracownikDane.Adres = pp.Address;
pracownikDane.Telefon = pp.HomePhone;
pracownikDane.WWW = pp.PhotoPath;
}
return pracownikDane;
}
Right now you are just getting the .First() result from the Query collection:
Employee pp = query.First();
If you want to list all employees you need to iterate through the entire collection.
Now, if you want to return all the employee's you should then store each new "pracownikDane" you create in some sort of IEnumerable
public IEnumerable<PracownikDane> GetPracownik(string imie) {
using (NORTHWNDEntities database = new NORTHWNDEntities())
{
var query = from pros in database.Employees
where pros.Title == imie
select pros;
var EmployeeList = new IEnumerable<PracownikDane>();
foreach(var pp in query)
{
EmployeeList.Add(new PracownikDane()
{
Tytul = pp.Title,
Imie = pp.FirstName,
Nazwisko = pp.LastName,
Kraj = pp.Country,
Miasto = pp.City,
Adres = pp.Address,
Telefon = pp.HomePhone,
WWW = pp.PhotoPath
});
}
return EmployeeList;
}
Then, with this returned List you can then do what ever you wanted with them.

expression cannot be translate into store expression

I have to download some complex datas from database which aggregate lots of usefull infos about post. I would like to do something like this:
var list = (from message in db.BLOGS_MESSAGES
where message.BLOG_ID == blogId
orderby message.CREATED_DATE descending
select new BlogMessage()
{
AUTHORS = **(from author in message.AUTHORS
select author.USERS).ToArray()**,
CREATED_BY = message.CREATED_BY,
CREATED_DATE = message.CREATED_DATE,
BLOG_MESSAGE_ID = message.POST_ID,
MESSAGE_TITLE = message.TITLES.TITLE,
TAGS = **(from tag in message.TAGGED_MESSAGES
select tag.TAGS).ToArray()**,
LOGIN = message.USERS.LOGIN,
MESSAGE = message.MESSAGES.MESSAGE,
MESSAGE_ID = message.MESSAGE_ID,
POST_NOTE = message.POST_NOTES.Sum(x => (long?)x.NOTE) ?? 0 / message.POST_NOTES.Count(),
}).ToList();
but it doesn't work. It throws an exception that can't translate expression in store expression.
so far i did it in this way:
var mlist = (from message in db.BLOGS_MESSAGES
where ....
orderby ....
select new {
AUTHORS = (from author in message.AUTHORS
select author.USERS),
....
}
List<BlogMessage> list = new List<BlogMessage>();
foreach(var item in mlist)
{
list.Add(new BlogMessage()
{
AUTHORS = item.AUTHORS.ToArray(),
...
});
}
is it possible to make it work 'in first way - style'?
You can either:
Change BlogMessage.AUTHORS to be an IEnumerable<USERS> and remove the .ToArray() calls in the query like Grundy suggested.
Bring the results into memory before creating BlogMessage's.
For example:
var step1 = db.BLOGS_MESSAGES
.Where(...)
.Select(message => new {
Authors = message.AUTHORS.Select(a => a.USERS), // No .ToArray()
...
}).ToList();
var step2 = step1.Select(message => New BlogMessage {
Authors = message.Authors.ToArray(),
...
}).ToList();

Creating multiple results from Linq query

I have a fairly unique situation, I have never needed to this before anyways. I have a Linq query that returns data from a database using EF4.1. I want to create multiple similar (same signature) anonymous (or even named if necessary) results from each query result.
Here's the code i'm using now:
var data = getMyData().Select(x =>
new
{
GoalName = x.GoalType.Name,
Start = x.StartDate,
End = x.EndDate,
x.StartValue,
x.CheckIns
}).ToList();
var r1 = data.Select(x =>
new
{
title = x.GoalName,
start = x.Start.ToString(),
end = x.End.ToString(),
className = "hidden",
type = "goal"
});
var r2 = data.Select(x =>
new
{
title = string.Format("Start: {0:0.##}", x.StartValue),
start = x.Start.ToString(),
end = x.Start.ToString(),
className = "",
type = ""
});
var r3 = data.Select(x =>
new
{
title = "End",
start = x.End.ToString(),
end = x.End.ToString(),
className = "",
type = ""
});
var r4 = data.SelectMany(x => x.CheckIns)
.Select(y =>
new
{
title = y.CheckInValue.Value.ToString(),
start = y.CheckInDateTime.ToString(),
end = y.CheckInDateTime.ToString(),
className = "",
type = ""
});
var result = r1.Union(r2).Union(r3).Union(r4);
Now maybe this is as good a way as any, but I can't help feeling that i'm missing something.
Is there a better solution?
What you have is actually OK I think.
But StevenzNPaul's suggestion not that bad, here's how you can use the let keyword to store the different projections, then select the results individually (for brevity, I did not project all the fields, but you get the point):
var query = from x in data
let result1 = new {title = x.GoalName, start = x.Start}
let result2 = new {title = string.Format("Start: {0:0.##}", x.StartValue), start = x.Start}
let result3 = new {title = "End", start = x.End}
let checkins = x.CheckIns.Select(checkin => new { title = "...", start = checkin.Start })
from result in new[] { result1, result2, result3 }.Concat(checkins)
select result;
Obviously, whether this is better is a matter of preference. Also, this will result in a different ordering, which may or may not be a problem for you.
You can create an iterator using yield which also has the advantage of being evaluated lazily (doesn't require the ToList()). I created a typed class Result to hold the query results
private IEnumerable<Result> PerformQuery()
{
var results= getMyData().Select(x => new {GoalName = x.GoalType.Name,
Start = x.StartDate, End = x.EndDate, x.StartValue, x.CheckIns});
foreach (var result in results)
{
yield return new Result() { Title = result.GoalName, Start = result.Start.ToString(), End = result.End.ToString(), ClassName = "Hidden", Type = "Goal" };
yield return new Result() { Title = String.Format("Start: {0:0.##}",result.StartValue), Start = result.Start.ToString(), End = result.Start.ToString() }
yield return new Result() { Title = "End", Start = result.End.ToString(), End = result.End.ToString() };
foreach (var checkIn in result.CheckIns)
yield return new Result() { Title = checkIn.CheckInValue.Value.ToString(), Start = checkIn.CheckInDateTime.ToString(), End = checkIn.CheckInDateTime.ToString() };
}
}
try using let keyword it will work for you.

How can I access this in C#?

private void LoadSubforums(Forum forum, XElement subforumsXML)
{
forum.Subforums = (from forumItem in subforumsXML.Descendants("forum")
select new Subforum()
{
ID = (string)forumItem.Element("id"),
Name = (string)forumItem.Element("name"),
URL = (string)forumItem.Element("url"),
Description = (string)forumItem.Element("description"),
Type = (string)forumItem.Element("type"),
TopicCount = (string)forumItem.Element("topics"),
ReplyCount = (string)forumItem.Element("replies"),
LastPost = ParseLastPost(forumItem.Element("lastpost")),
IsRedirect = How can I access the LastPost attribute here? :S
}).ToList();
}
}
The IsRedirect attribute of the Subforum class is dependant of a value inside the LastPost attribute.
Is it possible for me to access this attribute from within the Linq query?
EDIT:
This is what I'm trying to do:
forum.Subforums = (from forumItem in subforumsXML.Descendants("forum")
let lastPost = ParseLastPost(forumItem.Element("lastpost"))
select new Subforum()
{
ID = (string)forumItem.Element("id"),
Name = (string)forumItem.Element("name"),
URL = (string)forumItem.Element("url"),
Description = (string)forumItem.Element("description"),
Type = (string)forumItem.Element("type"),
TopicCount = (string)forumItem.Element("topics"),
ReplyCount = (string)forumItem.Element("replies"),
LastPost = lastPost,
IsRedirect = if(lastPost.ID == null) ? true ; false
}).ToList();
But this code doesn't compile.
forum.Subforums = (from forumItem in subforumsXML.Descendants("forum")
let lastPost = ParseLastPost(forumItem.Element("lastpost"))
select new Subforum()
{
ID = (string)forumItem.Element("id"),
Name = (string)forumItem.Element("name"),
URL = (string)forumItem.Element("url"),
Description = (string)forumItem.Element("description"),
Type = (string)forumItem.Element("type"),
TopicCount = (string)forumItem.Element("topics"),
ReplyCount = (string)forumItem.Element("replies"),
LastPost = lastPost,
IsRedirect = !string.IsNullOrEmpty(lastPost)
}).ToList();

Categories