C# LINQ Projecting to a class - c#

I call a web service and I get a response object back in JSON.
I would like the results in a list so I can enumerate through them.
So I have this piece of code which works as intended.
var players = getPlayersResponse.Payload
.Select(p => new { p.Name, p.Pid, p.Model, p.Ip, p.Gid })
.ToList();
foreach (var p in players)
{
Players.Items.Add(p.Name);
}
I would like to return a list of a known class instead so have tried following:
var players = getPlayersResponse.Payload
.Select(p => new Player { Name= p.Name, Pid= p.Pid, Model= p.Model, Ip= p.Ip, Gid= (int)p.Gid })
.ToList();
foreach (var p in players)
{
Players.Items.Add(p.Name);
}
And the class
public class Player
{
public string Name { get; set; }
public int Pid { get; set; }
public string Model { get; set; }
public string Ip { get; set; }
public int Gid { get; set; }
}
That version do not work (listbox is not populated with items) and I get no errors at all and compiler do not complain. Strangely though the complier did complain if I did not cast p.Gid to int. It is the same json result in both cases.
I cannot see what I am doing wrong.

I solved the problem by changing the class like this
public class Player
{
public string Name { get; set; }
public int Pid { get; set; }
public string Model { get; set; }
public string Ip { get; set; }
public int? Gid { get; set; }
}

Related

How can i fix Cannot implicitly convert Generic.List< > to Generic.List< >

How can i fix Cannot implicitly convert System.Collections.Generic.List <RPHistory> to System.Collections.Generic.List <RPHistory> exception error.
I am trying to combine two Entities together to get a single list
RP Entity Class:
public class RP
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
}
RPHistory Entity Class :
public class RPHistory:
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
}
And I created this third class
RpWithHistory Class :
public class RpWithHistory {
public int ID;
public int RPID;
public string Name;
public int ProductID;
public List<RPHistory> History;
}
Linq Query
var RPs = await Context.RP.Where(b => b.ProductID == request.ID)
.Select(x=> new RpWithHistory {
ID = x.ID,
RPID = x.RPID,
Name = x.Name,
ProductID = x.ProductID,
History = Context.RPHistory
.Where(y=> y.RPID
== x.RPID)
.ToList()
}
).ToListAsync();
But i get this error,
>Cannot implicitly convert System.Collections.Generic.List <RPHistory> to
>System.Collections.Generic.List <RPHistory> exception error
Thanks!
I am not sure why you're doing that. Can I suggest this?
You do not need to go all that way of creating a class that joins the two. Just create a Navigation property on your RP that points to RPHistory Objects.
public class RP
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
public ICollection<RPHistory> HistoryList { get; set; } // Navigation Property
}
public class RPHistory:
{
public int ID { get; set; }
public int RPID { get; set; }
public string Name { get; set; }
public int ProductID { get; set; }
[ForeignKey(nameof(RPID))] // Identify the Foreign Key from RP Class
public RP RP { get; set; } // Navigation back to RP
}
Then you can chain everything into a single list using LINQ:
var RPs = Context.RP.Where(rp => rp.ProductID == request.ID)
.Include(rp=>rp.RPHistory) // This includes RPHistory
.ToList();
You need to clone or create a new list.
Option 1: Use ConvertAll
List<RPHistory> pPHistoryCopy = rphWithHistory.RPHistory.ConvertAll(history => new RPHistory(rphWithHistory.RPHistory));
Option 2:
//Clone Extension
static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}
Use the clone extention

Group by for list in list

I have list of Issues : List<Issue> where Issue is class :
public class Issue : BaseEntity
{
private string m_KeyString;
[JsonProperty("expand")]
public string Expand { get; set; }
[JsonProperty("id")]
public int Id { get; set; }
#region Special key solution
[JsonProperty("key")]
public string ProxyKey
{
get
{
return Key.ToString();
}
set
{
m_KeyString = value;
}
}
[JsonIgnore]
public IssueKey Key
{
get
{
return IssueKey.Parse(m_KeyString);
}
}
#endregion Special key solution
[JsonProperty("fields")]
public Fields Fields { get; set; }
}
Class Fields looks like this :
public class Fields
{
[JsonProperty("summary")]
public string Summary { get; set; }
[JsonProperty("assignee")]
public Assignee Assignee { get; set; }
[JsonProperty("worklog")]
public List<WorkLog> WorkLogs { get; set; }
}
And class WorkLog:
public class WorkLog : BaseEntity
{
[JsonProperty("updateAuthor")]
public string Author { get; set; }
[JsonProperty("timeSpent")]
public string TimeSpent { get; set; }
[JsonProperty("timeSpentSeconds")]
public int TimeSpentSeconds { get; set; }
}
I want to get on output Author - SUM (TimeSpentSeconds)
So i need to group by Author and then get SUM.
For each item in Issues list I make this :
var sumTime = issue.Fields.WorkLogs.GroupBy(x => x.Author).Select(x => new
{
User = x.Key.Name,
Amount = x.Sum(s => s.TimeSpentSeconds)
});
which group by user and count sum.
But how can i manage the same not just for one item in list but for all list?
OK, I think you're saying that you can run this query for a single Issue, but you want to be able to run it over all the items in a List<Issue>.
I'm assuming that you don't want to break it out separately by Issue, so what I'd do is this:
List<Issue> issues = // get Issues from wherever
var sumTime = issues.SelectMany(issue => issue.Fields.WorkLogs)
.GroupBy(x => x.Author)
.Select( x => new
{
x.Key.Name,
Amount = x.Sum(s => s.TimeSpentSeconds)
}
);
Disclaimer:
this was done entirely from memory, with no testing.

Find object with in class using LINQ

I want to return the item that has the profile ID I send. So in order to do this I will need to loop through all of the Items -> WebProproperties -> profile. The Class structure is at the end of the question.
I would rather use LINQ than create a nested foreach. I have been trying to get this to work for more than an hour now. I am stuck.
My first idea was to simply use where. But that doesn't work because you need to have something on the other side that needs to equal.
this.Accounts.items.Where(a => a.webProperties.Where(b => b.profiles.Where(c => c.id == pSearchString)) ).FirstOrDefault();
My second idea was to try using Exists which I don't have much experience with:
Item test = from item in this.Accounts.items.Exists(a => a.webProperties.Exists(b => b.profiles.Exists(c => c.id == pSearchString))) select item;
This doesn't work either:
Could not find an implementation of query pattern for source type 'Bool'
public RootObject Accounts {get; set;}
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
public List<Item> items { get; set; }
}
public class Profile
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
public class WebProperty
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string internalWebPropertyId { get; set; }
public string level { get; set; }
public string websiteUrl { get; set; }
public List<Profile> profiles { get; set; }
}
public class Item
{
public string id { get; set; }
public string kind { get; set; }
public string name { get; set; }
public List<WebProperty> webProperties { get; set; }
}
You can use Any() to determine existence. Also, note that many of the extension methods have overloads which take a predicate, including FirstOrDefault():
this.Accounts.items.FirstOrDefault(a => a.webProperties
.Any(b => b.profiles
.Any(c => c.id == pSearchString)));
You are looking for the .Any() operation I think. This will return true/false for whether there are any items matching your query.
For example:
if (this.Accounts.Items.Any(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)));
EDIT: You have full answer (was posted while I was composing mine) and as pointed out in comments my answer isn't actually returning your found item, just letting you know whether there is one. You can rework the first .Any to be a .FirstOrDefault to get that match.
E.g.
var result = this.Accounts.Items.FirstOrDefault(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)))
You can use the below mentioned code.
var abc = rr.items.Where(p => p.webProperties.Any(c => c.profiles.Any(d => d.id == "1"))).FirstOrDefault();
Just for your reference, your class should look like:
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
private List<Item> _items=new List<Item>();
public List<Item> items
{
get { return _items; }
set { _items = value; }
}
}

Filtering LINQ child collection EF

Im having some problems trying to do this filtering and im sure it can be done better than what im doing. I will show my classes and how im solving it but i was wondering if I could use Linq to filter this. My Classes:
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public int Order { get; set; }
public virtual List<FeatureType> Features { get; set; }
}
public class ItemType
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<FeatureType> FeatureTypes { get; set; }
public override string ToString()
{
return Name;
}
}
public class FeatureType
{
public int Id { get; set; }
public string Name { get; set; }
public Section Section { get; set; }
public virtual List<ItemType> ItemTypes { set; get; }
}
I'm trying to get all Sections, and filter Features by an ItemTypeID, so only the FeatureTypes of that ItemTypes are listed. What Im doing now its just getting all sections, and just do a for and just add the ones that work for me in other:
public ItemTypeFeatureViewModel(int myItemTypeId, IUnitOfWork myUnitOfWork)
{
ItemTypeId = myItemTypeId;
unitOfWork = myUnitOfWork;
Sections = unitOfWork.SectionRepository.Get(includeProperties: "Features")
.ToList();
foreach (var item in Sections)
{
var x = new List<FeatureType>();
foreach (var feature in item.Features)
{
foreach (var itemType in feature.ItemTypes)
{
if (itemType.Id == ItemTypeId)
{
x.Add(feature);
break;
}
}
}
item.Features = x;
}
}
Can i improve this and avoid all this foreach?
You can't filter out included collection on server side, but you can replace two inner loops with:
item.Features = item.Features
.Where(f => f.ItemTypes.Any(i => i.Id == ItemTypeId))
.ToList();
That will select only those features which have at least one item type with id you provided.
Try the following:
Sections
.ForEach(x => x.Features = x.Features.Where(y => y.Any(z => z.Id == ItemTypeId))
.ToList());

How can I get the data from fields of an .Include in a LINQ Query?

I am using Entity Framework 5 and I have these classes. What I want to do is to be able to get the data to populate the view listed below:
public partial class Subject
{
public int SubjectId { get; set; }
public string Name { get; set; }
public virtual ICollection<Topic> Topics { get; set; }
}
public partial class Topic
{
public int TopicId { get; set; }
public string Name { get; set; }
public int SubjectId { get; set; }
public virtual Subject Subject { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public partial class SubTopic
{
public int SubTopicId { get; set; }
public string Name { get; set; }
public int TopicId { get; set; }
public virtual Topic Topic { get; set; }
}
Now I am trying to write a LINQ query to populate this class:
public class TopicSubTopicSelect
{
public int TopicId { get; set; }
public int SubTopicId { get; set; }
public string TopicName { get; set; }
public string SubTopicName { get; set; }
}
So far I have this:
return _subjectsRepository
.GetAll()
.Where(s => s.SubjectId == subjectId)
.Include(s => s.Topics.SelectMany(t => t.SubTopics))
.AsEnumerable()
.Select(item => new TopicSubTopicSelect(item.TopicId <<<
item.SubTopicId <<
item.Topic.Name <<
item.Name <<))
.ToList();
Can someone tell me how I can get data from the fields I marked with <<. I tried to do .item.Topic.TopicId etc but that does not seem to work.
You shouldn't start from Subject. You just start from SubTopic Repository, and you won't even need to use .Include. Do it like this:
_subTopicRepository
.GetAll()
.Where(s => s.Topic.SubjectId == subjectId)
.Select(s => new TopicSubTopicSelect()
{
TopicId = s.TopidId,
SubTopicId = s.SubTopicId,
TopicName = s.Topic.Name,
SubTopicName = s.Name
})
.ToList();
As I mentioned in my comment on ataravati's answer, you shouldn't actually have a SubTopicRepository so you are correct in starting at SubjectsRepository however you are querying by the Subject ID so you shouldn't be going via GetAll(), you should have a Get(int id) method. The include should be handled as an implementation detail inside Get as the children (SubTopics) are part of the Subject. That makes the method call look like this instead:
return _subjectsRepository
.Get(subjectId)
.SelectMany(subject => subject.SubTopics))
.Select(subTopic => new TopicSubTopicSelect
{
TopicId = subTopic.TopicId,
SubTopicId = subTopic.SubTopicId,
TopicName = subTopic.Topic.Name,
SubTopicName = subTopic.Name
}).ToList();

Categories

Resources