Unable to pull fields from within LINQ query - vb.net

Quick overview:
Visual Basic 2010 WinForm app pulls data from DB2. The app allows users to filter the data.
The problem:
I'm doing something wrong with my LINQ query (or object definition), as I'm not able to access fields within the dataset. Pulling data from DB2 is fine, I get that data and store it as an IEnumerable.
I plan to run this app as disconnected since its readonly for 95% of the users and it accesses 100,000+ records. Because of this, I have two datasets: 1) 'data' which is the full dataset pulled from DB2 (I don't plan on doing any modifications to it), (2) 'filteredData' which is a subset of data based on user entered filters.
Dim data As IEnumerable
Dim dataFiltered = From record in data
Select record
'Filter data based on version
Select case uxCodeVersion.Text
Case "10"
dataFiltered = From rec in dataFiltered
Where rec.
... (other parts of case statement removed)
End Select
and this is my problem. I'm expecting to see the list of fields within 'rec.' (such as rec.CodeVersion) ;however, I just receive object methods (Equals, GetHashCode, GetType, ReferenceEquals, ToString).
What simple thing am I missing?
Performance is an issue too, but I figured one problem at a time...
Thank you,
Brian.
Here is the answer as provided below.
When defining data, I need to define it to the generic list DTO. So in my case, that becomes:
Dim data As IEnumerable(Of DataAccessLayer.DiagnosisAndDiagnosisIndustryCombinedDTO)
Then when accessing the code, its the same as before, though I temporarily took out the dataFiltered field and just used data.
dataFiltered = From rec in data
Where rec.CodeVersion = uxCodeVersion.Text

From your code sample, you define data as IEnumerable. From your comment you say that your data layer returns a List(of T),
While the assignement of List(of T) to IEnumerable is valid, the type contained in the IEnumerable is Object. That's why you don't get intellisense.
You should either declare IEnumerable(of T), or do something like:
Dim data = datalayer.GetFoo()
This will cause type inference and you'll get intellisense.

Until you assign a type to IEnumerable, you won't be able to access the fields in this way. For example (c#): IEnumerable<YourType> should work.

Related

Entity Framework: Check if value is nothing without reading all the data?

I am looking for a way to test a db field via an EF query simply to see if it is null or not, but without reading the entirety of the data. If it's not null, it's going to be huge and there are many rows, so doing this the standard way (as in the snippet below) is a very heavy operation on the db and takes ages. What I would like is to just let the user know which rows have some data in that field, and then they can choose to view it, which will then perform a single operation on that row to return just that data.
I feel like this should be possible, testing to see if something has a value or not doesn't require reading all of the data, so is there a way to achieve the desired result?
Class MyClassModel
Property HasData As Boolean?
Function Query (DB as DBContext) As IEnumerable(Of MyClassLineModel)
Dim data As IQueryable(Of MyClass) = DB.MyClasses
If HasData.HasValue Then data = From t In data Where t.Data IsNot Nothing = HasData
Dim ret = (From t In data Select New MyClassLineModel With {
.HasData = t.Data IsNot Nothing}).ToArray
Return ret
End Function
End Class
Class MyClassLineModel
Property HasData As Boolean?
End Class
p.s. This is an MVC project, hence the "model" etc. But this question is still applicable in general.
EDIT
NVM, turns out that EF does this for me. The above code converts to a db query
CASE WHEN Data IS NULL THEN 0 ELSE 1 END
which doesn't read the whole field. Response time is fine. Mods can delete the Q, or keep it for anyone else who wants to know. Thanks for all the answers guys.
You can use .Any() - if you use this on a IQueryable backed by SQL it will do something like a WHERE EXISTS in SQL which won't read all the data...
i.e.
var dataExists = _dataContext.Entity.Any(e => e.Field != NULL);

Is it possible to pass a single variable containing many parameters to an Insert query ln TableAdapter?

I'm working with TableAdapter on VS, and have something like this:
TableAdapter.Insert(parameter1,parameter2,parameter3,...,parameterN)
And would like to know if is it possible to pass a single variable containing those parameters instead, and get something like this:
TableAdapter.Insert(all_parameters)
NOTE: I would like to know because I have sometimes a lot of parameters to pass and the line code becomes very large
In a word; no. The TableAdapter Insert method is generated in response to the GenerateDBDirect checkbox being ticked during the tableadapter wizard setup, and the direct methods for Insert/Update/Delete only exist in the format that you see
That being said, you can pass a strongly typed DataRow to the Update method, as it has overloads that are intended for persisting changes in a dataset/datatable/collection of datarow/single datarow, to a database
This means you could, instead of doing the following to insert a row:
personTA.Insert("John", "Smith", 30, "1 The Road");
Build a datarow that is state Added, and Update it, which will cause the SQL INSERT to be run:
var ro = personDataTable.NewPersonRow();
ro.FirstName = "John";
ro.LastName = "Smith";
ro.Age = 30;
ro.Address = "1, The Road";
personDataTable.AddPersonRow(ro);
//even though it's called Update, this will insert the record to the table, because it's RowState is Added
personTA.Update(ro);
This is more the way TableAdapters were intended to be used: you'd bind the datarow to text boxes, the user would type in them, and then you'd use Update() to save the changes to the database. You haven't really had to do any work in doing so, and it's hence less laborious to code, than doing something like:
personTA.Insert(_firstNameTextBox.Text, _lastNameTextBox.Text, …)
When you work with strongly typed datasets, databound textboxes etc can be created with a simple drag operation, by pulling a relevant node out of the DataSOurces window and dropping it onto the form. If you aren't using this / don't have a UI / aren't databinding then consider that there are some other ways you can make your life easier.. If your data is already in an array, then you could just set the ItemArray of the row:
var ro = personDataTable.NewPersonRow();
ro.ItemArray = array_with_person_data;
personDataTable.AddPersonRow(ro);
personTA.Update(ro);
Apologies that this is C# syntax; I've just seen that you've tagged VB. There are, however, no significant differences in the two syntaxes for this particular aspect of coding - I think mentally removing the semicolons and changing var to Dim would be all that's required to turn this particular C# into VB
Edit:
Don't forget that you're typically allowed to add your own code to anything that Microsoft provide for you in a designer. If you double click a tableadapter in the design surface you'll be taken to an empty partial class where you can add another/your own overload of Insert, perhaps one that takes an array and then simply calls the generated Insert, using all the parameters in order:
Namespace YourProgram.YourDataSetTableAdapters{
Partial Class YourTableAdapter
//ADD YOUR OWN INSERT METHOD HERE
Public Function Insert(things as Object()){
Return this.Insert( _
DirectCast(things[0] as String), _
DirectCast(things[1] as String), _
DirectCast(things[2] as Int32), _
DirectCast(things[0] as String) _
)
End Function
End Class
End Namespace
I've guessed at this VB; it might have syntax errors, hopefully you get the idea

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.