C# Dynamic Test for fields before aggregation functions like GroupBy - c#

Assume the following code:
var citizens = await _stateProvider.SelectWhere(whereParams);
var retDto = new PercentGroupBy()
{
Total = citizens.Count,
Elements = citizens.GroupBy(p => p.Content.Current.AggState.ToString()).ToDictionary(g => g.Key, g => g.Count())
};
return retDto;
citizens is a list of the following class:
public class Citizen {
public string ETag { get; set; }
public string Name{ get; set; }
public dynamic Content { get; set; }
}
What is the best option to test that "p.Content.Current.AggState" property exists?
SelectWhere might return a few citizens where Content.Current is null and therefore asking for AggState throws error.

Oops I found the answer while posting the question, so here it is to share the knowledge:
Add a fluid "Where" before "GroupBy"
var citizens = await _stateProvider.SelectWhere(whereParams);
var retDto = new PercentGroupBy()
{
Total = citizens.Count,
Elements = citizens
.Where(p => p.Content.Current != null)
.GroupBy(p => p.Content.Current.AggState.ToString())
.ToDictionary(g => g.Key, g => g.Count())
};
return retDto;

Related

Get Result of GroubBy() as List<List<SomeObject>>

class SomeObject
{
int Value { get; set; }
string ID { get; set; }
}
var TheList = List<SomeObject> { ... }
var groupedObjects = TheList.GroupBy(o => o.ID);
The return type is IEnumerable<IGrouping<SomeObject, string, SomeObject>>
In the Debug "Results View" the Items are grouped correctly.
How can I get List<List<SomeObject>> as return?
A simple ToList() does obviously not work.
You need two ToList()s:
TheList.GroupBy(o => o.ID, c => c)
.Select(g=>g.ToList())
.ToList();
Here's a similar question with good answers: How to get values from IGrouping
You could try
var groupedObjects = TheList.GroupBy(o => o.ID, c => c).SelectMany();

Selecting specific columns from GroupBy list

Model:
public class Ticket {
public Ticket();
public int Id { get; set; }
public virtual TicketUrgency TicketUrgency { get; set; }
public int UrgencyId { get; set; }
}
public class TicketUrgency {
public TicketUrgency();
[Key]
public int Id { get; set; }
[MaxLength(50)]
[Required]
public string Name { get; set; }
public ICollection<Ticket> Tickets { get; set; }
}
I have the following linq statement:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => x.UrgencyId)
.Select(g => new {
id = g.Key,
count = g.Count(),
name = g.FirstOrDefault(u => u.UrgencyId == g.Key).TicketUrgency.Name
});
I want to Group Entities by UrgencyId and then return the Key (UrgencyId), and also count of the items in a single group and show the name of the Urgency.
When I run it, the query just gets hung up without any exceptions.
This should work, doing it the other way around, by retrieving all TicketUrgencies first and grouping it.
Entities.Include(e => e.Tickets)
.GroupBy(t => t.Id)
.Select(g => new {
id = g.Key,
name = g.FirstOrDefault().Name,
count = g.FirstOrDefault().Tickets.Count()
});
Very simple. Just try this :
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => new {UrgencyId = x.UrgencyId ,
Name = x.TicketUrgency.Name})
.Select(x=> new { UrgencyId = x.Key.UrgencyId,
Name = x.Key.Name,
Count = x.Count()});
Since you are grouping by UrgencyId, you know all members of g have the same id as the Key, so to pick up the name just pull the first one. You also know g isn't empty because that wouldn't make a group:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => x.UrgencyId)
.Select(g => new {
id = g.Key,
name = g.First().TicketUrgency.Name
count = g.Count(),
});
You could group by those two properties:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => new{ x.UrgencyId, x.TicketUrgency.Name })
.Select(g => new {
id = g.Key.UrgencyId,
count = g.Count(),
name = g.Key.Name
});
Another way could be, as #ASpirin suggested,starting the query from TickerUrgency:
var result= TicketUrgencies.Include(t=>t.Tickets)
.Where(t=>t.Tickets.Any())
.Select(t=> new {id=t.Id,name=t.Name, count= t.Tickets.Count()})

Order by array values in Linq to Entity Framework Query

I am trying to write an OrderBy clause in a Linq to EntityFramework query. My problem is that the entity table I am looking at stores an ID, that relates to a table in a different database and I cannot adjust the database.
MainDatabase.EntityToOrder
ID
Name
OtherID
SecondDatabase.OtherEntity
ID
Name
My C# EntityToOrder Model looks like this, and I need to be able to order by "OtherName"
EntityToOrder.cs
public class EntityToOrder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public long OtherId { get; set; }
public string OtherName { get; set; }
}
So, I would like to Order EntityToOrder by "OtherName" in the most efficient way possible. My existing query looks like this.
var entities = mainContext.EntityToOrder.OrderBy(e => e.Name).Skip(startIndex).Take(pageSize).ToList();
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
entities.ForEach(e => OtherName = otherNames[e.OtherID]);
How can I write the most efficient query to order by "OtherName", preferably avoiding selecting the whole EntityToOrder table into memory.
Update
For clarity, here is some code that achieves the OrderBy, but needs to retrieve the entire EntityToOrder table into memory. I was hoping this could be achieved in a more efficient way. Also, the OtherEntity can belong to many EntityToOrder rows.
var entities = mainContext.EntityToOrder.ToList();
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
entities.ForEach(e => OtherName = otherNames[e.OtherID]);
return entities.OrderBy(e => e.OtherName).Skip(startIndex).Take(pageSize).ToList();
Quite challenging task. I was thinking initially just to switch the roles and perform pagination (OrderBy/Skip/Take) on OtherEntity table, but unfortunately that doesn't work due to one to many relationship. So I ended up with doing some pre pagination in memory on OtherEntity. However, in order to do that I needed counts of the matching items in EnityToOrder, so this is retrieved with additional db query, which makes the solution involving 3 db queries and some memory processing. Here it is
var countByOtherId = db.EntityToOrder
.GroupBy(e => e.OtherId)
.Select(g => new { ID = g.Key, Count = g.Count() })
.ToDictionary(e => e.ID, e => e.Count);
var other = new Dictionary<long, string>();
int skipCount = startIndex, useCount = 0;
foreach (var e in db.OtherEntity.OrderBy(e => e.Name))
{
int count;
if (!countByOtherId.TryGetValue(e.ID, out count)) continue;
if (skipCount > 0 && other.Count == 0)
{
if (skipCount >= count) { skipCount -= count; continue; }
count -= skipCount;
}
other.Add(e.ID, e.Name);
if ((useCount += count) >= pageSize) break;
}
var entities = db.EntityToOrder
.Where(e => other.Keys.Contains(e.OtherId))
.AsEnumerable()
.Select(e => new EntityToOrder { ID = e.ID, Name = e.Name,
OtherId = e.OtherId, OtherName = other[e.OtherId] })
.OrderBy(e => e.OtherName).ThenBy(e => e.Name)
.Skip(skipCount).Take(pageSize)
.ToList();
Now, I'm not quite sure if that's better to what are you doing currently, but it's worth trying.
If you can change the model, then you might try the following:
public class EntityToOrder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public long OtherId { get; set; }
[ForeignKey("OtherId")]
public OtherEntity OtherEntity{ get; set; }
}
Then, you should be able to perform this query:
using System.Data.Entity;
var entities = mainContext
.EntityToOrder
.Include(x => x.OtherEntity)
.OrderBy(e => e.OtherEntity.Name)
.Skip(startIndex)
.Take(pageSize)
.ToList();
Edit :
Sorry, I missed the point that you had 2 databases....
I found an alternative which I thought I would post in case it is useful to anyone. I used a .Join() to merge the dictionary of OtherEntity into my query. This still selects into an IEnumerable so I don't think it is more efficient.
var entities = mainContext.EntityToOrder;
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
Func<EntityToOrder, KeyValuePair<long, string>, EntityToOrder> joinFunc = ((a, b) => {
a.OtherName= b.Value;
return a;
});
return entities.Join(otherNames, e => e.OtherID, oe => oe.Key, joinFunc)
.OrderBy(e => e.OtherName)
.Skip(startIndex)
.Take(pageSize)
.ToList();
Note on Includes
When applying Join you select into an IEnumerable and therefore lose the ability to access properties from a linked table. To counter this you would need to add a .Include() for any linked table you need to access before applying the .Join(). E.g.
var entities = mainContext.EntityToOrder
.Include("LinkedEntity");
return entities.Join(otherNames, e => e.OtherID, oe => oe.Key, joinFunc)
.OrderBy(e => e.OtherName)
.ThenBy(e => e.LinkedEntity.Name) //reference to linked table
.ToList();

Add GroupBy to Select

I have this query
[HttpGet]
public List<AttachedPhotosModel> GetReportAttachedPhotos(int reportId)
{
var photos = new ReportsRepository().GetInjuryPhotos(reportId);
return photos.Select(x => new AttachedPhotosModel()
{
Id = x.Id,
Type = x.InjuryType,
Photos = photos.Where(y => y.InjuryType == x.InjuryType).Select(z => z.ServicePhotoUrl).ToList()
}).ToList();
}
I need to GroupBy InjuryType, how to do this?
I added return photos.GroupBy(k => k.InjuryType).Select(x => new AttachedPhotosModel() but how to select model, x have new value key and I don't know how to select my data
This code should work. Assuming photos is collection of objects with InjuryType property and PhotoUrl property and AttachedPhotosModel has an InjuryType and Photos properties like this.
public class AttachedPhotosModel
{
public string InjuryType { set; get; }
public List<string> Photos { set; get; }
}
Code for grouping by InjurType.
var grouped = photos
.GroupBy(s => s.InjuryType,d => d.PhotoUrl, (k, g) => new
AttachedPhotosModel
{
InjuryType = k,
Photos = g.ToList()
}).ToList();

Getting properties from a child

I have the following entities:
public class Parent
{
int Id { get; set; }
string ParentName { get; set; }
List<Child> Children { get; set; }
}
public class Child
{
int Id { get; set; }
string ChildName { get; set; }
}
and the following dto:
public class ParentDTO
{
int Id { get; set; }
List<string> ChildrenNames { get; set; }
}
using QueryOver code below I can get the Parent values
ParentDTO result = null;
Parent parentAlias = null;
Child childAlias = null;
var query = session.QueryOver(() => parentAlias)
.JoinAlias(() => parentAlias.Children, () => childAlias, JoinType.LeftOuterJoin)
.SelectList(list => list.Select(c => c.Id).WithAlias(() => result.Id)
.Select(c => c.ParentName).WithAlias(() => result.Name)
//this part does not work
.Select(c => c.Children .Select(v => v.ChildName)).WithAlias(() => result.ChildrenNames)
//
)
.TransformUsing(Transformers.AliasToBean<ParentDTO>());
return query.List<ParentDTO>();
However I cant seem to be able to project the list of childName values into my ChildrenNames collection.
Any ideas?
As some guys said in comments, you need to do two queries. Using linq, you could try something like this:
// get the parent Ids
var parentIds = session.Query<Parent>().Select(c => c.Id).ToList();
// get the childNames
var childNames = session.Query<Child>()
.Where(x => parentIds.Contains(x.ParentId)) // get on the child from parents query
.Select(x => new {x.Name, x.ParentId}) // get only the properties you need
.ToList(); // list of anon objects
// loop in memory between parentIds filling the corresponding childNames
var result = parentIds.Select(parentId => new ParentDTO()
{
Id = parentId,
ChildrenNames = childNames.Where(x => x.ParentId == parentId).ToList()
}).ToList();
I am not sure if it works, but you could try this in a single query:
var query = from p in session.Query<Parent>()
let names = p.Children.Select(c => c.ChildName).ToList()
select new ParentDTO()
{
Id = o.Id,
ChildrenNames = names
};
return query.Tolist();
Obs: I did not test it.

Categories

Resources