Why isn't this Linq query on Dictionary<TKey, TValue> working as DataSource - vb.net

I have the following in VB:
Dim sources = From source In importSources Select New With _
{.Type = source.Key, .Source = source.Value.Name}
dgridSourceFiles.DataSource = sources
When I debug, sources shows an in-memory query and has 2 records within. Yet the datagrid view will not show the records.
So why won't this work? suggestions can be either VB or C#...
Update
When I use:
Dim sources = (From source In importSources Select New With _
{.Type = source.Key, .Source = source.Value.Name}).ToList()
...the datasource is displayed.

Your LINQ query is lazily evaluated and implements the IEnumerable<T> interface only (as far as I know), which means its results are not established until an enumerator calls MoveNext somewhere (as happens within a foreach loop, for example).
It seems the DataSource property does not enumerate its contents in this way. It's completely expecting an implementation of IList (or one of a few other interfaces—see below) so that it can access items by index. This is used internally by the control for sorting, filtering, etc. With this in mind, it's likely that all setting the DataSource property does is check the object's type to see whether it implements any of the supported interfaces. So I don't think the DataSource property is designed to deal with this type of object (a lazily evaluated query) at all.
Now, that ToList call populates a List<T> with the results of your query; this does implement IList and can therefore be used as the DataSource.
My understanding is that the reason DataSource is typed merely as object is that it expects any of the following interfaces:
IList
IListSource (in which case the IListSource.GetList method is used together with the DataMember property to provide data to the control)
IBindingList (which propagates changes in the list to the control for UI updates)
IBindingListView (like BindingSource)
This is according to the MSDN documentation.

You may need to call DataBind after setting the source. Try:
dgridSourceFiles.DataSource = sources
dgridSourceFiles.DataBind()

Related

Create a "clone" of this object, not point to it

Let's say I got a list called
myFirstList
And then I want to create a copy of that list so I can do some tweaks of my own. So I do this:
mySecondList = myFirstList
mySecondList.doTweaks
But I noticed that the tweaks also affect the myFirstList object! I only want the tweaks to affect the second one...
And afterwards I will want to completely delete mySecondList, so I do mySecondList = Nothing and I'm good, right?
Adam Rackis, I don't like your "Of course it does", because it is not at all obvious.
If you have a string variable that you assign to another string variabe, you do not change them both when making changes to one of them. They do not point to the same physical piece of memory, so why is it obvious that classes do?
Also, the thing is not even consistent. In the following case, you will have all elements in the array pointing at the same object (they all end up with the variable Number set to 10:
SourceObject = New SomeClass
For i = 1 To 10
SourceObject.Number = i
ObjectArray.Add = SourceObject
Next i
BUT, the following will give you 10 different instances:
For i = 1 To 10
SourceObject = New SomeClass
SourceObject.Number = i
ObjectArray.Add = SourceObject
Next i
Apparently the scope of the object makes a difference, so it is not at all obvious what happens.
Here is how you do it:
'copy one object to another via reflection properties
For Each p As System.Reflection.PropertyInfo In originalobject.GetType().GetProperties()
If p.CanRead Then
clone.GetType().GetProperty(p.Name).SetValue(clone, p.GetValue(OriginalObject, Nothing))
End If
Next
in some cases when the clone object got read-only properties you need to check that first.
For Each p As System.Reflection.PropertyInfo In originalobject.GetType().GetProperties()
If p.CanRead AndAlso clone.GetType().GetProperty(p.Name).CanWrite Then
clone.GetType().GetProperty(p.Name).SetValue(clone, p.GetValue(OriginalObject, Nothing))
End If
Next
Since you have not divulged the type of item that you are storing n your list, I assume it's something that's implementing IClonable (Otherwise, if you can, implement IClonable, or figure out a way to clone individual item in the list).
Try something like this
mySecondList = myFirstList.[Select](Function(i) i.Clone()).ToList()
But I noticed that the tweaks also
affect the myFirstList object! I only
want the tweaks to affect the second
one...
Of course it does. Both variables are pointing to the same object in memory. Anything you do to the one, happens to the other.
You're going to need to do either a deep clone, or a shallow one, depending on your requirements. This article should give you a better idea what you need to do
Expanding on Adam Rackies' answer I was able to implement the following code using VB.NET.
My goal was to copy a list of objects that served mainly as data transfer objects (i.e. database data). The first the class dtoNamedClass is defined and ShallowCopy method is added. A new variable named dtoNamedClassCloneVar is created and a LINQ select query is used to copy the object variable dtoNamedClassVar.
I was able to make changes to dtoNamedClassCloneVar without affecting dtoNamedClassVar.
Public Class dtoNamedClass
... Custom dto Property Definitions
Public Function ShallowCopy() As dtoNamedClass
Return DirectCast(Me.MemberwiseClone(), dtoNamedClass)
End Function
End Class
Dim dtoNamedClassVar As List(Of dtoNamedClass) = {get your database data}
Dim dtoNamedClassCloneVar =
(From d In Me.dtoNamedClass
Where {add clause if necessary}
Select d.ShallowCopy()).ToList
Here's an additional approach that some may prefer since System.Reflection can be slow.
You'll need to add the Newtonsoft.Json NuGet package to your solution, then:
Imports Newtonsoft.Json
And given a class type of MyClass, cloning can be as easy as:
Dim original as New MyClass
'populate properties of original...
Dim copy as New MyClass
copy = JsonConvert.DeserializeObject(Of MyClass)(JsonConvert.SerializeObject(original))
So the approach is to first use the JSON converter to serialize the original object, and than take that serialized data and deserialize it - specifying the class type - into the class instance copy.
The JSON converters are extremely powerful and flexible; you can do all sorts of custom property mappings and manipulations if you need something the basic approach above doesn't seem to address.
this works for me:
mySecondList = myFirstList.ToList
clone is the object you are attempting to clone to.
dim clone as new YourObjectType
You declare it like that.

Return object as parameter for nHibernate entity

I'm writing a solution where the data entities are passed to clients using datasets through a WCF service, with nHibernate as ORM.
My predecessor has written some translator classes, converting datasets to entities and vice versa. In most cases he's declared the return object as a parameter for the object.
For example:
Public Shared Function CreateEntity(ByVal ds As DataSetObject, ByVal entity As EntityObject) As EntityObject
Dim row As ds.EntityObjectRow = ds.EntityObject(0)
entity.Id = row.Id
// Etc.
Return entity
End Function
While I want to do it like:
Public Shared Function CreateEntity(ByVal ds As DataSetObject) As EntityObject
Dim row As ds.EntityObjectRow = ds.EntityObject(0)
Dim entity As New EntityObject
entity.Id = row.Id
// Etc.
Return entity
End Function
He's not with the company anymore, so I can't ask him why he's done it this way. Hence my question here. Is there some performance gain, or traceablity thing with nHibernate by using the first implementation rather than the latter?
When at the university I was always told not to pass the return object to the method, unless there was a very specific reason for it.
Please advice. :)
From the information you gave there is no problem to create the object to return from inside the method and not receive it from outside.
The only reason I can see for this is that maybe he passes the entity as a parameter with its ID already defined and doesn't change it inside, because the dataset could not know the entity's ID. But I guess that's not the case. So in my opinion go ahead and do the way you propose.

Generic Linq query on a DataGridView Datasource

I would like to implement a generic Linq query on a DataGridView.DataSource to retreive a row whith an idField. I did search MSDN, StackOverflow, etc... but didn't quite find what I'm looking for. Does anyone have an idea on how I could implement it ?
This is the non-generic code, but I would like to make it work whatever the type of the DataSource is (as long as it implements IEnumerable I guess) and the name and type of the key field.
Dim query = (From note In notesList _
Where note.IdNote = mIdNoteSelectionne _
Select note).FirstOrDefault()
Dim ancienIndex As Integer = notesList.IndexOf(query)
noteList is a List(Of Note), Note is a simple entity class I created, with just members and properties
This is in VB, but feel free to send some C# code, I'll translate it.
Unfortunately you won't be able to do this for any datasource. Since you are trying to compare to an ID field, something that not every object has, you won't be able to. What you need to do, is define a base class or an interface that has an ID property on it. Then you can cast the datasource as an IEnumerable of your base class. using linqs Cast<> operator.

.NET - Is there a way to programmatically fill all tables in a strongly-typed dataset?

I have a SQL Server database for which I have created a strongly-typed DataSet (using the DataSet Designer in Visual Studio 2008), so all the adapters and select commands and whatnot were created for me by the wizard.
It's a small database with largely static data, so I would like to pull the contents of this DB in its entirety into my application at startup, and then grab individual pieces of data as needed using LINQ. Rather than hard-code each adapter Fill call, I would like to see if there is a way to automate this (possibly via Reflection).
So, instead of:
Dim _ds As New dsTest
dsTestTableAdapters.Table1TableAdapter.Fill(_ds.Table1)
dsTestTableAdapters.Table2TableAdapter.Fill(_ds.Table2)
<etc etc etc>
I would prefer to do something like:
Dim _ds As New dsTest
For Each tableName As String In _ds.Tables
Dim adapter as Object = <routine to grab adapter associated with the table>
adapter.Fill(tableName)
Next
Is that even remotely doable? I have done a fair amount of searching, and I wouldn't think this would be an uncommon request, but I must be either asking the wrong question, or I'm just weird to want to do this.
I will admit that I usually prefer to use unbound controls and not go with strongly-typed datasets (I prefer to write SQL directly), but my company wants to go this route, so I'm researching it. I think the idea is that as tables are added, we can just refresh the DataSet using the Designer in Visual Studio and not have to make too many underlying DB code changes.
Any help at all would be most appreciated. Thanks in advance!
I saw all above solutions and they all are good, they inspired me to find my solution,
I made a more squeezed one, I know this is an old post, but I hope it helps people in the time to come,
Private Sub FillDataSet(ByRef ds As SvuDS)
For Each t As DataTable In ds.Tables
Dim adType As Type = Assembly.GetExecutingAssembly.GetType("ProjectNameSpace.MyDSTableAdapters." & t.TableName & "TableAdapter")
'Create Adapter Instance
Dim adapter As Object = Activator.CreateInstance(adType)
'Fill the Table
adapter.GetType().GetMethod("Fill").Invoke(adapter, New Object() {t})
Next
End Sub
I could've even inferred the namespace somehow too, but I wanted it to be simple, and it worked for me
There does not exists any api that lets you do this auto-fill of the entire typed-dataset or no such code is generated within typed-dataset that supports this. It is also difficult to do this because TableAdapters do not have a common base-class that can let you do this.
If you really need to do this, you'll have to maintain a collection of DataTable type-names and TableAdapter type-names and iterate over the collection to perform the dataset fill.
So I recommend to fill dataset for each table in 'hard-code' manner as your first code examples states.
EDIT
Here's one possible solution.
Define an Interface ITableAdapter as following
public interface ITableAdapter<TDataTable> : where TDataTable : DataTable
{
TDataTable SelectAll();
}
All TableAdapters are partial classes, so you can extend them and add your custom code in partial custom class for TableAdapter. Implement ITableAdapter on each TableAdapter in your typed-data-set. so it might look like this.
public partial class YourTableAdapter : ITableAdapter<YourDataSet.YourDataTableDataTable>
{
public YourDataSet.YourDataTableDataTable SelectAll()
{
return this.GetData();
}
}
Now, you can iterate over each type in your assembly and filter those of type ITableAdapter and call SelectAll() method on each of them fill it into your Dataset. :)
EDIT2
I just came up with another elegant solution for this problem. All you need to do is define the Interface ITableAdapter to map the already implemented methods in TableAdapters that are generated by the dataset-designer.
public interface ITableAdapter<TDataTable> : where TDataTable : DataTable
{
void Fill(TDataTable);
}
And extend your TableAdapter partial classes like this.
public partial class YourTableAdapter : ITableAdapter<YourDataSet.YourDataTableDataTable>
{
//No code required here, since Fill method is already defined in TableAdapter :)
}
OK, I think I have this worked out, and just want to share the results on the off chance that there are people out there who are as insane as I am.
Basically, all the magic happens using a couple of LINQ queries and reflection. For the purposes of this example, we will assume:
There is a strongly-typed DataSet created using the DataSet Designer in Visual Studio 2008, called dsTest. A module-level variable holds an instance of this DataSet and is called (appropriately enough), m_DataSet.
The tables themselves all follow a standard SQL Server naming convention, starting with "tbl".
As a part of this wizard, a series of table adapters were created for each table inside a namespace called dsTestTableAdapters.
Each adapter is named according to the table (so if we have "tblThingy", then an adapter named "tblThingyTableAdapter" would be created).
The application is in a namespace called, for lack of anything better, MyNamespace.
Here's the routine, called on Form Load:
Private Sub PopulateDataSet()
' Get our table adapters
Dim adapters As List(Of Type) = (From t As Type In System.Reflection.Assembly.GetExecutingAssembly.GetTypes Where t.Namespace = "MyNameSpace.dsTestTableAdapters" And t.Name.StartsWith("tbl") Select t).ToList
' Initialize our dataset
m_DataSet = New dsUtility
' Get our table names
Dim tableNames as List(Of String) = (From dtbl As DataTable In m_DataSet.Tables Select dtbl.TableName).ToList
' Loop through each table name and fill the table with the corresponding adapter
For Each iter As String In tableNames
' Grab the corresponding adapter name
Dim tableName As String = iter ' Grab a copy of the table name to avoid LINQ issues with iteration variables
Dim adapterType As Type = (From t As Type In adapters Where t.Name.StartsWith(tableName) Select t).First
' Given the adapter type name, use Reflection to create an instance
Dim adapter As Object = Activator.CreateInstance(adapterType)
' Use the instance to fill the appropriate table
adapter.Fill(m_DataSet.Tables(tableName))
Next
End Sub
I tried that, and it worked like a charm. Thanks, everyone, for your help and I hope you find this useful!
I think You have only one problem !
if this Typed dataset has relations between tables, this code won't load the datatables in the correct order !
Thanks, Mike, for the very thoughtful solution. Like you, I have been searching for a way to do what you've done, and to use the same mechanism to avoid an ugly switch statement (in C#) that has to case the generated TableAdapters to perform data binding updates.
I converted your VB code to C# as noted below. I made two changes (I'm using VS Express 2010 and .NET 4.0):
I changed the StartWith("tbl") method to EndsWith("TableAdapter") since a number of generated members in the TableAdapters namespace other than just the TableAdapters begin with "tbl" (assuming you want or need to follow that convention anyway), but only the TableAdapters end with "TableAdapter."
I changed the call to the Fill method since VS tells me at build time that the object referenced by "adapter" (which does look like a TableAdapter in the debugger) doesn't have a Fill method and there is no Fill extension method. I therefore cannot perform the Fill. I'm not at all sure why this didn't work. But in any case, I changed it to explicitly find the Fill method and then invoke that method. That seems to work.
Steve
public PopulateDataSet ()
{
// Get the TableAdapters
List<Type> tableAdapters = (from t in
System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
where t.Namespace == "MyNameSpace.m_DataSetTableAdapters"
&& t.Name.EndsWith("TableAdapter")
select t).ToList();
// Get the DataTable names
List<string> tableNames = (from DataTable dtbl in m_DataSet.Tables
select dtbl.TableName).ToList();
// Loop thru each table and fill it using the corresponding TableAdapter
foreach (string iter in tableNames)
{
string tableName = iter; // Stopt Linq issues with iteration vbls
Type adapterType = (from t in tableAdapters
where t.Name.StartsWith(tableName)
select t).First();
// Given the adapter type name, use Reflection to create an instance
Object adapter = Activator.CreateInstance(adapterType);
// Get a reference to the Fill method of the relevant adapter
MethodInfo method = adapter.GetType().GetMethod("Fill");
// Invoke the Fill method, passing in the relevant DataTable parameter
method.Invoke(adapter, new Object[] {m_DataSet.Tables[tableName]});
}
}
Some time ago I've found this thread and since then use this approach with success in my small project. But, recently I've found it out a bit limited. I have few queries for each table adapter in a data set with the names like "FillByContext", "FillById", "FillByName", etc., each one with a different set of parameters of different data types. All methods return tables with the same structure, but with different contents. So that I added some small "generalization" to the approach.
Private Sub MethodsAndParams(ds As DataSet,
dt As DataTable,
taParams() As Object,
taMethod As String)
Dim taType As Type = Assembly.GetExecutingAssembly.GetType(
"MyProjectName." +
ds.DataSetName +
"TableAdapters." +
dt.TableName +
"TableAdapter")
Dim ta As Object = Activator.CreateInstance(taType)
dt = ds.Tables(dt.TableName)
ta.GetType().GetMethod(taMethod).Invoke(
ta, New Object() {dt}.Union(taParams).ToArray)
End Sub
Now I can pass table adapter method names as strings and appropriate parameter sets as arrays of objects to this routine.

Linq To SQL DAL and lookup data source

I am learning linq to sql and I am trying to setup a lookup combo box in my web page. I was using a linq data source but I then moved my linqtosql code to it's own class library for my DAL dll (took out of app_code folder). So, I am converting the page code to be able to still have lookups driven now by a biz object.
Here's what I have done in my biz layer...
Public Class KosherTypes
Public Shared Function GetKosherTypes() As List(Of KosherTypeLookup)
Dim db As New DBDataContext
Dim types = From kt In db.KosherTypes _
Where kt.IsDeleted = False _
Order By kt.Name _
Select New KosherTypeLookup With {.Name = kt.Name, .ID = kt.KosherTypeID}
Return types.ToList
End Function
End Class
I then setup an object data source and mapped it to this class.
I have a few questions as when I was doing internet searches I didn't find anyone that seemed to be doing this and yet I know lookup tables / combo boxes are common...
Did I totally just miss something and there are better way(s) to do this?
I went with returning a list but I could have returned an IEnumerable or IQueryable. In my reading it seemed IQueryable had more functionality for linq to sql but since I am returning a simple two column list then I only need to return an IEnumerable or List. I went with a List since it's strongly typed. Did I make a good decision? AKA - Should I just have returned and IEnumerable or perhaps gone with IQueryable?
Thanks!
I'll answer in reverse order:
I wouldn't use IQueryable outside of your repos / DAL for the simple reason that since execution is deferred, you lose control of what exactly is executed (i.e., an aribtrary function could be assigned as a delegate for WHERE), making maintenance and testing much harder. I don't see an issue with you returning an IEnumberable(Of KosherTypeLookup) though.
If the lookup is a static lookup that never or rarely changes, you might want to look at a way to cache the lookup after the first use, to avoid having to hit the db every time that box is called. It really depends on your expected load, though. As always, performance/load testing will highlight where you need to optimize.