I'm new to visual basic so forgive me in advance.
I'm trying to reference a data table from a module and I get “reference not a shared member requires object” error. I can’t figure out why.
Public Module myFuncs
Public Function get_name()
Dim lotto As Integer
Dim counter As Integer
Dim first As String
Dim last As String
Dim fullname As String
counter = Database11DataSet.Tbl_names.Rows.Count
lotto = Int((counter - 1 + 1) * Rnd() + 1)
Dim nameq = From n In Database11DataSet.Tbl_names.AsEnumerable()
Where n.ID = lotto
Select n
For Each n In nameq
first = n.first
Next
lotto = Int((counter - 1 + 1) * Rnd() + 1)
… 'does the same for the last name
Return fullname
End function
As you mentioned, your problems were on the lines that used:
Database11DataSet.Tbl_names
Within your code, Database11DataSet appears to be a type rather than an object; the only methods that can be called on a type are shared (or, depending upon the language, static). From http://en.wikipedia.org/wiki/Method_(computer_programming)#Static_methods:
Static methods neither require an instance of the class nor can they implicitly access the data (or this, self, Me, etc.) of such an instance. A static method is distinguished in some programming languages with the static keyword placed somewhere in the method's signature.
In your case, you should be able to resolve this by creating an instance of Database11DataSet at the top of your code, and then using that in place of the type:
Public Function get_name()
...
Dim data as new Database11DataSet()
counter = data.Tbl_names.Rows.Count
lotto = Int((counter - 1 + 1) * Rnd() + 1)
Dim nameq = From n In data.Tbl_names.AsEnumerable()
Where n.ID = lotto
Select n
...
End Function
Related
I am new to VB. I read online that in order to return from a function, you do something as follows -
Private Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
Dim Res as integer
Res = x + y
Add = Res ' use the function's name
End Function
My question is, does this syntax also work for user defined types? If not, what is the syntax. I tried the following -
Public Function getDetails() As clsDetails
Dim details As clsDetails
Set details = New clsDetails
With details
.X = "R"
.Y = "N"
.Z = "N"
' more code follows
End With
getDetails = details 'gives error-> object variable or with block variable not set
End Function
But this gives me an error on the above line - "object variable or with block variable not set".
What am I doing wrong here?
I suppose clsDetails is not a UDT, but a class. For variables defined as objects you need to use the SET keyword. I.e.:
set getDetails = details
For details using UDTs as function return values or parameters, see: User Defined Type (UDT) as parameter in public Sub in class module (VB6).
// function definition
Public Function add(a, b)
Dim c As integer
c=Val(a) + Val(b)
add=c
End Function
// function calling
x=Text1.Text
y=Text2.Text
z=add(x, y)
MsgBox (z)
I need to pass 8 database column dates to my CalculateMostRecentDate function since vb6 doesnt have a max function. Am i simply able to pass the database values like this? or how would I do it?
Public Function CalculateMostRecentDate(ParamArray dates() As Variant) As Variant
Dim i As Integer
Dim MostRecentDate As Variant
MostRecentDate = dates(LBound(dates))
For i = LBound(dates) + 1 To UBound(dates)
If MostRecentDate < dates(i) Then MostRecentDate = dates(i)
Next i
CalculateMostRecentDate = MostRecentDate
End Function
Call function within another function:
RECENT_APPRV_DT = CalculateMostRecentDate(EMPLOYER.UW_APPRV_DT, EMPLOYER.BE_APPRV_DT, . . .)
Looks about right.
Google turned up this which looks quite polished, also this from the old VB6 newsgroup.
The function looks OK but you cannot call it the way you have indicated. You will need to load the dates into an array and then pass the array in.
When using a Linq-to-SQL class, how can I make a simple copy of an entity and save it?
My entity has a guid for a unique ID that gets automatically generated in the SQL Server.
I don't require a "deep clone".
I tried to use some clone methods that are out there but I couldn't figure out how to get everything serialized that needed to be serialized (got stuck on the DataContext not being serializable).
Can I just get an entity, detach it from the DataContext, null out the unique ID and InsertOnSubmit in a new DataContext? If so, how would I do this?
VB.net code preferred but not required.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UPDATE:
Public Shared Function ReIssue(RequestID As Guid) As Guid
Dim req As Request
Dim new_req As Request
Using dc1 As New MBDataContext()
req = (From r In dc1.Requests Where r.ID = RequestID).Single()
End Using
new_req = req
new_req.ID = Guid.Empty
new_req.CreateDate = Nothing
Using dc2 As New MBDataContext()
dc2.Requests.InsertOnSubmit(new_req)
dc2.SubmitChanges()
End Using
End Function
I get an error: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.
on this line: dc2.Requests.InsertOnSubmit(new_req)
Nulling out the unique id and then calling InsertOnSubmit is the right way to go. Some things you have to consider though:
What is the type of the id? Is it an int? A Guid? Is it nullable? If it is nullable, make sure to set it to null, if it is an int, then to 0, or a Guid, then to Guid.Empty.
Does the type have a timestamp of some kind? If so, then you have to reset/set it to null as well, depending on the type.
Once you've done that, you can call InsertOnSubmit and then SubmitChanges and the change should take place.
Note, if you are doing this for a large number of records, you are better off writing a stored procedure which will perform the insert into the table using a select from the other table. It will be much faster that way (you won't be loading the data from the database into memory then pushing it back, inserting the records one at a time).
This method seems to have worked perfectly.
Making the final code look like this:
Public Shared Function ReIssue(RequestID As Guid) As Guid
Using dc As New MBDataContext()
Dim req As Request
req = (From r In dc.Requests Where r.ID = RequestID).Single()
Dim new_req As Request = DirectCast(Entity.Copy(req, New Request()), Request)
dc.Requests.InsertOnSubmit(new_req)
dc.SubmitChanges()
req.ActiveRequestParentID = new_req.ID
dc.SubmitChanges()
Return new_req.ID
End Using
End Function
Public NotInheritable Class Entity
Private Sub New()
End Sub
Public Shared Function Copy(source As Object, destination As Object) As Object
Dim sourceProps As System.Reflection.PropertyInfo() = source.[GetType]().GetProperties()
Dim destinationProps As System.Reflection.PropertyInfo() = destination.[GetType]().GetProperties()
For Each sourceProp As System.Reflection.PropertyInfo In sourceProps
Dim column As ColumnAttribute = TryCast(Attribute.GetCustomAttribute(sourceProp, GetType(ColumnAttribute)), ColumnAttribute)
If column IsNot Nothing AndAlso Not column.IsPrimaryKey Then
For Each destinationProp As System.Reflection.PropertyInfo In destinationProps
If sourceProp.Name = destinationProp.Name AndAlso destinationProp.CanWrite Then
destinationProp.SetValue(destination, sourceProp.GetValue(source, Nothing), Nothing)
Exit For
End If
Next
End If
Next
Return destination
End Function
End Class
String or binary data would be truncated. linq exception, cant find which field has exceeded max length.
i have around 350 fields. i checked each and every textbox maxlength with database field maxlength, everything seems to be correct, but i still get the exception.
please help
Troubleshooting this error with 350 fields can be extremely difficult, and SQL Server Profiler isn't much help in this case (finding the long string in the generated SQL is like finding a needle in a haystack).
So, here is an automated way to find the actual strings that are exceeding the database size limit. This is a solution that's out there on the internet, in various forms. You probably don't want to leave it in your production code, since the attribute/property searching is pretty inefficient, and it'll add extra overhead on every save. I'd just throw it in your code when you encounter this problem, and remove it when you're done.
How it works: it iterates over all properties on an object you're about to save, finding the properties with a LINQ to SQL ColumnAttribute. Then, if the ColumnAttribute.DbType contains "varchar", you know it's a string and you can parse that part of the attribute to find the maximum length.
Here's how to use it:
foreach (object update in context.GetChangeSet().Updates)
{
FindLongStrings(update);
}
foreach (object insert in context.GetChangeSet().Inserts)
{
FindLongStrings(insert);
}
context.SubmitChanges();
And here's the method:
public static void FindLongStrings(object testObject)
{
foreach (PropertyInfo propInfo in testObject.GetType().GetProperties())
{
foreach (ColumnAttribute attribute in propInfo.GetCustomAttributes(typeof(ColumnAttribute), true))
{
if (attribute.DbType.ToLower().Contains("varchar"))
{
string dbType = attribute.DbType.ToLower();
int numberStartIndex = dbType.IndexOf("varchar(") + 8;
int numberEndIndex = dbType.IndexOf(")", numberStartIndex);
string lengthString = dbType.Substring(numberStartIndex, (numberEndIndex - numberStartIndex));
int maxLength = 0;
int.TryParse(lengthString, out maxLength);
string currentValue = (string)propInfo.GetValue(testObject, null);
if (!string.IsNullOrEmpty(currentValue) && maxLength != 0 && currentValue.Length > maxLength)
Console.WriteLine(testObject.GetType().Name + "." + propInfo.Name + " " + currentValue + " Max: " + maxLength);
}
}
}
}
Update 12/03/2019 -
This answer is referenced on Linqpad.net website for the same error. In Linqpad (version 5) (that uses LinqToSql) the columns are no longer listed as properties instead they are fields . Use the following to iterate through fields:
foreach (FieldInfo propInfo in testObject.GetType().GetFields())
...
...
string currentValue = (string)propInfo.GetValue(testObject);
...
...
If you checked the max length of every textbox to the max length of every field, it is entirely possible the error is happening through a trigger. Are there triggers on the table?
And, hey, why not have the same solution (#shaunmartin's) in VB.Net, too? Sometimes you just gotta debug someone else's code!
Use:
For Each update as Object In context.GetChangeSet().Updates
FindLongStrings(update)
Next
For Each insert as Object In context.GetChangeSet().Inserts
FindLongStrings(insert)
Next
And core
Public Shared Sub FindLongStrings(ByVal testObject As Object)
Dim propInfo As PropertyInfo
For Each propInfo In testObject.GetType().GetProperties()
Dim attribute As ColumnAttribute
For Each attribute In propInfo.GetCustomAttributes(GetType(ColumnAttribute), True)
If attribute.DbType.ToLower().Contains("varchar") Then
Dim dbType As String = attribute.DbType.ToLower()
Dim numberStartIndex As Integer = dbType.IndexOf("varchar(") + 8
Dim numberEndIndex As Integer = dbType.IndexOf(")", numberStartIndex)
Dim lengthString As String = dbType.Substring(numberStartIndex, (numberEndIndex - numberStartIndex))
Dim maxLength As Integer = 0
Integer.TryParse(lengthString, maxLength)
Dim currentValue As String = CType(propInfo.GetValue(testObject, Nothing), String)
If Not String.IsNullOrEmpty(currentValue) AndAlso maxLength <> 0 AndAlso currentValue.Length > maxLength Then
Console.WriteLine(testObject.GetType().Name & "." & propInfo.Name & " " & currentValue & " Max: " & maxLength)
End If
End If
Next
Next
End Sub
But with a lot of data or a lot of fields this takes a lot of time for sure. The debugging side of linq is lacking - the exception thrown should tell you the field!
i set the max length for all the 350 fields. i guess thats the only way. thanks for your support.
I'm using VBA in an Access application and I would like to have a n-tuple object that contains values of different data types. Then I would like a collection of those objects.
If I was doing this in javascript it would look like:
var myStructure = {
name: "blah"
age: 33
moreStuff : "test"
};
And then I would need a collection of myStructure. How can I best accomplish this in VBA?
You can define your own variable type with code such as:
Public Type T_Person
name as string
dateOfBirth as date
....
email() as string (*)
....
End type
You can then declare a T_person type in your code with:
Dim currentPerson as T_Person
currentPerson.name = myName
currentPerson.dateOfBirth = myDate
currentPerson.email(1) = myFirstEmail
....
(*) I do not remember the details for declaring arrays in such circumstances. You might have to determine array's length when defining the variable. Please check help.
The same result can also be reached by declaring a class module named, for example, "Person". In this class module, you'll be not only able to follow the objet properties (such as name, dateOfBirth, etc), but also object events (initialisation and deletion). You'll be also able to create methods on this object. You code would then look like:
Dim myPerson as Person
set myPerson = New Person
myPerson.name = myName
myPerson.dateOfBirth = myDate
if myPerson.age > 18 then (*)
'the guy is an adult'
myPerson.createAccount
Else
'the guy is not ...'
Endif
(*) Here, age is a calculated proerty of your object, available when dateOfBirth is not null. Please google "VBA class module" to find different examples for the implementation of a class module in VBA.
Now, if you want to manage a collection of similar "objects" (here, Persons), you will have to go through the creation of a class module for your objects collection (a "Persons" class module for example) and make use of the "Collection" objet available in VBA. You will then end with 2 different class modules: Person (will hold each person's detail), and Persons (will hold the collection of Persons). You'll then be able to have code like this:
Public myPersons as Persons 'at the app level, 1 main collection'
myPersons.add .... 'adding a new person to your collection'
myPersons.count ... 'counting number of persons in myPersons'
Please google on "VBA collection object" for examples on Collection management in VBA. Check my code proposal, as this was written on the fly, and without VBA help file.
The "Class" solution is clearly more powerfull, but more complex than the "Type". Go for it if you need it. It is definitely worth the pain!
PS: I am not very happy with my namings here, as this can lead to very confusing code when dealing with the myPersons collection and myPerson instance of o Person object. I'd advise you to find a more obvious one, like "PersonCollection" and "Person", or even "Individual"
You can use Variant multidimensional arrays to store your collections. Variants can store any data type making it very versatile.
Const Name as Integer = 0
Const Age as Integer = 1
Const moreStuff as Integer = 2
Dim myStructure as Variant
Redim myStructure(0 to 2, 0 to n)
myStructure(Name, 0) = "Blah"
myStructure(Age, 0) = 33
myStructure(moreStuff, 0) = "test"
Note: You can only expand the last dimension of a multidimensional array in VBA and preserve the values, so make sure that it is for the dimension you want to scale.
That is the basic data structure and you can develop a class or functions to wrap everything up to suit your needs.
You may also want to look into the Scripting.Dictionary object, though I have read that it is considered unsafe. It is more dynamic than using Type definitions, and unlike Collection, it gives you access to the keys.
Here's a sample:
Public Function TestDictionary()
Dim d As New Scripting.Dictionary 'need reference to Microsoft Scripting Runtime
'Dim d As Object
'Set d = CreateObject("Scripting.Dictionary")
Dim k As Variant
d.Add "Banana", "Yellow"
d.Add "Apple", "Red"
d.Add "Grape", "Green"
For Each k In d.Keys
Debug.Print k; ": "; d.Item(k)
Next k
End Function
The Collection class is an option.
Dim col As New Collection
col.Add("blah", "name")
col.Add(33, "age")
col.Add("test", "moreStuff")
This gives you the most flexibility. However it isn't very efficient and the Collection class has no way to get a list of keys.