Why is my function not CLS-compliant? - vb.net

I'm getting the following warning message...
Return type of function 'ConnectionNew' is not CLS-compliant.
...for this function:
Public Function ConnectionNew(ByVal DataBaseName As String) As MySqlConnection
Dim connection As MySqlConnection = Nothing
connection = getConnection(DataBaseName())
Return connection
End Function
What does this message mean, and how can I fix it?

It is because you are returning an object of a type that's not CLS compliant. Nothing you can do about that, you didn't write the type. Just acknowledge that you know that it isn't compliant, it isn't otherwise likely to cause any problems. Unless you use the function in another language that doesn't support all the .NET types. Fix:
<CLSCompliant(False)> _
Public Function ConnectionNew(ByVal DataBaseName As String) As MySqlConnection
'' etc...
End Function

Related

Implicit conversion from object to integer and other errors

I have two Layes Classes Business Layer And Data Layer And i have The Main class i called DatabaseManager contain all functions i need for stored procedures
I search on these errors I cannot find solutions
First Error in DatabaseManager class is :
implicit conversion from object to integer
Public Function ExecuteScalar(ByVal sql As String) As Object
Dim cmd As New SqlCommand(sql) With {
.CommandType = CommandType.Text,
.Connection = Connection
}
Dim retval As Integer = ExecuteScalar(cmd)
Return retval
End Function
In Data Layer Class i have this code :
Friend Function Get_Last_Visits_Type(ByRef cmd As SqlCommand)
Dim retval As Integer
cmd = New SqlCommand("Get_Last_Visits_Type")
retval = dm.ExecuteScalar(cmd)
Return retval
End Function
I got two errors here
function without an 'as' clause return type of object assumed
And
implicit conversion from object to integer
When Form Loaded i put this code on Load action :
TxtVisitTypeID.Text = Val(p.Get_Last_Visits_Type)
And i got this error :
implicit conversion from Double to String
Thanks...
Quite a few problems here as mentioned in comments:
Avoid naming a function anything that is a reserved word in the scope of your project at the very least: ExecuteScalar is a method of SqlCommand so use something like MyExecuteScalar instead.
Dim retval As Integer = ExecuteScalar(cmd) probably should be Dim retval As Integer = cmd.ExecuteScalar() unless you want a recursion (which I doubt). (Refer 1.)
Turn Option Strict on in your project settings. As mentioned, ALWAYS have this on. (And I prefer to have Option Explicit on and Option Infer off as well for similar reasons.)
With compile options set as in 3. you will have (valid) compilation errors pertaining to type conversion (at least), with a good chance of resulting in working code once you fix them. Eg Dim retval As Integer = Ctype(cmd.ExecuteScalar(), Integer) (if you're sure that the result of the query will be Integer, otherwise you will need to test and/or error trap).
Connection isn't defined anywhere: .Connection = Connection. You don't pass it nor declare it.
Since retval is declared as an Integer then the return type can also be tightened up to Integer as well, rather than Object.
Your second function has no return type.
What is dm? Not declared/defined.
Consider using Using blocks to close-and-dispose of SQL connection and command on exit.
CommandType.Text is the default so you only need to state it by way of explanation.
Here's what I'd do with your first function:
Public Function MyExecuteScalar(ByVal sql As String) As Integer
Try
Using con As New SqlConnection(sql)
Using cmd As New SqlCommand(sql, con)
Return CType(cmd.ExecuteScalar(), Integer)
End Using
End Using
Catch
Return -1 ' Or something recognizable as invalid, or simply Throw
End Try
End Function
Addressing the first function:
This code is too abstract to be useful. The name of the function is bad. It appears to be recursive but the value you pass back to the function is not a string but a command. If the line of code is Dim retval As Integer = cmd.ExecuteScalar(), then .ExecuteScalar() returns an Object. You cannot convert an Object to an Integer without a conversion method. If you are declaring retval as an Integer why would you have typed your function As Object? I won't even get into the connection problem here. I suggest you delete the function and start again.
Addressing the second function:
Why are you passing the command ByRef? This function has no connection at all! How do you expect it to execute anything? Same problem with retval As Integer and ExecuteScalar returning an Object.
Again, delete and start again.
Now to the code in Form.Load:
Val went out with VB6. I can give unanticipated results. Guess what, Val returns a Double. A .Text property expects a string. Also you appear to be calling the function you showed us above. That function asks for the calling code to provide an argument, namely an SqlCommand object.
My suggestions:
Forget about layers and try to learn the basics of data access with ADO.net. Turn on Option Strict now and forever. Ask new questions with a single problem. Tell us what line the error occurs on. You have been advised before that functions require a datatype but it doesn't seem to sink in.

Erroneous Oracle numeric or value error

I have an ongoing issue in which a windows service gets this Oracle error:
Message: ORA-06502: PL/SQL: numeric or value error: character to number conversion error
However, I am sure that I am passing a numeric value to Oracle. Working with our Oracle DBA and an Oracle support ticket we were able to capture what the Oracle server receives. My problem is with bind variable #10. The value Oracle reports in the trace log on the server is "A#Y8". However, the value I am actually sending is 167569173.
The Oracle value “A#Y8” does not look familiar – it’s not the value from another field. It is always the same value in the Oracle trace file – always A#Y8. But the numeric value that I am sending is different for each record.
The service pulls data every few minutes from an external source and inserts it into Oracle. It works for about two days and then I get the ORA-06502. Once I get the Oracle error I keep getting it on every pass until I restart the service. When the service restarts it works property for a few days – even though it is pulling the same data that was causing the error.
The Windows Service is written in VB.Net.
When I read the data I put the field into nullable Integer –
Int32?.
I send this variable to an Oracle package which throws the
ORA-06502 and the trace value show the value of "A#Y8".
I then log the value of the variable and my log shows it as numeric
(it logs as 167569173).
The conversion error happens on the call to the procedure. That is, when trying to pass the parameter to the procedure no on the actual insert within the procedure.
Somehow, the Integer value in my code is not an Integer when Oracle receives it. There appears to be some corruption but I don’t think it is in Vb.Net. My field is an integer and would not hold the value Oracle reports.
If the field did hold that value then it should log that value when I log it.
How can I see what value the Oracle client is sending to the Oracle server? I cannot find any relevant client logs.
Any ideas on what could be causing this?
Here is the Oracle trace entry:
Bind#10
oacdty=01 mxl=32(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=03 fl2=1000010 frm=01 csi=178 siz=0 off=480
kxsbbbfp=9fffffffbed31848 bln=32 avl=06 flg=01
value="A#Y8"
EXEC #11529215044982021440:c=0,e=1300,p=0,cr=34359738368,cu=0,mis=0,r=0,dep=0,og=1,plh=0,tim=2656312500288
ERROR #4:err=6502 tim=2656312500311
Here is my code:
This is the method that inserts into the database - it throws the error:
Public Shared Sub AddNewUploadedDocument(document As ManualUploadDocument)
Using conn As OracleConnection = DataFactory.GetConnection()
Using command As New OracleCommand("OM.EXTERNAL_DOCUMENT_OBJECTS.insertUploadedDocuments", conn)
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add("tSelecteeID", document.SelecteeID)
command.Parameters.Add("tFileName", document.FileNameGUID)
command.Parameters.Add("tFileType", document.FileType)
command.Parameters.Add("tOriginalFileName", document.OriginalFileName)
command.Parameters.Add("tOriginalFileType", document.OriginalFileType)
command.Parameters.Add("tDocTypeID", DBNull.Value)
command.Parameters.Add("tStatus", document.Status.ToString())
command.Parameters.Add("tSource", Integer.Parse(document.Source))
command.Parameters.Add("tSubmitted", Convert.ToInt32(document.Submitted))
command.Parameters.Add("tIsStaffingConversion", Convert.ToInt32(document.IsStaffingConversion))
command.Parameters.Add("tStaffingDocumentationID", document.StaffingDocumentationID)
command.ExecuteNonQuery()
End Using
'close connection
conn.Close()
End Using
End Sub
After this, I call this method to log the values:
Private Shared Function getInsertAttemptFields(document As OnBoardingDataObjects.ManualUploadDocument) As String
Dim sb As New StringBuilder()
sb.AppendLine("Insert Fields")
sb.Append("tSelecteeID:")
sb.AppendLine(document.SelecteeID)
sb.Append("tFileName:")
sb.AppendLine(document.FileNameGUID)
sb.Append("tFileType:")
sb.AppendLine(document.FileType)
sb.Append("tOriginalFileName:")
sb.AppendLine(document.OriginalFileName)
sb.Append("tOriginalFileType:")
sb.AppendLine(document.OriginalFileType)
sb.Append("tDocTypeID:")
sb.AppendLine("<null>")
sb.Append("tStatus:")
sb.AppendLine(document.Status.ToString())
sb.Append("tSource:")
sb.AppendLine(Integer.Parse(document.Source))
sb.Append("tSubmitted:")
sb.AppendLine(Convert.ToInt32(document.Submitted))
sb.Append("tIsStaffingConversion:")
sb.AppendLine(Convert.ToInt32(document.IsStaffingConversion))
sb.Append("tStaffingDocumentationID:")
sb.AppendLine(document.StaffingDocumentationID)
Return sb.ToString()
End Function
It is the same object that I pass to both methods. The StaffingDocumentationID field is the one that the Oracle trace shows as A#Y8 and the log shows as 167569173.
Here is the definition of the manualUploadDocument object that I am passing around. For brevity, I remove the code in some methods - the properties are what is relevant here and the removed code does not reference them.
<Serializable()>
Public Class ManualUploadDocument
Private _Submitted As Boolean = False
Public Property DocumentUploadID As Integer
Public Property SelecteeID As Integer
Public ReadOnly Property FileName As String
Get
Return FileNameGUID + "." + FileType
End Get
End Property
Public Property FileType As String
Public Property OriginalFileName As String = ""
Public Property OriginalFileType As String = ""
Public Property DocumentTypeID As Integer
Public Property DocumentType As String = ""
Public Property UploadDate As Date
Public Property FileNameGUID As String
Public Property Status As PublicEnums.ManualUploadStatus
Public Property Source As PublicEnums.ManualUploadSource
Public Property IsMarkedFortransmission As Boolean = False
Public Property TransmissionStatusDescription As String
Public Property IsStaffingConversion As Boolean = False
Public Property StaffingDocumentationID As Int32?
Public Property Submitted As Boolean
Get
Return _Submitted
End Get
Set(value As Boolean)
_Submitted = value
End Set
End Property
Public Shared Function getStatusDisplayName(status As PublicEnums.ManualUploadStatus) As String
Dim name As String = ""
'Some code here
Return name
End Function
Public Shared Function getStatusDescription(status As PublicEnums.ManualUploadStatus) As String
Dim val As String = ""
'Some code here
Return val
End Function
End Class
Well, I don't see any error in the provided codes. So, there should be some errors in OM.EXTERNAL_DOCUMENT_OBJECTS.insertUploadedDocuments stored procedure. In any case, I have three recommendations:
1- Make sure the stored procedure OM.EXTERNAL_DOCUMENT_OBJECTS.insertUploadedDocuments is correct.
2- Use a try catch block in AddNewUploadedDocument, like:
Public Shared Sub AddNewUploadedDocument(document As ManualUploadDocument)
Using conn As OracleConnection = DataFactory.GetConnection()
Using command As New OracleCommand("OM.EXTERNAL_DOCUMENT_OBJECTS.insertUploadedDocuments", conn)
Try
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add("tSelecteeID", document.SelecteeID)
command.Parameters.Add("tFileName", document.FileNameGUID)
command.Parameters.Add("tFileType", document.FileType)
command.Parameters.Add("tOriginalFileName", document.OriginalFileName)
command.Parameters.Add("tOriginalFileType", document.OriginalFileType)
command.Parameters.Add("tDocTypeID", DBNull.Value)
command.Parameters.Add("tStatus", document.Status.ToString())
command.Parameters.Add("tSource", Integer.Parse(document.Source))
command.Parameters.Add("tSubmitted", Convert.ToInt32(document.Submitted))
command.Parameters.Add("tIsStaffingConversion", Convert.ToInt32(document.IsStaffingConversion))
command.Parameters.Add("tStaffingDocumentationID", document.StaffingDocumentationID)
command.ExecuteNonQuery()
Catch Ex as Exception
Log (document.StaffingDocumentationID)
Log (Ex.StackTrace)
End Try
End Using
'close connection
conn.Close()
End Using
End Sub
3- If everything else failed, rewrite AddNewUploadedDocument using sql statements instead of stored procedure.

Is there an elegant way to write this code?

I've inherited some code and it is making me cringe when I look at it. Is there more elegant way to write the following?
Dim myItem As DTO.MyBaseClass = Nothing
Dim myType As String = GetTypeString()
Select Case myType
Case Is = "Case1"
myItem = Bus.BusManager(Of DTO.MyClass1).Read()
Case Is = "Case2"
myItem = Bus.BusManager(Of DTO.MyClass2).Read()
'... etc etc for 30 lines
Is there a way to make a map from the string to the class type and then just have a line like so? Or something similar?
myItem = Bus.BusManager(Of MappingDealy(myType)).Read()
Since BusManager is a Generic, the type you pass into Of <type> must be specified at compile time. It's not like a traditional parameter that you can change at runtime.
It's unclear from the code you listed what BusManager actually does. If all it's doing is creating an instance of the Generic type, then maybe the person who created it doesn't really understand generics. Do you have the ability to rework how BusManager works, or are you limited to using it as is?
As #jmoreno mentioned, you can use reflection to create an instance of a type from a string containting the name of the type. Here's how that would work:
Imports System.Reflection
Imports System.IO
Public Class ObjectFactory
Private Shared Function CreateObjectFromAssembly(ByVal assembly As Assembly, ByVal typeName As String) As Object
' resolve the type
Dim targetType As Type = assembly.GetType(typeName)
If targetType Is Nothing Then
Throw New ArgumentException("Can't load type " + typeName)
End If
' get the default constructor and instantiate
Dim types(-1) As Type
Dim info As ConstructorInfo = targetType.GetConstructor(types)
Dim targetObject As Object = info.Invoke(Nothing)
If targetObject Is Nothing Then
Throw New ArgumentException("Can't instantiate type " + typeName)
End If
Return targetObject
End Function
Public Shared Function CreateObject(ByVal typeName As String) As Object
Return CreateObjectFromAssembly(Assembly.GetExecutingAssembly, typeName)
End Function
Public Shared Function CreateObject(ByVal typeName As String, ByVal assemblyFileName As String) As Object
Dim assemblyFileInfo = New FileInfo(assemblyFileName)
If assemblyFileInfo.Exists Then
Return CreateObjectFromAssembly(Reflection.Assembly.LoadFrom(assemblyFileName), typeName)
Else
Throw New ArgumentException(assemblyFileName + " cannot be found.")
End If
End Function
End Class
In a production app, I'd probably set the return type for all of these methods to my base class or interface. Just make sure you pass in the full typeName including the Namespace.
With that factory class in place, then the elegant version of your code would look something like this:
Dim myItem as DTO.MyBaseClass = ObjectFactory.CreateObject("DTO." & GetTypeString())
First of all, never use Case Is = and never initialize to Nothing. So a quick one would be:
Dim myItem As DTO.MyBaseClass
Select Case GetTypeString()
Case "Case1"
myItem = Bus.BusManager(Of DTO.MyClass1).Read()
' etc etc
But since you're using templating, there's really no way to map it unless you want to use reflection, which is horribly inefficient at the expense of slightly cleaner and shorter code. You could also add an Imports DTO to save on 124 characters, and also Bus to save on another 120 characters.
Without seeing more of the code I would recommend use an Enumeration on my Case Statement to prevent minor bugs from appearing.
You could then either use a Factory Method to process the data based on the Enumeration.

What did VB replace the function "Set" with?

I've found several aspx codes for forms which include the use of a "Set" function. When I try them out on the hosting server, I get an error message that "Set is no longer supported". Anyone know what replaced the "Set" command?
More specifically, how do I change this:
Dim mail
Set mail = Server.CreateObject("CDONTS.NewMail")
mail.To = EmailTo
mail.From = EmailFrom
mail.Subject = Subject
mail.Body = Body
mail.Send
to be VB.NET compatible?
If you mean the VB6 syntax
Set obj = new Object
then you can simply remove the Set
obj = new Object()
Set is a keyword in VB6, with the intrudction of VB.NET the keyword, as used in this context, was removed.
Formerly, Set was used to indicate that an object reference was being assigned (Let was the default). Because default properties no longer are supported unless they accept parameters, these statements have been removed.
Module Module1
Sub Main()
Dim person As New Person("Peter")
Dim people As New People()
people.Add(person)
'Use the default property, provided we have a parameter'
Dim p = people("Peter")
End Sub
End Module
Public Class People
Private _people As New Dictionary(Of String, Person)
Public Sub Add(ByVal person As Person)
_people.Add(person.Name, person)
End Sub
Default Public ReadOnly Property Person(ByVal name As String) As Person
Get
Return _people(name)
End Get
End Property
End Class
Public Class Person
Private _name As String
Public Sub New(ByVal name As String)
_name = name
End Sub
Public ReadOnly Property Name() As String
Get
Return _name
End Get
End Property
End Class
Some things to remember for .Net:
NEVER use Server.CreateObject() in .Net code. Ever.
NEVER Dim a variable without giving it an explicit type. Except for new Option Infer linq types
NEVER use the Set keyword. Except when defining a property.
In fact, in .Net you can get rid probably of the CDONTS dependancy entirely, as .Net has a built-in mail support:
Dim smtp As New System.Net.SmtpClient()
Dim message As New System.Net.MailMessage(EmailFrom, EmailTo, Subject, Body)
smtp.Send(message)

Writing a cache function in VB.NET

I'm still learing VB.NET and usually I just google my questions, but this time I really don't know what to look for so I'll try here.
Trying to write a function that takes the cache key as a parameter and returns the cached object. No problems there, but I can't figure out how to pass the type into the function to use with TryCast, so that I don't have to do that with the returned result.
Here is my function so far, the ??? is to be replaced with the type that is passed into the function somehow.
Public Function GetCache(ByVal tag As String) As Object
Dim obj As Object = Nothing
Dim curCache As Object = TryCast(System.Web.HttpContext.Current.Cache(tag), ???)
If Not IsNothing(curCache) Then
Return curCache
Else
Return Nothing
End If
End Function
Am I doing this completely wrong or am I just missing something?
Use a generic:
Public Function GetCache(Of T)(ByVal tag As String) As T
Return CType(System.Web.HttpContext.Current.Cache(tag), T)
End Function
update:
Edited to use CType, because trycast only works with reference types. However, this could throw an exception if the cast fails. You can either handle the exception, check the type before making the cast, or limit your code to reference types like this:
Public Function GetCache(Of T As Class)(ByVal tag As String) As T
Return TryCast(System.Web.HttpContext.Current.Cache(tag), T)
End Function