I have this piece of code
var myList = (from p in db.Full
where ((p.date_reception > begin & p.date_reception < end & !p.mc_host_class.Contains("NULL")) &
(!strListe.Contains(p.mc_host_class)))
group p by p.mc_host_class into g
orderby g.Count() descending
select new
{
hostclassx = g.Key,
countx = g.Count()
}).Take(10).ToList();
HttpContext.Current.Session["allList"] = myList;
i want to get two type of values from my session variable , before using session variable i used to do
object[] ys = myList.Select(a => (object)a.countx.ToString()).ToArray();
List<String> xs = new List<string>();
foreach (var x in myList.Select(i => i.hostclassx))
{
xs.Add(x);
}
I want to get the same type of variables(xs and ys) from my session variable
You have stored an anonymous object inside the session. Anonymous objects are not intended to be leaving the boundaries of the current method. So start by defining a model:
public class MyModel
{
public string HostClass { get; set; }
public int Count { get; set; }
}
and then project your query into this object (bear in mind that you might need to adjust the type of the HostClass property according to your needs - I have defined it as a string but in your particular model it might be some other type, it's just not clear what types of objects are involved in your queries from the code you have pasted so far):
...
orderby g.Count() descending
select new MyModel
{
HostClass = g.Key,
Count = g.Count()
}).Take(10).ToList();
Alright, now you've got a List<MyObject> stored inside your Session["allList"]. So in order to retrieve this value somewhere else in your code it's just a matter of casting back to this same type:
var list = (List<MyModel>)HttpContext.Current.Session["allList"];
And as a side note you seem to be using a & operator instead of && in your where predicates, maybe you didn't exactly wanted to use this. You seem to be confusing the logical AND operator and the binary AND operator.
Related
I have a query which selects a list of my class. It looks like so:
IQueryable<ClaimsBySupplierAggregate> agg =
(from d in alliance.SupplierSearchByReviewPeriod
where d.ClientID == ClientID && ReviewPeriodIDs.Contains((int)d.ReviewPeriodID)
select new ClaimsBySupplierAggregate {
Amount = d.Amount,
StatusCategoryID = d.StatusCategoryID,
DeptName = d.DepartmentName,
APLReason = d.APLReason,
Area = d.AreaDesc,
StatusCategoryDesc = d.StatusCategoryDesc,
Agreed = d.Agreed
});
Later on in the application I select each variable and get the distinct values like this:
SupplierModel.APLReason = agg.Select(r => r.APLReason).Distinct().ToList();
SupplierModel.AreaDesc = agg.Select(r => r.Area).Distinct().ToList();
SupplierModel.DeptName = agg.Select(r => r.DeptName).Distinct().ToList();
SupplierModel.StatCatDes = aggg.Select(r => r.StatusCategoryDesc).Distinct().ToList();
Is there a way to do this in one LINQ statement?
You could using Aggregate, but you would need a complex object for the seed, which brings you to the same complexity of code. I think that for this particular case, using LInQ enters the Golden Hammer antipattern. Just use an old fashioned loop and four HashSets instead of Lists and you are done and the code is more readable and your intention clearer.
I think you can use this then customize the code follow your expectation.
Or you can use group by.
public class LinqTest
{
public int id { get; set; }
public string value { get; set; }
public override bool Equals(object obj)
{
LinqTest obj2 = obj as LinqTest;
if (obj2 == null) return false;
return id == obj2.id;
}
public override int GetHashCode()
{
return id;
}
}
List<LinqTest> uniqueIDs = myList.Distinct().ToList();
There is a contrived way to do this:
from d in alliance.SupplierSearchByReviewPeriod
where d.ClientID == ClientID && ReviewPeriodIDs.Contains((int)d.ReviewPeriodID)
group d by 0 into g
select new
{
APLReasons = g.Select(d => d.APLReason).Distinct(),
AreaDescs = g.Select(d => d.Area).Distinct(),
DeptNames = g.Select(d => d.DeptName).Distinct(),
StatusCategoryDescs = g.Select(d => d.StatusCategoryDesc).Distinct(),
}
The part group d by 0 into g creates one group of all items, from which you can subsequently query any aggregate you want.
BUT...
...this creates a very inefficient query with UNIONs and (of course) DISTINCTs. It's probably better (performance-wise) to simply get the flat data from the database and do the aggregations in subsequent code.
I've got a table Installation which can contains one or many Equipements.
And for functionnal reasons, I've overwritten my table Installation and added a field NbrEquipements.
I want to fill this field with Linq, but I'm stuck...
Due to special reasons, there is no relation between these to tables. So, no Installation.Equipements member into my class. Therefore, no Installation.Equipements.Count...
I'm trying some stuff. Here is my code:
var query = RepoInstallation.AsQueryable();
// Some filter
query = query.Where(i => i.City.RegionId == pRegionId));
int?[] etatIds = { 2, 3 };
query = (from i in query
select new Installation
{
NbrEquipements= (from e in RepoEquipement.AsQueryable()
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e.SasId
).Count()
});
But with this try, I got this error:
The entity or complex type 'myModel.Installation' cannot be constructed in a LINQ to Entities query
I've tried some other stuff but I'm always turning around...
Another thing that can be useful for me: It would be great to fill a field called Equipements which is a List<Equipement>.
After that, I would be able to Count this list...
Is it possible ?
Tell me if I'm not clear.
Thanks in advance.
Here is the final code:
//In the class:
[Dependency]
public MyEntities MyEntities { get; set; }
//My Methode code:
var query = MyEntities .SasInstallations.AsQueryable();
// Some filter
query = query.Where(i => i.City.RegionId == pRegionId));
var liste = new List<Installation>();
var queryWithListEquipements =
from i in query
select new
{
Ins = i,
EquipementsTemp = (from eq in MyEntities.Equipements.AsQueryable()
where eq.SpecialId == i.SpecialId
&& (etatIds.Contains(eq.SasEquEtat))
select eq
).ToList()
};
var listWithListEquipements = queryWithListEquipements.ToList();
foreach (var anonymousItem in listWithListEquipements)
{
var ins = anonymousItem.Ins;
ins.Equipements = anonymousItem.EquipementsTemp;
ins.NumberEquipements = ins.Equipements.Count();
liste.Add(ins);
}
return liste;
By the way, this is very very fast (even the listing of Equipements). So this is working exactly has I wished. Thanks again for your help everyone!
Use an anonymous type. EF does not like to instantiate entity classes inside a query.
var results = (from i in query
select new
{
NbrEquipements= (from e in RepoEquipement
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e.SasId
).Count()
})
.ToList();
Notice how I used select new instead of select new Installation.
You can then use the data inside the list (which is now in memory) to create instances of type Installation if you want like this:
var installations = results.Select(x =>
new Installation
{
NbrEquipements = x.NbrEquipements
}).ToList();
Here is how to obtain the list of equipment for each installation entity:
var results = (from i in query
select new
{
Installation = i,
Equipment = (from e in RepoEquipement
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e).ToList()
})
.ToList();
This will return a list of anonymous objects. Each object will contain a property called Installation and another property called Equipment (which is a list). You can easily convert this list (of anonymous objects) to another list of whatever type that you want.
I am having problem with converting the DateTime I am collecting from the database to localtime with LINQ. A LINQ query won't let me use ToLocalTime() and I can't seem to get any fix outside the query to work with the anonymous type of list.
Here is the LINQ query from the controller :
// GET: api/Scan
public object Getv_Update_ComplianceStatusAll()
{
var dato = DateTime.Now.AddYears(-1);
var scan = (from n in db.C_RES_COLL_NO9000AC
join s in db.v_Update_ComplianceStatusAll on n.MachineID equals s.ResourceID
join u in db.v_UpdateInfo on s.CI_ID equals u.CI_ID
join c in db.v_CICategoryInfo on u.CI_ID equals c.CI_ID
where (n.MachineID == s.ResourceID) && (u.DateRevised > dato)
group s by new { n.Name } into grp
select new
{
Name = grp.Key.Name,
StatusScan = grp.Max(t=> t.LastStatusCheckTime)
});
return scan;
}
This is my attemt at a fix outside the query :
var newScan = scan.ToList();
foreach (var s in newScan)
{
s.StatusScan = s.StatusScan.ToLocalTime();
}
return newScan;
The converstion works, but it returns "Error 306 Property or indexer 'AnonymousType#1.StatusScan' cannot be assigned to -- it is read only"
So, how do I convert the UTC to local time in the controller (before I return anything to the website)?
Yes, Anonymous Type is handy, but it is a bad idea to return it from a method -- Outsider do not know what actually the object is. It is recommended to create a strong type to store the result and return IEnumerable<ScanItem>. Then you are able to modify the result.
public class ScanItem
{
public string Name { get; set; }
public datetime StatusScan { get; set; }
}
If you must use Anonymous Type, You can build a new list like this
scan.ToList().Select(i => new {Name = i.Name, StatusScan = i.StatusScan.ToLocalTime()});
var newScan = scan.ToList();
foreach (var s in newScan)
{
s.StatusScan = s.StatusScan.ToLocalTime();
}
return newScan;
your newScan is a List, you cannot directly assign
s.StatusScan = s.StatusScan.ToLocalTime();
I'm trying to loop through an IEnumerable but for some reason it is not going through the foreach loop, as the values for each ChartClass was not modified.
public IEnumerable<ChartClass> get(string ID, string buildname, string placeholder)
{
var context = new Entities();
var metrics = from c in context.VSTS_CODE_METRICS
where c.BUILD_NAME == buildname && c.OBJECT_TYPE == "Namespace"
group c by c.BUILD_ID into g
select new ChartClass
{
Build_ID = g.Key,
BuildTrim = g.Key,
Index = g.Average(c => c.MAINTAINABILITYINDEX_).Value
};
foreach (var i in metrics)
{
int num = i.BuildTrim.LastIndexOf('_');
i.BuildTrim = "2";
}
return metrics;
}
I'm trying to change each ChartClass' BuildTrim field to "2" but it's not happening for some reason
Why don't you just set the BuildTrim equal to "2" in the query?
public IEnumerable<ChartClass> get(string ID, string buildname, string placeholder)
{
var context = new Entities();
var metrics = from c in context.VSTS_CODE_METRICS
where c.BUILD_NAME == buildname && c.OBJECT_TYPE == "Namespace"
group c by c.BUILD_ID into g
select new ChartClass
{
Build_ID = g.Key,
BuildTrim = "2",//g.Key,
Index = g.Average(c => c.MAINTAINABILITYINDEX_).Value
};
/*foreach (var i in metrics)
{
int num = i.BuildTrim.LastIndexOf('_');
i.BuildTrim = "2";
}*/
return metrics;
}
metrics is an IQueryable. Every single time you iterate the object it is going to to to the database, query the items that you're asking for, put them into the objects that you specified, and then allow those objects to be iterated. You're modifying the objects that are being returned, but those same in-memory objects won't be used if you iterate the sequence again. Instead it's going back to the database a second time and pulling back a fresh query that doesn't have your changes.
As mentioned in the other answer, if you simply modify your first Select call to set BuildTrim to the desired value to begin with, rather than modifying an object that is just about to be thrown away, your query will work as intended.
Very basic question. How do I do modify Linq results?
To expound further, I selected an order list from an order table in the database. I need to display the results in a gridview on a web form. First, I need to modify some of the results. For instance, the "Quote" field should be changed to blank when the "Order" field has a value. It is likely I might have to do more elaborate manipulation of the results. In the past, it was possible to loop through arrays, and modify them, but today's programming seems to not want loops happen. At the moment the results seem to be read-only, as if I am doing something wrong by needing to modify the list.
protected void fillGridView()
{
using (CqsDataDataContext cqsDC = new CqsDataDataContext())
{
var orderlist = from x in cqsDC.MasterQuoteRecs
where x.CustomerNumber == accountNumber && x.DateCreated > DateTime.Now.AddDays(howfar)
orderby x.DateCreated descending
select new
{
customer = x.customername,
order = x.OrderReserveNumber,
quote = x.Quote,
date = Convert.ToDateTime(x.DateCreated).ToShortDateString(),
project = x.ProjectName,
total = x.Cost,
status = x.QuoteStatus
};
// I would like to loop thru list and make changes to it here
GridView1.DataSource = orderlist;
GridView1.DataBind();
}
}
You end up with an IQueryable<anonymoustype> with your current query. Since they're anonymous types they're readonly and can't be changed anyway.
Your best option, especially if you intend to have more complex manipulations that can't be done in the query by the database, is to use a class instead. You'll also want to add a ToList() at the end of your query so you end up with a List<YourClass> and can then loop over it as you usually would and change the objects.
So make a class that has all your properties, for example MasterQuote, and use that in your query instead:
var query = from x in cqsDC.MasterQuoteRecs
where x.CustomerNumber == accountNumber && x.DateCreated > DateTime.Now.AddDays(howfar)
orderby x.DateCreated descending
select new MasterQuote
{
Customer = x.customername,
Order = x.OrderReserveNumber,
Quote = x.Quote,
Date = Convert.ToDateTime(x.DateCreated).ToShortDateString(),
Project = x.ProjectName,
Total = x.Cost,
Status = x.QuoteStatus
};
var orderList = query.ToList();
foreach (var item in orderList)
{
if (item.OrderReserveNumber > 0)
{
item.Quote = "";
}
}
Your MasterQuote class would look something like:
public class MasterQuote
{
public string Customer { get; set; }
public int Order { get; set; }
// and so on
}
Of course for your given example you could probably accomplish the Quote manipulation in your query as seth mentioned.
Just use a ternary operator.
select new
{
customer = x.customername,
order = x.OrderReserveNumber,
quote = x.OrderReserveNumber != null ? string.Empty : x.Quote,
date = Convert.ToDateTime(x.DateCreated).ToShortDateString(),
project = x.ProjectName,
total = x.Cost,
status = x.QuoteStatus
};
Depends on how complex the changes are really, but the one you mentioned could be done with a simple method on a static class.
Of you could just chain linq statements together, and do the equivalent of sql case for the quote column.
It seems unlikely that OrderList doesn't implement IEnumerable so if all the linq gets too messy foreach will do the job.