How do I use Linq-to-sql to iterate db records? - vb.net

I asked on SO a few days ago what was the simplest quickest way to build a wrapper around a recently completed database. I took the advice and used sqlmetal to build linq classes around my database design.
Now I am having two problems. One, I don't know LINQ. And, two, I have been shocked to realize how hard it is to learn. I have a book on LINQ (Linq In Action by Manning) and it has helped some but at the end of the day it is going to take me a couple of weeks to get traction and I need to make some progress on my project today.
So, I am looking for some help getting started.
Click HERE To see my simple database schema.
Click HERE to see the vb class that was generated for the schema.
My needs are simple. I have a console app. The main table is the SupplyModel table. Most of the other tables are child tables of the SupplyModel table.
I want to iterate through each of Supply Model records. I want to grab the data for a supply model and then DoStuff with the data. And I also need to iterate through the child records, for each supply model, for example the NumberedInventories and DoStuff with that as well.
I need help doing this in VB rather than C# if possible. I am not looking for the whole solution...if you can supply a couple of code-snippets to get me on my way that would be great.
Thanks for your help.
EDIT
For the record I have already written the following code...
Dim _dataContext As DataContext = New DataContext(ConnectionStrings("SupplyModelDB").ConnectionString)
Dim SMs As Table(Of Data.SupplyModels) = _dataContext.GetTable(Of Data.SupplyModels)()
Dim query = From sm In SMs Where sm.SupplyModelID = 1 Select sm
This code is working...I have a query object and I can use ObjectDumper to enumerate and dump the data...but I still can't figure it out...because ObjectDumper uses reflection and other language constructs I don't get. It DOES enumerate both the parent and child data just like I want (when level=2).
PLEASE HELP...I'M stuck. Help!
Seth

in C# it would be:
var result = from s in _dataContent.SupplyModels where s.SupplyModelID==1 select s;
foreach(SupplyModel item in result)
{
// do stuff
foreach(SupplyModelChild child in item.SupplyModelChilds)
{
//do more stuff on the child
}
}
and a VB.NET version (from the Telerik code converter)
Dim result As var = From s In _dataContent.SupplyModels _
Where s.SupplyModelID = 1 _
Select s
For Each item As SupplyModel In result
' do stuff
'do more stuff on the child
For Each child As SupplyModelChild In item.SupplyModelChilds
Next
Next

Related

Return multiple records from subroutine and parse into datatable [Unidata][U2.NET]

I am working with Unidata, and ADO.NET using the U2 .NET Provider. This may be a shot in the dark as there are not many resources for Unidata and .NET these days.
Currently I can only return a single MV record 153926þIþ and parse it using MV_To_DataTable. I'd like to return multiple records like 153926þIþÿ153926þIþÿ. Is there any built in mechanism for doing this? I fear I will have to write the extension to best accomodate me.
I retrieve a single record in a unidata subroutine this way:
SUBROUTINE GETITEMS(results)
EXECUTESQL "SELECT ID, STATUS, DESC FROM ITEMS TO GETITEM_LIST;"
DONE = 0
RECCNT = 0
LOOP
RECCNT += 1
READNEXTTUPLE REC FROM "GETITEM_LIST" ELSE DONE = 1
results := REC
IF RECCNT EQ 1 THEN EXIT
UNTIL DONE
REPEAT
results
CLEARSQL
RETURN
Simple subroutine that returns one record without any record marks. This works when I use the U2Parameter method called MV_To_DataTable to parse it into an existing datatable.
However when I change the subroutine line:
results:= REC to results:= REC : #RM to append the record marks and remove the limit of 1, the MV_To_DataTable no longer is able to parse it correctly. In fact it will throw System.IndexOutOfRangeException: Cannot find column 3.
VB.NET Code:
' ... Open database connection called U2Connection ...
Dim cmd = U2Connection.CreateCommand
cmd.CommandText = "CALL GETITEMS(?)"
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Clear()
cmd.Parameters.Add(New U2Parameter("#arg1", "") With {.Direction = ParameterDirection.InputOutput})
cmd.ExecuteNonQuery()
Dim tb As New DataTable
tb.Columns.Add("ID")
tb.Columns.Add("STATUS")
tb.Columns.Add("DESC")
cmd.Parameters.Item(0).MV_To_DataTable(tb) ' Error happens here
' System.IndexOutOfRangeException: Cannot find column 3.
It appears the method does not separate records. I could be interpretting this incorrectly.
*****UPDATE 2/9/2019
I went ahead and wrote my own extension method to support my return format with the record markers. It populates a datatable with records allowing me to continue as I normally would.
You are kind of straddling the Multivalue/System.Data divide here. If you have not already done so, I would suggest looking into the U2 Toolkit for .NET, which I believe is generally readily available if you are current on maintenance. It comes with some samples of how to do things like this in C# and VB as well some Entity Framework stuff.
But as to what is going on here, You are trying to put a U2Type.DynArray into a System.DataTable is kind of tricky as the DynArray is a Record state, which could contain multiple rows from multiple tables within a DataSet. As #RM is the terminator for a record so it turns DynArray into DynArray[] and you can't have that as a parameter as such.
To fix this with the minimum refactoring, you can still use MV_To_DataTable, but note that it is expecting your data to be tabular and in in a single record. These example assume a newline is an Attribute mark (#FM/#AM)
Here is the contents of what you are returning
Row1Column1
Row1Column2
Row1Column3:#RM
Row2Column1
Row2Column2
Row2Column3:#RM
Row13olumn1
Row13olumn2
Row13olumn3:#RM
And here is what MV_To_DataTable expects
Row1Column1:#VM:Row1Column2:#VM:Row1Column3
Row2Column1:#VM:Row2Column2:#VM:Row2Column3
Row3Column1:#VM:Row3Column2:#VM:Row3Column3
If you adjust your U2 sub to output that, it should work.
Additionally, you could try using your SQL command directly for .net, but that becomes perilous for other reasons depending on your data.
Good Luck!

Retrieve a document class-description symbolicName without fetching

I'm triying to retrieve a ClassDescription symbolicName of an IDocument object. It seems that i have to fetch its ClassDescription even if I just want the symbolicName.
Is there a way to do it ? I just want to avoid doing a fetch for every browsed document...
(Also IDocument.GetClassName doesn't help, it returns "Document")
I finally found a way, by making an SQL SELECT request retrieving the classDescription ID (which is not the symbolicName ID, but rather an "internal" one) :
Select This, d.Id, d.ClassDescription
From Document d
where d.Id = ID
It seems to be lighter than a line like document.fetch(classDescription) (pseudo call) cause it should just retrieves the ID.
I thought it worth mentioning a problem regarding the accepted answer.
There are times that doing a query would be "lighter" however I believe you are missing something involving fetching a document.
FileNet's fetchInstance command can take in a PropertyFilter.
In your case you could do something along the lines of:
PropertyFilter pf = new PropertyFilter();
pf.AddIncludeProperty(new FilterElement(null, null, null, "ClassDescription", null));
doc = Factory.Document.FetchInstance(os, new Id("doc.ID()"), pf);
You would probably want to look at your original fetch of this document and make sure to specify the full list of property filters at that point.
See Working With Documents

How to use LINQ to Entities to make a left join using a static value

I've got a few tables, Deployment, Deployment_Report and Workflow. In the event that the deployment is being reviewed they join together so you can see all details in the report. If a revision is going out, the new workflow doesn't exist yet new workflow is going into place so I'd like the values to return null as the revision doesn't exist yet.
Complications aside, this is a sample of the SQL that I'd like to have run:
DECLARE #WorkflowID int
SET #WorkflowID = 399 -- Set to -1 if new
SELECT *
FROM Deployment d
LEFT JOIN Deployment_Report r
ON d.FSJ_Deployment_ID = r.FSJ_Deployment_ID
AND r.Workflow_ID = #WorkflowID
WHERE d.FSJ_Deployment_ID = 339
The above in SQL works great and returns the full record if viewing an active workflow, or the left side of the record with empty fields for revision details which haven't been supplied in the event that a new report is being generated.
Using various samples around S.O. I've produced some Entity to SQL based on a few multiple on statements but I feel like I'm missing something fundamental to make this work:
int Workflow_ID = 399 // or -1 if new, just like the above example
from d in context.Deployments
join r in context.Deployment_Reports.DefaultIfEmpty()
on
new { d.Deployment_ID, Workflow_ID }
equals
new { r.Deployment_ID, r.Workflow_ID }
where d.FSJ_Deployment_ID == fsj_deployment_id
select new
{
...
}
Is the SQL query above possible to create using LINQ to Entities without employing Entity SQL? This is the first time I've needed to create such a join since it's very confusing to look at but in the report it's the only way to do it right since it should only return one record at all times.
The workflow ID is a value passed in to the call to retrieve the data source so in the outgoing query it would be considered a static value (for lack of better terminology on my part)
First of all don't kill yourself on learning the intricacies of EF as there are a LOT of things to learn about it. Unfortunately our deadlines don't like the learning curve!
Here's examples to learn over time:
http://msdn.microsoft.com/en-us/library/bb397895.aspx
In the mean time I've found this very nice workaround using EF for this kind of thing:
var query = "SELECT * Deployment d JOIN Deployment_Report r d.FSJ_Deployment_ID = r.Workflow_ID = #WorkflowID d.FSJ_Deployment_ID = 339"
var parm = new SqlParameter(parameterName="WorkFlowID" value = myvalue);
using (var db = new MyEntities()){
db.Database.SqlQuery<MyReturnType>(query, parm.ToArray());
}
All you have to do is create a model for what you want SQL to return and it will fill in all the values you want. The values you are after are all the fields that are returned by the "Select *"...
There's even a really cool way to get EF to help you. First find the table with the most fields, and get EF to generated the model for you. Then you can write another class that inherits from that class adding in the other fields you want. SQL is able to find all fields added regardless of class hierarchy. It makes your job simple.
Warning, make sure your filed names in the class are exactly the same (case sensitive) as those in the database. The goal is to make a super class model that contains all the fields of all the join activity. SQL just knows how to put them into that resultant class giving you strong typing ability and even more important use-ability with LINQ
You can even use dataannotations in the Super Class Model for displaying other names you prefer to the User, this is a super nice way to keep the table field names but show the user something more user friendly.

How can I call a WCF service from code, in the .net framework 3

I'm working in VB.Net and I'm trying to make a piece of code more generic.
In fact, there's a big Select Case statement that build a ProxyServer based on a value passed in parameter (a string).
Select Case _strNteraHL7
Case Constantes.NomPRPMIN306010
strUrl = ObtenirUrl("ProviderDetailsQuery", _strVersion, _strEnvir, True, _blnSimulCAIS, _blnSimulPDS, _blnSimulPDSSIIR, _blnSimulPDSInteg)
objWsHL7 = New wsProviderDetailsQuery.ProviderDetailsQueryClient(objBinding, New EndpointAddress(strUrl))
Case Constantes.NomPRPMIN301010
strUrl = ObtenirUrl("AddProvider", _strVersion, _strEnvir, True, _blnSimulCAIS, _blnSimulPDS, _blnSimulPDSSIIR, _blnSimulPDSInteg)
objWsHL7 = New wsAddProvider.AddProviderClient(objBinding, New EndpointAddress(strUrl))
The objects like "wsAddProvider" and "wsProviderDetailsQuery" in the previous example are service references that have been added through the GUI of Visual Studio...
What I want to know, is basically, if I can call this constructor from a certain pool containing service references, similar as when I want to call a control in a controls container...
for example:
objWsHL7 = new wcfServicesContainer("serviceNameHere", paramArray())
or something similar, so I can remove all those big switch cases, that repeat the same thing 30 times.
objWsHL7 being an object or type "object" at compiling.
Sorry if I didn't mention enough detail, feel free to let me know if you need more, I don't really know what information I have to provide for this.
Edit: I've spotted another piece of code here that uses similar calls, maybe it'll help understanding...
Again, in another switch case statement,
objMsgHL7Out = _objWsHL7.ProviderDetailsQuery(_objMsgIn)
objMsgHL7Out is a System.ServiceModel.Channels.Message
_objMsgIn is a System.ServiceModel.Channels.Message
_objWsHL7 is an Object
Try this:
Create a hashmap of HashMap<string, string>
Add Constantes.NomPRPMIN306010, ... as key and "AddProvider", ... as value.
call ObtenirUrl(hashmap[_strNteraHL7], ...

MOSS 2007: What is the source of "Directories"?

I'm trying to generate a new SharePoint list item directly using SQL server. What's stopping me is damn tp_DirName column. I have no ideas how to create this value.
Just for instance, I have selected all tasks from AllUserData, and there are possible values for the column: 'MySite/Lists/Task', 'Lists/Task' and even 'MySite/Lists/List2'.
MySite is the FullUrl value from Webs table. I can obtain it. But what about 'Lists/Task' and '/Lists/List2'? Where they are stored?
If try to avoid SQL context, I can formulate it the following way: what is the object, that has such attribute as '/Lists/List2'? Where can I set it up in GUI?
Just a FYI. It is VERY not supported to try and write directly to SharePoint's SQL Tables. You should really try and write something that utilizes the SharePoint Object Model. Writing to the SharePoint database directly mean Microsoft will not support the environment.
I've discovered, that [AllDocs] table, in contrast to its title, contains information about "directories", that can be used to generate tp_DirName. At least, I've found "List2" and "Task" entries in [AllDocs].[tp_Leaf] column.
So the solution looks like this -- concatenate the following 2 components to get tp_DirName:
[Webs].[FullUrl] for the web, containing list, containing item.
[AllDocs].[tp_Leaf] for the list, containing item.
Concatenate the following 2 components to get tp_Leaf for an item:
(Item count in the list) + 1
'_.000'
Regards,
Well, my previous answer was not very useful, though it had a key to the magic. Now I have a really useful one.
Whatever they said, M$ is very liberal to the MOSS DB hackers. At least they provide the following documents:
http://msdn.microsoft.com/en-us/library/dd304112(PROT.13).aspx
http://msdn.microsoft.com/en-us/library/dd358577(v=PROT.13).aspx
Read? Then, you know that all folders are listed in the [AllDocs] table with '1' in the 'Type' column.
Now, let's look at 'tp_RootFolder' column in AllLists. It looks like a folder id, doesn't it? So, just SELECT the single row from the [AllDocs], where Id = tp_RootFolder and Type = 1. Then, concatenate DirName + LeafName, and you will know, what the 'tp_DirName' value for a newly generated item in the list should be. That looks like a solid rock solution.
Now about tp_LeafName for the new items. Before, I wrote that the answer is (Item count in the list) + 1 + '_.000', that corresponds to the following query:
DECLARE #itemscount int;
SELECT #itemscount = COUNT(*) FROM [dbo].[AllUserData] WHERE [tp_ListId] = '...my list id...';
INSERT INTO [AllUserData] (tp_LeafName, ...) VALUES(CAST(#itemscount + 1 AS NVARCHAR(255)) + '_.000', ...)
Thus, I have to say I'm not sure that it works always. For items - yes, but for docs... I'll inquire into the question. Leave a comment if you want to read a report.
Hehe, there is a stored procedure named proc_AddListItem. I was almost right. MS people do the same, but instead of (count + 1) they use just... tp_ID :)
Anyway, now I know THE SINGLE RIGHT answer: I have to call proc_AddListItem.
UPDATE: Don't forget to present the data from the [AllUserData] table as a new item in [AllDocs] (just insert id and leafname, see how SP does it itself).