return custom json from LINQ select statement - asp.net-mvc

A very new programmer to MVC, JSON & LINQ - I have created an ActionResult that returns a JSONResult:
var formhistory = from p in _formsRepository.ReturnedForms
where p.DateAdded >= DateTime.Now.Date.AddDays(-15) && p.DateAdded <= DateTime.Now.Date
group p by new {p.Centre, p.Form, p.DateAdded}
into g
select new {
g.Key.Centre,
g.Key.Form,
g.Key.DateAdded,
Total = g.Sum(p => p.Quantity)
};
return Json(formhistory, JsonRequestBehavior.AllowGet);
This gives me a nice JSON result set as follows:
[
{"Centre":"Centre1","Form":"Advice","DateAdded":"\/Date(1331856000000)\/","Total":1067},
{"Centre":"Centre1","Form":"Advice","DateAdded":"\/Date(1332460800000)\/","Total":808},
{"Centre":"Centre1","Form":"Advice","DateAdded":"\/Date(1333062000000)\/","Total":559},
{"Centre":"Centre1","Form":"Advice","DateAdded":"\/Date(1333666800000)\/","Total":1448}
]
My question is this: I'm trying to manipulate this JSON string so that instead of 2 key/value pairs for "Form" and "Total" I only have 1, i.e. "Form":"Total".
I realise this is probably a very basic question, but can anyone point me in the correct direction? (Apart from the door!)

select new {
g.Key.Centre,
//g.Key.Form,
g.Key.DateAdded,
Form = g.Sum(p => p.Quantity)
}
would give you a key "Form" whose value is the former "Total". Is that what you want?

Modify the select part in your linq query
select new {
g.Key.Centre,
g.Key.DateAdded,
NewField = String.Format("{0} - {1}",g.Key.Form,g.Sum(p => p.Quantity).ToString())
};
I think this will solve your purpose.

Related

Cannot compare in linq

I am trying to get the result of the user logged in but receiving this error :
"Cannot compare elements of type 'System.Linq.IQueryable`1'. Only
primitive types, enumeration types and entity types are supported. "
Here is the query I'm applying in my index action:
var viewModel = new PointsViewModel();
viewModel.Point = db.Point.ToList();
viewModel.Redeem = db.Redeem.ToList();
TempData["UserPoints"] = null;
var usrname = (from a in db.Instructors
where a.Email == User.Identity.Name
select new { a.PersonID });
if (usrname.Count().Equals(0))
{
TempData["UserPoints"] = "You have not earn any points yet.";
return View();
}
viewModel.instructor = db.Instructors
.Where(i => i.PersonID.Equals(usrname))// if I directly insert id here then it works properly but I don't want direct inserts
.Single();
PopulateAssignedPointData(viewModel.instructor);
return View(viewModel);
Please help me with this please...I am unable to find any solution on google
It's because you're passing usrname as a parameter to another query. usrname is a query, not a value, so you need to retrieve a value from the query (in this case, by using First(), but you could just as easily use Single() if you like) before you can use it as a parameter in another query. I would recommend the following changes:
if (!usrname.Any())
{
TempData["UserPoints"] = "You have not earn any points yet.";
return View();
}
var personId = usrname.First();
viewModel.instructor = db.Instructors
.Where(i => i.PersonID.Equals(personId))
.Single();
I also changed usrname.Count().Equals(0) to !usrname.Any() as it is more idiomatic (it will use the exists keyword in SQL, rather than count)
Try to use this:
viewModel.instructor = db.Instructors
.Where(i => usrname.Any(u => u.PersonID == i.PersonID))
.Single();

Need help to build an EF query with LEFT JOIN

I can't find the correct way to build an EF (4.1) query that will return the same result as this SQL containing a LEFT JOIN:
SELECT
s.id_service,
s.description,
x.id_service as isDisponible
FROM
role.service_disponible s
LEFT JOIN
role.service_disponible_x_ue x
ON s.id_service = x.id_service AND x.id_ue = 1 and flg_actif = '1'
In fact I'm just trying to obtain the complete list of services disponible (ServiceDisponible) adding a field that tell me if service is disponible for a specific entity (filtered with the id_ue) which information come from a many to many related table (ServiceDisponibleXUe).
My model is:
Ideally, I would like this query to return this viewModel object what is basically my serviceDisponible domain with one more field indicating the disponibility of the service.
public ServiceDisponibleViewModel(ServiceDisponible ServiceDisponible, bool isDisponible)
{
this.serviceDisponible = serviceDisponible;
this.isDisponible = isDisponible;
}
What I have so far is this query but the syntax is invalid:
services = context.ServiceDisponible
.Select(a => new ServiceDisponibleViewModel
{
c => new ServiceDisponible
{
id_service = a.id_service,
description = a.description
},
isDisponible = a.ServiceDisponibleXUe
.Any(b => b.flg_actif && b.id_ue == idUe)
}).ToList();
Try this:
ServiceDisponibleViewModel services =
from sd in context.ServiceDisponible
from sdx in context.ServiceDisponibleXUe
.Where(x => x.id_ue == 1 && flg_actif == '1' && x.id_service == sd.id_service)
.DefaultIfEmpty()
select new ServiceDisponibleViewModel(
new ServiceDisponible
{
id_service = sd.id_service,
description = sd.description
},
sdx.id_service
);
Having SQL as example often makes one jump to a join in linq. But using navigation properties produces much more succinct syntax:
from sd in context.ServiceDisponible
from sdx in sd.ServiceDisponibleXUes.Where(x => x.id_ue == 1
&& x.flg_actif == "1")
.DefaultIfEmpty()
select new
{ sd.id_service,
sd.description,
isDisponible = sdx.id_service
};
(I couldn't help using the plural form of ServiceDisponibleXUe which imo is more clear).

How do I use 2 include statements in a single MVC EF query?

I am trying to write a query that includes 2 joins.
1 StoryTemplate can have multiple Stories
1 Story can have multiple StoryDrafts
I am starting the query on the StoryDrafts object because that is where it's linked to the UserId.
I don't have a reference from the StoryDrafts object directly to the StoryTemplates object. How would I build this query properly?
public JsonResult Index(int userId)
{
return Json(
db.StoryDrafts
.Include("Story")
.Include("StoryTemplate")
.Where(d => d.UserId == userId)
,JsonRequestBehavior.AllowGet);
}
Thank you for any help.
Try to flatten your hierarchy if it works for you. Here is a sample, and you may want to customize it for your needs.
var result = from c in db.Customers
join o in db.Orders
on c equals o.Customers
select new
{
custid = c.CustomerID,
cname = c.CompanyName,
address = c.Address,
orderid = o.OrderID,
freight = o.Freight,
orderdate = o.OrderDate
};
If flattering does not meet your requirements then you need to use query that returns a Nested Group. Finally, look at the following link for more references - LINQ Query Expressions .

Getting a list of distinct entities projected into a new type with extra field for the count

I'm designing an interface where the user can join a publicaiton to a keyword, and when they do, I want to suggest other keywords that commonly occur in tandem with the selected keyword. The trick is getting the frequency of correlation alongside the properties of the suggested keywords.
The Keyword type (EF) has these fields:
int Id
string Text
string UrlString
...and a many-to-many relation to a Publications entity-set.
I'm almost there. With :
var overlappedKeywords =
selectedKeyword.Publications.SelectMany(p => p.Keywords).ToList();
Here I get something very useful: a flattened list of keywords, each duplicated in the list however many times it appears in tandem with selectedKeyword.
The remaining Challenge:
So I want to get a count of the number of times each keyword appears in this list, and project the distinct keyword entities onto a new type, called KeywordCounts, having the same fields as Keyword but with one extra field: int PublicationsCount, into which I will populate the count of each Keyword within overlappedKeywords. How can I do this??
So far I've tried 2 approaches:
var keywordCounts = overlappingKeywords
.Select(oc => new KeywordCount
{
KeywordId = oc.Id,
Text = oc.Text,
UrlString = oc.UrlString,
PublicationsCount = overlappingKeywords.Count(ok2 => ok2.Id == oc.Id)
})
.Distinct();
...PublicationsCount is getting populated correctly, but Distinct isn't working here. (must I create an EqualityComarer for this? Why doesn't the default EqualityComarer work?)
var keywordCounts = overlappingKeywords
.GroupBy(o => o.Id)
.Select(c => new KeywordCount
{
Id = ???
Text = ???
UrlString = ???
PublicationsCount = ???
})
I'm not very clear on GroupBy. I don't seem to have any access to 'o' in the Select, and c isn't comping up with any properties of Keyword
UPDATE
My first approach would work with a simple EqualityComparer passed into .Distinct() :
class KeywordEqualityComparer : IEqualityComparer<KeywordCount>
{
public bool Equals(KeywordCount k1, KeywordCount k2)
{
return k1.KeywordId== k2.KeywordId;
}
public int GetHashCode(KeywordCount k)
{
return k.KeywordId.GetHashCode();
}
}
...but Slauma's answer is preferable (and accepted) because it does not require this. I'm still stumped as to what the default EqualityComparer would be for an EF entity instance -- wouldn't it just compare based on primary ids, as I did above here?
You second try is the better approach. I think the complete code would be:
var keywordCounts = overlappingKeywords
.GroupBy(o => o.Id)
.Select(c => new KeywordCount
{
Id = c.Key,
Text = c.Select(x => x.Text).FirstOrDefault(),
UrlString = c.Select(x => x.UrlString).FirstOrDefault(),
PublicationsCount = c.Count()
})
.ToList();
This is LINQ to Objects, I guess, because there doesn't seem to be a EF context involved but an object overlappingKeywords, so the grouping happens in memory, not in the database.

How to specify a condition in an Entity Framework join?

I have a Blogs table related to BlogComments table with a FK.
I need to get, through Linq, all the BlogComments items that match a certain flag
If i do:
db.Blogs.Where(b => b.BlogComments.Where(bc=>bc.Where(bc.Flag1==true));
I get "Cannot implicity convert type IEnumerable to bool"
Which is the best way to solve this problem?
Because this expression:
b.BlogComments.Where(...)
returns an IEnumerable (of BlogComments), but you are then passing it into this method:
db.Blogs.Where(...)
which expects a function that returns a bool, not an IEnumerable.
You probably need something like this:
var blogId = 5;
db.BlogComments.Where(bc => bc.BlogId == blogId && bc.Flag1 == true)
If you need to select comments from multiple blogs, then you could try using Contains:
var blogIds = new [] {1,2,3,4,5};
db.BlogComments.Where(bc => blogIds.Contains(bc.BlogId) && bc.Flag1 == true)
If you want to place criteria on the set of blogs, as well as the comments, then you could do this in one query using a join:
var query = from b in db.Blogs
join c in db.BlogComments on c.Blog equals b
where b.SomeField == "some value"
&& c.Flag1 == true
select c;
You could write it in LINQ form.
var blogs = from b in db.Blogs
join c in db.BlogComments
on b.BlogId equals c.BlogId
where c.Flag1
select b;
If you have a composite key you can write
on new { A = b.BlogKey1, B = b.BlogKey2 }
equals new { A = c.CommentKey1, B = c.CommentKey2 }
If it were me, I would just have another DbSet in your DbContext.
DbSet<BlogComment> BlogComments
and just search through there without going through Blogs.
db.BlogComments.Where(bc => bc.Flag1 == true);
If anyone knows if there's anything wrong in doing so, then I'm all ears :)

Resources