Dynamic programming in VB - vb.net

We develop applications for SAP using their SDK. SAP provides a SDK for changing and handling events occuring in the user interface.
For example, with this SDK we can catch a click on a button and do something on the click. This programming can be done either VB or C#.
This can also be used to create new fields on the pre-existing form. We have developed a specific application which allows users to store the definition required for new field in a database table and the fields are created at the run time.
So far, this is good. What we require now is that the user should be able to store the validation code for the field in the database and the same should be executed on the run time.
Following is an example of such an event:
Private Sub SBO_Application_ItemEvent(ByVal FormUID As String, ByRef pVal As SAPbouiCOM.ItemEvent, ByRef BubbleEvent As Boolean) Handles SBO_Application.ItemEvent
Dim oForm As SAPbouiCOM.Form
If pVal.FormTypeEx = "ACC_QPLAN" Then
If pVal.EventType = SAPbouiCOM.BoEventTypes.et_LOST_FOCUS And pVal.BeforeAction = False Then
oProdRec.ItemPressEvent(pVal)
End If
End If
End Sub
Public Sub ItemPressEvent(ByRef pVal As SAPbouiCOM.ItemEvent)
Dim oForm As SAPbouiCOM.Form
oForm = oSuyash.SBO_Application.Forms.GetForm(pVal.FormTypeEx, pVal.FormTypeCount)
If pVal.EventType = SAPbouiCOM.BoEventTypes.et_LOST_FOCUS And pVal.BeforeAction = False Then
If pVal.ItemUID = "AC_TXT5" Then
Dim CardCode, ItemCode As String
ItemCode = oForm.Items.Item("AC_TXT2").Specific.Value
CardCode = oForm.Items.Item("AC_TXT0").Specific.Value
UpdateQty(oForm, CardCode, ItemCode)
End If
End If
End Sub
So, what we need in this case is to store the code given in the ItemPressEvent in a database, and execute this in runtime.
I know this is not straight forward thing. But I presume there must be some ways of getting these kind of things done.
The SDK is made up of COM components.
Thanks & Regards,
Rahul Jain

I've not done this myself, but I think you're going to have to actually use the Systems.Runtime.CompilerServices functions to dynamically compile an assembly and then link it in. Another solution if you are using SQL Server might be to take advantage of the fact that you can write C# or VB.NET code in stored procedures. That might be a way.

Dim sqlstring1 As String = "Blah Blah Blah SQL here"
Dim Rs SAPbobsCOM.Recordset
Rs = GetDIConnection.GetBusinessObject(SAPbobsCOM.BoObjectTypes.BoRecordset)
rs.doquery(SqlString1)

You can create the code dynamically and compile it..
Have some simple interfaces to call the validation code and in all your dynamic code, implement the interface(s). This way, you can load assembly dynamically and get the class as an interface and use that interface directly..

Related

Understanding visual basic code

I am new to Visual basic and followed a tutorial on how to filter a report viewer.
On the button click I call this function and pass the value within textbox1 as so:
Mysub(TextBox1.Text)
I have a rough understanding of what’s going on just by reading the code but I am wondering if any of you can annotate the code so I can have a full understanding of what’s happening (the tutorial I followed was not well documented)
Here is the Private sub:
Private Sub Mysub(ByVal make As String)
Me.ReportViewer1.LocalReport.DataSources.Clear()
Dim adapter As New Database1DataSetTableAdapters.DataTable1TableAdapter
Dim table As New Database1DataSet.DataTable1DataTable
adapter.FillByModulename(table, make)
Me.ReportViewer1.LocalReport.DataSources.Add(New ReportDataSource("DataSet1", CType(table, DataTable)))
Me.ReportViewer1.RefreshReport()
End Sub
FillByModulename is a query I created with the filter set to =#Modulename for the Module name column so whatever is entered into the textbox is what will be filtered in this column.
Thank you all for your time

Reading and changing fields in SAP with RFC via VB .NET

I'm currently trying to figure out the basics of remote function calls via vb .NET. I feel pretty helpless however, because their just isn't any useful documentation for the most simple of tasks.
What I'm trying to do atm is starting the transaction CO13, write the confirmation number in the appropriate field and cancel the order. Even this simple tasks turned out to be a pain in the ass. I'm still not sure how to access and modify the contents of a specific field. There are some examples with tables for excel in the net, but hat's about it. What I have so far is this (login is working and in another function):
Public Function stornieren() As Boolean
Dim ordernr As String
Dim confirmationnr
Dim confirmation As Object
Dim R3 As Object
Dim CO13 As Object
Dim result
R3 = CreateObject("SAP.Functions")
ordernr = TextBox3.Text
confirmationnr = TextBox4.Text
CO13 = R3.Add("RFC_CALL_TRANSACTION_USING")
CO13.exports("TCODE") = "CO13"
CO13.exports("MODE") = "S"
confirmation = CO13.exports("RUECK")
confirmation.value = confirmationnr
result = CO13.call
End Function
RUECK is the Field Name. I want to write the value of "confirmationnr" into the field RUECK. "confirmation.value = confirmationnr" throws the error message "the object variable could not be determined" and "NullReferenceException" was not handled. Sounds to me like the object is empty.
Thanks in advance.
EDIT: Now trying via BAPIs and particularly BAPI_PRODORDCONF_CANCEL. I have no idea about the syntax though. Any help would be appreciated.

How to prevent VBA variables from being shared across Word documents?

I have a VBA template project that runs automatically when a Word document is opened. However, if I open multiple documents, they all share the variables values. How can declare these variables to be only associated with the active window or active document?
I tried declaring them in a Class Module, but that did not help. Switching between opened document I can see that these variables are shared.
Any input is appreciated...
This what I have in my Module:
Option Private Module
Dim CurrentCommand As String
Public Function SetCurrentCommand(command)
CurrentCommand = command
End Function
Public Function GetCurrentCommand()
GetCurrentCommand = CurrentCommand
End Function
More Info: The code/Macro start at AutoExec like this:
Public Sub Main()
Set oAppClass.oApp = Word.Application
If PollingRate <> "" Then Application.OnTime Now + TimeValue(PollingRate), "CaptureUserViewState"
End Sub
And the CaptureUserViewState is a Sub that resides in a different Module and does all teh checks (comparing new values to last recorded ones) and here how this Sub does the check:
If WL_GetterAndSetter.GetLastPageVerticalPercentage <> pageVerticalPercentScrolled Then
'Update the last value variable
WL_GetterAndSetter.SetLastPageVerticalPercentage (pageVerticalPercentScrolled)
'log change
End If
You don't give us much information, but I assume you declared public variables at module level like this:
Public myString As String
Public myDouble As Double
From VBA documentation:
Variables declared using the Public statement are available to all procedures in all modules in all applications unless Option Private Module is in effect; in which case, the variables are public only within the project in which they reside.
The answer is to use Option Private Module.
When used in host applications that allow references across multiple projects, Option Private Module prevents a module’s contents from being referenced outside its project.
[...] If used, the Option Private statement must appear at module level, before any procedures.
EDIT You have now clarified that you declare your variables using Dim at module level. In this case, Option Private Module is irrelevant.
Variables declared with Dim at the module level are available to all procedures within the module.
i.e. regardless of whether you're using Option Private Module or not.
If you're finding that the values are retained between runs, then that must be because you are running a procedure from the same module from the same workbook. You may think you're doing something else, but in reality this is what you're doing.
EDIT
In your class module, instead of Dim CurrentCommand As String try Private CurrentCommand As String. Without more information it's hard to debug your program. I'm just taking random potshots here.
What you need to do is store multiple versions of the variables, one set per document.
So I would suggest that you create a simple class to hold the different values.
You then store them in a collection mapping the data-set with the document name or similar as the key.
In classmodule (MyData), marked as public:
Public data1 as String
Public data2 as Integer
In module with the event-handlers:
Dim c as new Collection 'module global declaration
Sub AddData()
Dim d as new MyData 'Your data set
d.data1 = "Some value"
d.data2 = 42
c.add Value:=d, Key:=ActiveDocument.name
End Sub
Then when you enter the event-handler you retrieve the data and use the specific set for the currently active document.
Sub EventHandler()
Dim d as MyData
set d = c.item(ActiveDocument.name)
'use data
'd.data1...
End Sub
Please not that this code is just on conceptual level. It is not working, You have to apply it to your problem but it should give you some idea on what you need to do. You will need to add alot of error handling, checking if the item is already in the collection and so on, but I hope you understand the concept to continue trying on your own.
The reason for this is because, as I understand the situation from your question, you only have one version of your script running, but multiple documents. Hence the script have to know about all the different documents.
On the other hand, If each document would have their own code/eventhandlers, hence having multiple versions of the script running, then you don't need the solution provided above. Instead you need to be careful what document instance you reference in your script. By always using "ThisDocument" instead of "ActiveDocument" you could achieve isolation if the code is placed in each open document.
However, as I understood it, you only have one version of the script running, separate from the open documents, hence the first solution applies.
Best of luck!
You might want to store the Document Specific details using
The Document.CustomDocumentProperties Property
http://msdn.microsoft.com/en-us/library/office/aa212718(v=office.11).aspx
This returns a
DocumentProperties Collection
Which you can add new Properties to Using
Document.CustomDocumentProperties.Add(PropertyName, LinkToContent, Value, Type)
And then Read From using
Document.CustomDocumentProperties.Item(PropertyName)
A downside, or bonus, here is that the properties will remain stored in the document unless you delete them.
This may be a good thing or a bad thing

How do I update a single table of a DataSet using a TableAdapter, without hard-coding the table name?

This seems like a really basic thing that I'm doing, yet I'm tearing my hair out trying to make it work.
My situation is this: I have a project which contains a large number of lookup tables, and I have all of these lookup tables represented in a single typed DataSet, which contains TableAdapters for each lookup. I've designed an editor for these lookup tables, which should allow editing of one of these at a time. My front-end is written in VB and WinForms, the back-end is a SOAP web service; I can successfully pass the changes to the DataSet back to the web service, but can't find a way to use a TableAdapter to update the single table that has been changed.
What I'm trying to do is instantiate the appropriate TableAdapter for the updated DataTable by sending the name of the table back to the web service along with the DataSet, then referring to the TableAdapter with a dynamic name. The normal way to instantiate a TableAdapter is this:
Dim ta As New dsLookupsTableAdapters.tlkpMyTableTableAdapter
What I'd like to do is this, but of course it doesn't work:
strTableName = "tlkpMyTable"
Dim ta As New dsLookupsTableAdapters(strTableName & "TableAdapter")
Is there any way to achieve this, or am I taking the wrong approach altogether? My other alternative is to write separate code for each table, which I'd prefer to avoid!
You can use Activator to create an instance of your TableAdapter from its string name, just like you want:
object adapter = Activator.CreateInstance(Type.GetType("My.Namespace.MyDataSetTableAdapters." + myTable.Name + "TableAdapter"));
Then, because TableAdapters don't have a common interface, you should use reflection to call its Update method:
adapter.GetType().GetMethod("Update").Invoke(adapter, null);
http://msdn.microsoft.com/en-us/library/system.type.getmethod.aspx
This is from memory, but roughly close enough. You can also use GetProperty to get the connection property and set it as required.
Not sure I 100% understand, do you have a single DataTable in your DataSet, or one DataTable per lookup table?
Anyway, perhaps you could you this approach to filter by lookup table?
It's pretty easy to create types at runtime given the (string) type name.
Here's a self-contained VB class which illustrates one way to do it: use System.Activator.CreateInstance to create instances of types using a string representation of the type name. Then you can cast it to a DataAdapter base class and use it like any other DataAdapter.
Public Class dsLookupsTableAdapters
Public Function CreateInstance(ByVal strName As String) As Object
CreateInstance = Nothing
For Each a As System.Reflection.Assembly In System.AppDomain.CurrentDomain.GetAssemblies()
Try
Dim strAssemblyName As String() = a.FullName.Split(New Char() {","c})
Dim strNameTemp As String = strAssemblyName(0) & "." & strName
Dim instance As Object = System.Activator.CreateInstance(a.FullName, strNameTemp)
If instance IsNot Nothing Then
Dim handle As System.Runtime.Remoting.ObjectHandle
handle = CType(instance, System.Runtime.Remoting.ObjectHandle)
Dim o As Object = handle.Unwrap()
CreateInstance = o
Exit For
End If
Catch ex As System.Exception
Continue For ' ignore exception, means type isn't there
End Try
Next
End Function
Public Class tlkpMyTableTableAdapter
Inherits System.Data.Common.DataAdapter
End Class
Public Sub Test()
' define type name. note that, in this sample, tlkpMyTableTableAdapter is a nested
' class and dsLookupsTableAdapters is the containing class, hence the "+". If, however,
' dsLookupsTableAdapters is a namespace, replace the "+" with a "."
Dim typeName As String = "dsLookupsTableAdapters+tlkpMyTableTableAdapter"
Dim adapter As System.Data.Common.DataAdapter
Dim o As Object = CreateInstance(typeName)
adapter = CType(o, System.Data.Common.DataAdapter)
End Sub
End Class
If you are using VB.Net 2008, then use the tableadaptermanager (http://msdn.microsoft.com/en-us/library/bb384426.aspx). I think this would be much easier to code against :)
Wade

Creating An Insert Statement -- Windows application Vb.Net

I am doing windows appliction in vb.net. i have customer object contains save method. how do i generate insert query?
I need to save the object in relational database (SQL server). I need to know which is the correct way of doing the insertion ie,. Inside the save method i have written the SQL statement to save the object. Is it the correct way?
Thanks
A simple INSERT statement for SQL takes this basic form:
INSERT INTO [tablename] ( [column1], [column2], ... ) VALUES ( [value1], [value2], ...)
So, we obviously need to know about the database table you are using: what columns it has. We also need to know about the class: what properties it has. Finally, we need to know about the data types for the table columns and class properties, and how the properties will map to the columns. For very simple objects the names and types will just line up. But in other cases your class may itself contain a collection (or several) that would mean inserting data into more than one table.
After all this is determined, we still need two things: connection information for the database (usually distilled down into a single connection string) and whether or not you are concerned that your class instance may have been saved previously, in which case you want to build an UPDATE statement rather than INSERT.
Assuming you can answer all of that in a satisfactory manner, your VB.Net code will look something like this (of course substituting your specific column, property, type, and connection information where appropriate):
Public Class Customer
Public Sub Save()
DAL.SaveCustomer(Me)
End Sub
' ...'
End Class
.
' a VB Module is a C# static class'
Public Module DAL
Private ConnString As String = "Your connection string here"
Public Sub SaveCustomer(ByVal TheCustomer As Customer)
Dim sql As String = "" & _
"INSERT INTO [MyTable] (" & _
"[column1], [column2], ..." & _
") VALUES (" & _
"#Column1, #Column2, ... )"
Using cn As New SqlConnection(ConnString), _
cmd As New SqlCommand(sql, cn)
cmd.Parameters.Add("#column1", SqlDbTypes.VarChar, 50).Value = TheCustomer.Property1
cmd.Parameters.Add("#column2", SqlDbTypes.VarChar, 1000).Value = TheCustomer.Property2
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub
End Module
I know you've already heard that separating out your database code is the "right thing to do"tm, but I thought you might also want some more specific reasons why you would want to structure your code this way:
Your connection string is kept in one place, so if your database server moves you only need to make one change. Even better if this is it's own assembly or config file.
If you ever move to a completely different database type you only need to change one file to update the program.
If you have one developer or a DBA who is especially good with sql, you can let him do most of the maintenance on this part of the app.
It makes the code for your "real" objects simpler, and therefore easier to spot when you make a logical design error.
The DAL code might eventually be re-usable if another application wants to talk to the same database.
If you use an ORM tool most of the DAL code is written for you.
There's a few issues here. First, exactly where are you saving this? You say SQL, but is it a SQL Server, an instance of SQL Express, a Local Data Cache (SQL CE 3.5) or saving via a Web Service to talk to your SQL SERVER. These different data sources have different connectivity options/requirements, and in the case of SQL CE there's a few other "gotchas" involved in the SQL itself.
Second, are you sure you want to save data into a relational datastore like SQL Server? Consider, you could use XML, a data file (text, CSV. etc) or even a custom binary file type instead.
Since you're working on a windows application, you have a bunch of options on where and how to save the data. Until you know where you want to put the data, we'd be hard pressed to help you do so.
I agree with Mike Hofer. Keeping your class that does your retrieval and persisting of object separate from your business classes is key to having a flexible and robust design. This is the kind of code you want to be seeing in your GUI or Business layer:
//Populate Customer Objects List with data
IList<Customer> customerList = new List<Customer>()
Customer newCustomer1 = new Customer();
newCustomer.Name = "New Name"
newCustomer.email ="abcd#abcd.com"
customerList.Add(newCustomer1)
//DAL calls
DataAccessClass dalClass = new DataAccessClass ();
dalClass.InsertCustomers(customerList);
Inside your DALClass there should be a method called InsertCustomers(IList customers) and it should have the following code:
Public Function InsertCustomers(ByVal objectList As IList(Of Customer)) As Integer
Dim command As IDbCommand = Nothing
Dim rowsAffected As Integer = 0
Dim connection As IDbConnection = New System.Data.SqlClient.SqlConnection(Me.ConnectionString)
Try
connection.Open
Dim e As IEnumerator = objectList.GetEnumerator
Do While e.MoveNext
command = connection.CreateCommand
command.CommandText = "insert into dbo.Customer(CustomerID,CustomerGUID,RegisterDate,Password,SiteID,Las"& _
"tName,FirstName,Email,Notes,BillingEqualsShipping,BillingLastName) values (#Cust"& _
"omerID,#CustomerGUID,#RegisterDate,#Password,#SiteID,#LastName,#FirstName,#Email"& _
",#Notes,#BillingEqualsShipping,#BillingLastName)"
System.Console.WriteLine("Executing Query: {0}", command.CommandText)
Dim paramCustomerID As IDbDataParameter = command.CreateParameter
paramCustomerID.ParameterName = "#CustomerID"
command.Parameters.Add(paramCustomerID)
Dim paramCustomerGUID As IDbDataParameter = command.CreateParameter
paramCustomerGUID.ParameterName = "#CustomerGUID"
command.Parameters.Add(paramCustomerGUID)
Dim paramRegisterDate As IDbDataParameter = command.CreateParameter
paramRegisterDate.ParameterName = "#RegisterDate"
command.Parameters.Add(paramRegisterDate)
Dim paramPassword As IDbDataParameter = command.CreateParameter
paramPassword.ParameterName = "#Password"
command.Parameters.Add(paramPassword)
Dim paramSiteID As IDbDataParameter = command.CreateParameter
paramSiteID.ParameterName = "#SiteID"
command.Parameters.Add(paramSiteID)
Dim paramLastName As IDbDataParameter = command.CreateParameter
paramLastName.ParameterName = "#LastName"
command.Parameters.Add(paramLastName)
Dim paramFirstName As IDbDataParameter = command.CreateParameter
paramFirstName.ParameterName = "#FirstName"
command.Parameters.Add(paramFirstName)
Dim paramEmail As IDbDataParameter = command.CreateParameter
paramEmail.ParameterName = "#Email"
command.Parameters.Add(paramEmail)
Dim paramNotes As IDbDataParameter = command.CreateParameter
paramNotes.ParameterName = "#Notes"
command.Parameters.Add(paramNotes)
Dim paramBillingEqualsShipping As IDbDataParameter = command.CreateParameter
paramBillingEqualsShipping.ParameterName = "#BillingEqualsShipping"
command.Parameters.Add(paramBillingEqualsShipping)
Dim paramBillingLastName As IDbDataParameter = command.CreateParameter
paramBillingLastName.ParameterName = "#BillingLastName"
command.Parameters.Add(paramBillingLastName)
Dim modelObject As Customer = CType(e.Current,Customer)
paramCustomerID.Value = modelObject.CustomerID
paramCustomerGUID.Value = modelObject.CustomerGUID
paramRegisterDate.Value = modelObject.RegisterDate
If IsNothing(modelObject.Password) Then
paramPassword.Value = System.DBNull.Value
Else
paramPassword.Value = modelObject.Password
End If
paramSiteID.Value = modelObject.SiteID
If IsNothing(modelObject.LastName) Then
paramLastName.Value = System.DBNull.Value
Else
paramLastName.Value = modelObject.LastName
End If
If IsNothing(modelObject.FirstName) Then
paramFirstName.Value = System.DBNull.Value
Else
paramFirstName.Value = modelObject.FirstName
End If
If IsNothing(modelObject.Email) Then
paramEmail.Value = System.DBNull.Value
Else
paramEmail.Value = modelObject.Email
End If
If IsNothing(modelObject.Notes) Then
paramNotes.Value = System.DBNull.Value
Else
paramNotes.Value = modelObject.Notes
End If
paramBillingEqualsShipping.Value = modelObject.BillingEqualsShipping
If IsNothing(modelObject.BillingLastName) Then
paramBillingLastName.Value = System.DBNull.Value
Else
paramBillingLastName.Value = modelObject.BillingLastName
End If
rowsAffected = (rowsAffected + command.ExecuteNonQuery)
Loop
Finally
connection.Close
CType(connection,System.IDisposable).Dispose
End Try
Return rowsAffected
End Function
It is painful to write the DAL code by hand, but you will have full control of your DAL, SQL and Mapping code and changing any of those will be a breeze in the future.
If you don't feel like to write all the DAL Code by hand, you can get a CodeGenerator like Orasis Mapping Studio to generate exactly the same code shown without writing anything. You just need to build your SQL in the tool, map the properties to the paramaters and you are done. It will generate all the rest for you.
Good luck and happy DAL coding!
I'm with Stephen Wrighton. There are a LOT of variables here, and a lot of unanswered questions. If it's SQL, is it even a Microsoft dialect of SQL? Is it Oracle? MySQL? Something else?
In any event, my personal preference is to avoid building SQL in an application if I can, and invoke a stored procedure, even for inserts and updates. Then I pass the arguments for the procedure to the ADO.NET command object. I have this insane idea in my head that SQL belongs in the database. Perhaps that comes from all that time I spent debugging horrifically written ASP code that spliced SQL strings together back in the Dot Com era. (Never again.)
If you feel it's absolutely necessary to do so, meet the System.Text.StringBuilder class. Learn it. Love it. Make it your best friend.
UPDATE:
Seeing your response, I see now that you are working with SQL Server. That makes things much better.
I'd recommend separating your SQL code into a separate class, away from the actual business class. Some might not agree with that, but it will keep the PURPOSE of the classes clear. (See Separation of Concerns.)
You want to have your business object handle the business logic, and a separate class that handles the work of getting data into and out of the database. That way, if you have a problem with the serialization logic, you have a far better idea of where to look, and your chances of hosing the business logic are greatly reduced. It also makes your application much easier to understand.
A little up front effort in writing a few more classes has a HUGE payoff down the road.
But that's just my opinion.
I prefer the idea of Mike Hofer, to have a Stored Proc in the SQL Server side to handle the actual data updates, and having a separate class to wrap calls to those stored procs.
Just my 0.02$
Not quite sure what the OP is asking.
You need to define exactly what you are doing in the "Save" method
If you are creating a new record in the Save method you need to use an INSERT statement.
If you are updating an existing record in the Save method then you need to use an UPDATE statement.
"Save" methods generally imply that both cases are handled by the procedure.
A better method would be to have ("Create" or "Insert") and ("Update" or "Save") methods.
Or perhaps have one procedure which handles both.