I'm newbie to c# and i have problem trying to access IList after i assign it to the query. Here is my code:
System.Collections.IList Invoices =
(from p in entities.InvoiceCards
where (p.CustomerCard.ID == CustomerID)
select new
{
InvoiceID = p.ID,
InvoiceDatetime = p.DateTime,
InvoiceTotal = (decimal) p.InvoiceTotal,
}).ToList();
// update the grid
invoiceCardDataGridView.DataSource = Invoices;
----------- Here the compiler is complaining about object c? how can I access the objects in the IList without executing the query again? I need to use IList to use as a datasource. what is a better way? Please include the code
foreach (var c in Invoices)
InvoiceTotal += (decimal)c.InvoiceTotal;
Your problem that you are using anonymous type in your query.
So when you get an IList of this anonymous type and assign to a datasource, by default you will loose its type.
When you want to retrieve it from the DataSource in another part of your code you have to cast it with the appropriate type. Since the anonymous type is generated by the compiler you will not be able to cast it.
A solution is to create the class that contain the type if doesn't exist already.
public class InvoicePart
{
public int InvoiceID {get; set}
public DateTime InvoiceDatetime {get; set}
public decimal InvoiceTotal {get; set}
}
Now you can modify your query to get a typed List
List<InvoicePart> Invoices =
(from p in entities.InvoiceCards
where (p.CustomerCard.ID == CustomerID)
select new InvoicePart
{
InvoiceID = p.ID,
InvoiceDatetime = p.DateTime,
InvoiceTotal = (decimal) p.InvoiceTotal,
}).ToList();
// update the grid
invoiceCardDataGridView.DataSource = Invoices;
and when you will get your data you will cast it to a List
List<InvoicePart> Invoices = (List<InvoicePart>)invoiceCardDataGridView.DataSource;
foreach (InvoicePart c in Invoices)
{
invoiceTotal += c.InvoiceTotal;
}
If the list contains anonymous types and the foreach loop is in another method than the first block of code you cannot use it that way.
Please have a look to this post that maybe could help in your case.
If you absolutely have to use IList then you are better of defining an explicit type rather than using an anonymous type. Then you'll have to cast the elements of the IList to your explicit type when you need to work with them.
Zied has the right idea about solving this issue. Note, however, that binding to a List<T> is not bidierctional (changes to the list will not reflect in the grid). For that you need to use a BindingSource:
List<InvoicePart> Invoices =
(from p in entities.InvoiceCards
where (p.CustomerCard.ID == CustomerID)
select ...
// update the grid
var bs = new BindingSource();
bs.DataSource = Invoices;
invoiceCardDataGridView.DataSource = bs;
Related
I have two collections returned from Dapper Query
Table1_Collection = Query<dynamic>("SELECT * FROM Table1"); // I can make Select T1, *
Table2_Collection = Query<dynamic>("SELECT * FROM Table2"); // I can make Select T1, *
See comment above I can just fix it with adding Select T1 with query. but how to do that in .NET Collections.
Now I need to merge both Collections:
List<dynamic>CombinedCollection = new List<dynamic>();
CombinedCollection.AddRange(Table1_Collection);
CombinedCollection.AddRange(Table2_Collection);
Above collection contains columns in both table1 and table2. I need to add a field to that collection in .NET to specify which table that row belongs.
Something Like:
CombinedCollections.AddRange(Table1_Collections).CombineWith("T1");
CombinedCollections.AddRange(Table2_Collections).CombineWith("T2");
Note that for DataTables, It have Columns.Add(DataColumn) with default
value. But in Dapper its only .NET Collections...
Adding a new property is very straight forward when we are using dynamics and ExpandoObject.
To simplify the process, let's add a function to create and return ExpandoObject with the added property
public ExpandoObject GetDynamic(dynamic obj, string tableName)
{
var exp = new ExpandoObject();
var objnew = (IDictionary<String, Object>)obj;
foreach (var item in objnew)
{
exp.TryAdd(item.Key, item.Value);
}
exp.TryAdd("Table", tableName);
return exp;
}
Then try this:
CombinedCollections.AddRange(Table1_Collections.Select(t => GetDynamic(t, "T1")));
CombinedCollections.AddRange(Table2_Collections.Select(t => GetDynamic(t, "T2")));
However, you can also do something like this without using the ExpandoObject for even cleaner segregation:
CombinedCollections.AddRange(Table1_Collections.Select(t => new { t, Table = "T1" }));
CombinedCollections.AddRange(Table2_Collections.Select(t => new { t, Table = "T2" }));
This will give you a collection of elements having 2 properties: The row and the Table
If you trying to add a default value you could do something like the below
orderDetails = connection.Query("SELECT * FROM Table1").Select(x =>
{
dynamic y = x;
y.table= "T1";
return y;
}).ToList();
However if you want a default value already instantiated it would be much easyier to change the query like you have mentioned or have a class with a default value so that instead of getting a dynamic back you get an instance of your class.
Edit:
If you really want the object bound / converted to a data-grid, then you would be better off following the expandoObject example that Anup Sharma, mentioned.
The below questions are related to this:
How can I use a List<Dynamic> as with DataGridView.DataSource?
Binding a GridView to a Dynamic or ExpandoObject object
I'm looking for a way to do a GroupBy on a complex object, instead of just one property. The trouble is that I want to do this on an IQueryable, because getting all the data from the table is a really bad idea in this case.
We're using Entity Framework 6.1.
The class looks like this:
public class Pin {
public Guid Id {get;set;}
public Guid PageId {get;set;} /* this is the foreign key to our Pages-table */
public PageClass Page {get;set;} /* this is a relation */
}
I need to report on the times a certain page has been "pinned", printing the name of the page as well.
Right now my code looks like this:
var pinnedPages = GetAll().GroupBy(x => x, comparer);
foreach (var pinnedPage in pinnedPages)
{
var numberOfTimesPinned = pinnedPage.Count();
var pin = pinnedPage.Key;
//write a line to the report
}
But if I group on PageId, the pinnedPage.Key returns a Guid, obviously, while I need the whole Page object for my reporting needs.
I have tried implementing a custom comparer as well, but this cannot be translated to SQL, obviously which is why this doesn't work either.
GetAll().GroupBy(x => x.pageId).Select(_ => new {key = _.Key, page = _.FirstOrDefault().Page, count = _.Count()});
This will group by on the pageId, however the select will create a new anonymous object which will contain the key (pageId) and select the first PageClass object
You don't need any grouping if you query the pages directly and use a navigation property that I assume exist (or else should be added):
var pinnedPages = context.Pages
.Select(p => new
{
Page = p
Pins = p.Pins.Count()
});
foreach (var pinnedPage in pinnedPages)
{
var numberOfTimesPinned = pinnedPage.Pins;
var pin = pinnedPage.Page;
//write a line to the report
}
I use context.Pages because the source of the statement should be IQueryable. GetAll returns IEnumerable (apparently, otherwise the GroupBy overload with a comparer wouldn't work).
I have two enumerables made up of different types.
public Contact
{
public string fullName;
}
public Property
{
public string Rep;
public string PropMgr;
}
I'm trying to get all "Contacts" who are represented in either the Rep or PropMgr fields.
My intuition says join on rep for one result, then join on PropMgr for another result set. Then join the result sets and select distinct. Not sure if that will even work and if it will seems like there is a more efficient way.
Adding some additional information:
Some data would be
Contact
FullName: Nick
Property
Name: "Happy Place"
PropMgr: Nick
Rep: Sally
When comparing the two sets and I get to this combination, I want to select the contact "Nick".
Keeping in mind I have IEnumerable ContactsList and IEnumerable PropertyList
I don't have any data to test it, but I guess something like this should work:
IEnumerable<Contact> contacts = ...
IEnumerable<Property> properties = ...
var query = from property in properties
from name in new[] { property.Rep, property.PropMgr }
join contact in contacts on name equals contact.FullName
select contact;
var result = query.Distinct().ToList();
Try following Linq Query
Contacts.Select(x=>x.fullName).Intersect(Properties.Select (x=>x.Rep).Union(Properties.Select(x=>x.PropMgr))
// contacts -> IEnumerable of Contact, Properties -> IEnumerable of Property
So many different ways to solve the same problem...
Something a little lower-tech to try:
var namesInProperties = new HashSet<string>();
foreach (Property p in PropertyList)
{
namesInProperties.Add(p.PropMgr);
namesInProperties.Add(p.Rep);
}
IEnumerable<Contact> matchingContacts = ContactsList.Where(c => namesInProperties.Contains(c.fullName));
i asked this a few weeks ago, but couldnt get any of the suggested answers working, so i would be grateful for any help on this:
i have a list of event Ids returned from an xml document as shown below
public IEnumerable<EventFeed> GetEventIdsByEventDate(DateTime eventDate)
{
return (from feed in xmlDoc.Descendants("Show")
from ev in feed.Elements("Event")
where Convert.ToDateTime(ev.Attribute("Date").Value).ToShortDateString() == eventDate.ToShortDateString()
select new EventFeed()
{
EventShowCode = feed.Attribute("Code").Value
}).ToList();
}
i now need to query my database to match events that equal the eventIds returned from the above method. so i would have something like:
select * from eventsdb where eventId in GetEventIdsByEventDate()
how can i do this using LINQ
thanks
kb
Hi Prutswonder, ive created the method below based on your suggestion
public IEnumerable<EventFeed> foo(DateTime str)
{
var foo = from f in GetAllEventsFromDatabase().ToList()
where GetAllEventsByDate(str).Contains(f.EventShowCode)
select e;
return (IEnumerable<EventFeed>) foo;
}
but on compile i get the following error
Error 7 The type arguments for method 'System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource>, TSource)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
GetAllEventsFromDatabase:
public IEnumerable<EventFeed> GetAllEventsFromDatabase()
{
var allEvents = from eventsList in GetEventsList()
select new EventFeed()
{
EventName = eventsList.Title,
EventSummary = eventsList.Introduction,
EventShowCode = eventsList.EventId,
EventImageSmall = eventsList.EventImageThumbUrl,
EventUrl = eventsList.Url,
EventSortBy = eventsList.SortOrder
};
return allEvents.OrderBy(x => x.EventSortBy);
}
The GetEventIdsByEventDate() method should return an IEnumerable of strings, containing the Event Ids (like the method name implies):
public IEnumerable<string> GetEventIdsByEventDate(DateTime eventDate)
{
return (from feed in xmlDoc.Descendants("Show")
from ev in feed.Elements("Event")
where Convert.ToDateTime(ev.Attribute("Date").Value).ToShortDateString() == eventDate.ToShortDateString()
select feed.Attribute("Code").Value
).ToList();
}
Also, don't forget to rename the foo() method to a more suitable name (for example GetEventsByEventDate())
About your error:
GetAllEventsByDate returns an IEnumerable containing EventFeed objects, so when you use the "Contains" method, it expects an "EventFeed" object to compare to the objects in the list. Instead, you are passing it an f.EventShowCode, which I assume is an integer or something:
EventShowCode = eventsList.EventId
I believe what you're looking for is this:
public IEnumerable<EventFeed> foo(DateTime str)
{
var foo = from f in GetAllEventsFromDatabase()
where GetAllEventsByDate(str).Contains(f)
select f;
return foo;
}
Download LINQPad. It's free but the upgraded version provides Intellisense support. This app has helped me figure out some pretty complicated LINQ queries.
I've got 2 lists of the same object. Now the first list is filled with data from a database table. the other list is filled with data downloaded a server.
Example:
public class HistoricData {
public int Id{get;set;}
public DateTime Date {get;set;}
public string Name {get;set;}
public float Impressions {get;set;}
}
So in my object I have the Id property that is unique. Now I need to check if I have objects in my second list that aren't in my first list.
I thought that I could do it in Linq, but I'm a little stuck.
var difference = from objHD in objHistoricData
join objHDN in objHistoricDataNew on objHD.Id equals objHDN.Id
select new {objHDNA = objHDN};
This always returns 0.
var difference = objHistoricDataNew.Except(objHistoricDataNew, new HistoricDataComparer());
(where HistoricDataComparer is a IEqualityComparer<HistoricData>)
use the Except function from LINQ, ans pass the IEqualityComparer that compare the ID.
var difference = a.Except( b, new YouEqualitityComparer() )
You need to override Equals and GetHashcode so that your types are compared by their values rather then by their references.
Try
var difference = list1.Except(list2);
But I think you'll need your own GetHashCode and Equals methods.
I assume that you would like to select all items in objHistoricDataNew that are different, than stored in objHistoricData?
Then maybe something like that:
var difference = objhistoricDataNew.Where(objHDN => objHistoricData.Select(objHD => objHD.Id).IndexOf(objHDN.Id) == -1)