I have a stored procedure with a CLOB as an input.
I'm looking to do a refactor, so my Oracle library is not directly referenced. When I create a parameter from the object, I'm getting back an IDbDataParameter object.
Passing a string to it without defining the type seems to work fine (setting the name and value).
However, the text gets cut off at 32767, the maximum length for Oracle VARCHAR.
So, my question is, how do I set the OracleDbType to type Clob, without having to expose the assembly or use reflection. I tried using a DbType of binary and it failed. I'd rather not load the library of use reflection if there is a better way.
Here's some sample code:
''' <summary>
''' Creates a Parameter
''' </summary>
''' <param name="name"></param>
''' <param name="value"></param>
''' <returns></returns>
Public Function CreateParameter(ByVal name As String, ByVal value As String) As IDbDataParameter
Dim parameter As IDbDataParameter = m_Db.CreateParameter()
parameter.ParameterName = name
parameter.Value = value
Return parameter
End Function
And from the caller. the p_notes parameter is what I want to be a CLOB.
oDAL = New DAL.DataAccessLayer(m_BSL)
Dim parameters As New List(Of IDbDataParameter)
parameters.Add(oDAL.CreateParameter("p_no", compNo))
parameters.Add(oDAL.CreateParameter("p_sys", m_BSL.CurrentSystem.Description.ToString))
parameters.Add(oDAL.CreateParameter("p_notes", notes))
parameters.Add(oDAL.CreateParameter("p_chuser", m_BSL.CurrentUser.ToString))
oDAL.Exec_SP("P_UPDATE_MASTSTAT_NOTES", parameters.ToArray())
So is there a way to set DbType in such a way that it will assign OracleDbType to CLOB?
Related
I've seen lots of posts on here (and elsewhere) with a solution to the problem of finding the available free space on a UNC path, but these all involve using GetDiskFreeSpaceEx which only appears to be available in C.
I've included it in some test code and get:
Error BC30451 'GetDiskFreeSpaceEx' is not declared. It may be inaccessible due to its protection level.
The IDE offers the solutions of creating a method or a property.
Trying this in a console program...
What is the VB equivalent of this?
When working with P/Invoke, I usually write a class with the name of the method and not only the method declaration itself and try to expose the functionality in a .NET fashion instead of low-level C++ style.
e.g. in the method description of the native GetDiskFreeSpaceEx function it is mentioned that in case the provided path is an UNC-path the trailing backslash is mandatory!
C++ style: Write it in the description, if the caller does not provide it in that fashion, they are to blame themselves, RTFM.
.NET style: We adjust it for the caller, they do not have to worry about such implementation details.
I would also provide 3 different methods for each available information (I included a 4th one for the space used), and a common one to get more than one value at once.
Here how it could look:
Imports System
Imports System.ComponentModel
Imports System.IO
Imports System.Runtime.InteropServices
Namespace PInvoke.Kernel32
Public Class GetDiskFreeSpaceEx
''' <summary>
''' Retrieves the number of bytes currently available to the caller. This takes into account any quotas etc. that may
''' exist on the folder resp. file share.
''' </summary>
''' <param name="folderName">The name of the path to retrieve the for me available bytes from.</param>
''' <returns>The maximum number of bytes that are currently available for me to use.</returns>
Public Shared Function GetAvailableBytesToCaller(folderName As String) As UInt64
Dim result As SizeInfo = Invoke(folderName)
Return result.BytesAvailableToCaller
End Function
''' <summary>
''' Retrieves the number of bytes currently available on the according volume. This may be more than I can use,
''' see <see cref="GetAvailableBytesToCaller(String)" /> for a method that respects quotas etc.
''' </summary>
''' <param name="folderName">The name of the path to retrieve the (in general) available bytes from.</param>
''' <returns>The maximum number of bytes that are available for all users together.</returns>
Public Shared Function GetAvailableBytesInTotal(folderName As String) As UInt64
Dim result As SizeInfo = Invoke(folderName)
Return result.BytesAvailableInTotal
End Function
''' <summary>
''' Retrieves the number of bytes currently used on the according volume. This value corresponds to
''' <see cref="GetBytesInTotal(String)"/> - <see cref="GetAvailableBytesInTotal(String)"/>.
''' </summary>
''' <param name="folderName">The name of the path to retrieve the used bytes from.</param>
''' <returns>The number of bytes that are already used by all users together.</returns>
Public Shared Function GetUsedBytesInTotal(folderName As String) As UInt64
Dim result As SizeInfo = Invoke(folderName)
Return result.BytesUsedInTotal
End Function
''' <summary>
''' Retrieves the size in bytes of the according volume (the total of already used and available space).
''' </summary>
''' <param name="folderName">The name of the path to retrieve the (in general) available bytes from.</param>
''' <returns>The maximum number of bytes that are available for all users together.</returns>
Public Shared Function GetBytesInTotal(folderName As String) As UInt64
Dim result As SizeInfo = Invoke(folderName)
Return result.TotalNumberOfBytes
End Function
''' <summary>
''' Retrieves a <see cref="SizeInfo"/> object containing the information about how many bytes are available at
''' the given path in general or for the current user account, how much is the total and how much is already
''' used.
''' </summary>
''' <param name="folderName">The name of the path from which to retrieve the size info.</param>
''' <returns>The according size info object.</returns>
Public Shared Function Invoke(folderName As String) As SizeInfo
'Check argument
If (String.IsNullOrWhiteSpace(folderName)) Then
Throw New ArgumentNullException(NameOf(folderName), "The folder's name must not be null, empty or white-space!")
End If
'Expand environment variables
Try
folderName = Environment.ExpandEnvironmentVariables(folderName)
Catch ex As Exception
Throw New ArgumentException($"Unable to expand possible environment variables of folder '{folderName}'! See inner exception for details...", ex)
End Try
'Get full path
Try
folderName = Path.GetFullPath(folderName)
Catch ex As Exception
Throw New ArgumentException($"Unable to retrieve absolute path of folder '{folderName}'! See inner exception for details...", ex)
End Try
'Append final back-slash (which is mandatory for UNC paths)
folderName = folderName.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)
If (Not folderName.EndsWith(Path.DirectorySeparatorChar)) Then
folderName &= Path.DirectorySeparatorChar
End If
'Invoke method
Dim bytesAvailableToCaller As UInt64
Dim bytesInTotal As UInt64
Dim bytesAvailableInGeneral As UInt64
Dim success As Boolean = Invoke(folderName, bytesAvailableToCaller, bytesInTotal, bytesAvailableInGeneral)
If (Not success) Then Throw New Win32Exception()
'Return result
Return New SizeInfo(bytesAvailableToCaller, bytesInTotal, bytesAvailableInGeneral)
End Function
'Private Methods
<DllImport("kernel32.dll", EntryPoint:="GetDiskFreeSpaceExW", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Unicode)>
Private Shared Function Invoke(folderName As String, ByRef bytesAvailableToCaller As UInt64, ByRef bytesInTotal As UInt64, ByRef bytesAvailableInGeneral As UInt64) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
'************************************************************************************************************************
' Inner Class "SizeInfo"
'************************************************************************************************************************
Public Class SizeInfo
Public Sub New(freeBytesAvailableToCaller As UInt64, totalNumberOfBytes As UInt64, freeBytesAvailableInTotal As UInt64)
Me.BytesAvailableToCaller = freeBytesAvailableToCaller
Me.BytesAvailableInTotal = freeBytesAvailableInTotal
Me.TotalNumberOfBytes = totalNumberOfBytes
End Sub
Public ReadOnly Property BytesAvailableToCaller As UInt64
Public ReadOnly Property BytesAvailableInTotal As UInt64
Public ReadOnly Property BytesUsedInTotal As UInt64
Get
Dim total As UInt64 = TotalNumberOfBytes
Dim available As UInt64 = BytesAvailableInTotal
If (total <= available) Then Return 0
Return total - available
End Get
End Property
Public ReadOnly Property TotalNumberOfBytes As UInt64
End Class
End Class
End Namespace
I am trying my best not to use late binding with the getObject function. How ever i know i wont be looked down upon turning strict off in only One class.
My question is i can't find what to declare my member type of.
Dim restPoint = GetObject("winmgmts:\\.\root\default:Systemrestore")
If restPoint IsNot Nothing Then
If restPoint.CreateRestorePoint("test restore point system", 12, 100) = 0 Then
MsgBox("Restore Point created successfully")
Else
MsgBox("Could not create restore point!")
End If
End If
I have spent hours trying to research msdn createrestorepoint come from. I don't want to use WMI directly or keep strict off.
Thanks
I guess i will go with WMI for now. That snippet cleaned up.
Imports System.Management
Public Class CreateRestorePoint
''' <summary>
''' Defines the search query for the ManagementScope path.
''' </summary>
Private Const MANAGEMENT_SCOPE As String = "\\localhost\root\default:SystemRestore"
''' <summary>
''' Attempts to create a new restore point using WMI.
''' </summary>
''' <returns>True if the restore point was created, other wise false.</returns>
''' <remarks>
''' Gets the object containing the input parameters to a method, and then fills in the values and passes the object to the InvokeMethod call.
''' </remarks>
Friend Function CreateRestorePoint() As Boolean
Dim created As Boolean = True
Using wmiQuery As New ManagementClass(New ManagementPath(MANAGEMENT_SCOPE))
Dim query = GetParams(wmiQuery)
Try
wmiQuery.InvokeMethod("CreateRestorePoint", query, Nothing)
Catch ex As ManagementException
created = False
End Try
End Using
Return created
End Function
''' <summary>
''' Sets the ManagementBaseObject parameters.
''' </summary>
''' <param name="wmiQuery"></param>
''' <returns>Returns a ManagementBaseObject representing the list of input parameters for a method.</returns>
''' <remarks>The members of this class enable you to access WMI data using a specific WMI class path.></remarks>
Private Function GetParams(wmiQuery As ManagementClass) As ManagementBaseObject
Dim query = wmiQuery.GetMethodParameters("CreateRestorePoint")
query("Description") = "-CautionSparta"
query("RestorePointType") = 12
query("EventType") = 100
Return query
End Function
End Class
I am having some issues writing a large set of data to an XML file. I am using the following class to serialize objects to xml and then write them to disk:
''' <summary>
''' Borrowed from http://icanmakethiswork.blogspot.ca/2012/11/xsdxml-schema-generator-xsdexe-taking.html
''' </summary>
''' <typeparam name="T"></typeparam>
''' <remarks></remarks>
Public Class XMLConverter(Of T)
Private Shared serializer As XmlSerializer = Nothing
''' <summary>
''' Static constructor that initialises the serializer for this type
''' </summary>
Shared Sub New()
serializer = New XmlSerializer(GetType(T))
End Sub
''' <summary>
''' Write a node to an xmlwriter
''' </summary>
''' <param name="writer"></param>
''' <param name="itemToAppend">the object to be converted and written</param>
''' <remarks></remarks>
Public Shared Sub AppendToXml(writer As XmlWriter, itemToAppend As T)
Dim strObj As String = ToXML(itemToAppend)
strObj = XMLCleaner.CleanResult(strObj)
writer.WriteRaw(strObj)
writer.Flush()
strObj = Nothing
End Sub
''' <summary>
''' Serialize the supplied object into a string of XML
''' </summary>
''' <param name="obj"></param>
''' <returns></returns>
Public Shared Function ToXML(obj As T) As String
Dim strXml As String = ""
Using memoryStream As New MemoryStream()
serializer.Serialize(memoryStream, obj)
memoryStream.Position = 0
Using sr As New StreamReader(memoryStream)
strXml = sr.ReadToEnd()
End Using
End Using
Return strXml
End Function
End Class
Public Class XMLCleaner
'This is just for removing junk and slightly modifying the output
Public Shared Function CleanResult(result As String) As String
Dim retVal As String = Regex.Replace(result, "\sxmlns.+?"".*?""", "")
retVal = Regex.Replace(retVal, "SavedSearchRecord", "Record")
retVal = retVal.Replace("<?xml version=""1.0""?>", "")
retVal = Regex.Replace(retVal, vbCrLf, vbCrLf & " ")
Return retVal
End Function
End Class
And am calling this like so:
XMLConverter(Of SavedSearchRecord).AppendToXml(writer, record)
The issue is that memory is quickly being accumulated as I append new records to the file and ultimately results in an out of memory exception.
I've seen that not caching the serializer can result in this behaviour, but I think I've sidestepped that issue in my implementation. (Please correct me if I am wrong).
After examining a memory dump:
716821b4 28535 10497120 System.String
71682b74 140213 145562968 System.Char[]
71685670 140258 758802112 System.Byte[]
I can see that I have an enormous number of byte arrays getting stuck in memory. The data in the arrays leads me to believe that these are being stranded in memory by the ToXML function (as they contain the unmodified serialized object strings).
Given that the memory stream is in a Using block, I can't figure out why these byte arrays are not being collected by the GC.
In addition to this there also seems to be a large number of Char arrays in memory as well (about 1/5 of the memory used by the byte arrays) that are not being collected.
Can anyone tell me how to prevent this code from culminating in out of memory exceptions?
FYI code is written using .NET 4.0
I posted my question in a Microsoft forum and it was correctly answered there. I invited the answerer to post their response here, but they have not done so.
The original answer in full can be found here
Paraphrasing:
The serializer creates an assembly to serialize a loaded assembly in an AppDomain which cannot be unloaded.
The workaround was to Cache the serializer by specifying the RootAttribute (only one assembly will leak, once, which is a minimum amount of memory)
This workaround is the best if all the serialized objects are the same type and the object being serialized is not too large.
In my code, I didn't have the RootAttribute specified in the XMLSerializer constructor which was causing a new assembly to be created each time I called the serializer.
Changing from this
''' <summary>
''' Static constructor that initialises the serializer for this type
''' </summary>
Shared Sub New()
serializer = New XmlSerializer(GetType(T))
End Sub
To this:
''' <summary>
''' Static constructor that initialises the serializer for this type
''' </summary>
Shared Sub New()
serializer = New XmlSerializer(GetType(T), XmlRootAttribute(GetType(T).ToString))
End Sub
Completely resolved the leaking.
For a more in depth explanation for why this worked, please see the original answer in the link I posted above.
Please note that I later had to modify the constructor a bit for the results to make sense, but the above was the minimum required to fix the leak.
Shared Sub New()
Dim root As String = GetType(T).ToString
root = root.Substring(root.LastIndexOf(".") + 1, root.Length - root.LastIndexOf(".") - 1)
Dim rootNode As New XmlRootAttribute(root)
rootNode.Namespace = "<appropriate.xsd>"
serializer = New XmlSerializer(GetType(T), rootNode)
End Sub
From MSDN https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer(v=vs.110).aspx
To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)
If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.
I'm taking a VB.Net DLL and turning it into a Portable Class Library. Once I got all of the classes moved into the new project for the PCL, Visual Studio started throwing errors for a lot of common VB syntax that I thought would still work just fine. Some examples:
LCase
InStr
Left
Mid
On Error GoTo 0
Err
Is it possible there is just some option or include I need to have to get these to work?
You must use methods of assemblies that are supported by Portable Class Libraries (see Assemblies section). You'll be able to find equivelants to the methods that aren't working for you (ex = SubString, ToUpper, ToLower, IndexOf, etc).
When using portable to target the down-level platforms (.NET 4.0, Silverlight, Windows Phone, Xbox), we do not support the majority of the features that are exposed within Microsoft.VisualBasic.dll.
Instead, we make use of the embedded runtime feature. This embeds certain functionality that would traditionally be found in Microsoft.VisualBasic.dll, into the resulting binary itself. The features that are supported are called out on this page, under the /vbruntime* section: http://msdn.microsoft.com/en-us/library/bb531259.aspx.
When targeting .NET 4.5 & Windows Store apps only, then you do get access to the traditional Microsoft.VisualBasic.dll.
As a workaround, to help you move to portable, you can define your own module that bridges the old VB functions to their .NET equivalents:
Public Module VisualBasicBridge
Public Function LCase(value As String) As String
Return value.ToLower()
End Function
End Module
As far as On Error, I'm not aware of a good way to bridging that without providing your own implementation of Microsoft.VisualBasic, and passing that via the /vbruntime switch/msbuild property.
You can create those methods so that you don't have to update tons of your legacy code. Most of it is very straightforward, the biggest difference comes with the string functions where the legacy VB functions use a 1 based index and .Net uses a 0 index. To give you an example, here is the Mid function recreated to behave like VB (created like an extension method here):
''' <summary>
''' Simulates the same functionality provide by the traditional 1 based index Mid function.
''' </summary>
''' <param name="str"></param>
''' <param name="startPos"></param>
''' <param name="length"></param>
''' <returns></returns>
''' <remarks></remarks>
<Extension()> _
Public Function Mid(ByVal str As String, ByVal startPos As Integer, ByVal length As Integer) As String
Return str.Substring(startPos - 1, length)
End Function
Here's a few more from your list and/or commonly used ones:
''' <summary>
''' Extension to the Visual Basic Left function
''' </summary>
''' <param name="str"></param>
''' <param name="length"></param>
''' <returns></returns>
''' <remarks></remarks>
<Extension()> _
Public Function [Left](ByVal str As String, ByVal length As Integer) As String
Return str.Substring(0, length)
End Function
''' <summary>
''' Extension to the Visual Basic Right function
''' </summary>
''' <param name="str"></param>
''' <param name="length"></param>
''' <returns></returns>
''' <remarks></remarks>
<Extension()> _
Public Function [Right](ByVal str As String, ByVal length As Integer) As String
Return str.Substring(str.Length - length, length)
End Function
''' <summary>
''' Determines whether a string is a numeric value. This implementation uses Decimal.TryParse to produce it's value.
''' </summary>
''' <param name="str"></param>
''' <returns></returns>
''' <remarks></remarks>
<Extension()> _
Public Function [IsNumeric](str As String) As Boolean
Dim result As Decimal = 0
Return Decimal.TryParse(str, result)
End Function
<Extension()> _
Public Function LCase(str As String) As String
Return str.ToLower
End Function
All think those methods are in Microsoft.VisualBasic namespace. You can replace them with standard ones:
LCase => string.ToLower()
InStr => string.IndexOf()
....
Replace "on error" with regular try/catch
Best regards
I got a stored procedure that reads a table and insert those data to antoher table. That's the way how it works because the first table imports data from excel using a package with SSIS.
In EF4 I imported the SP and create function import:
This SP has 2 IN variables and 2 OUT varibales.
The IN varibales are parameters and OUT variables are a message and the number of records created.
I put the code generated:
#Region "Function Imports"
''' <summary>
''' No Metadata Documentation available.
''' </summary>
''' <param name="parIDPoliza">No Metadata Documentation available.</param>
''' <param name="parFechaActual">No Metadata Documentation available.</param>
''' <param name="varError">No Metadata Documentation available.</param>
''' <param name="varKontador">No Metadata Documentation available.</param>
Public Function spCargaArchivos(parIDPoliza As Nullable(Of Global.System.Int64), parFechaActual As Nullable(Of Global.System.DateTime), varError As ObjectParameter, varKontador As ObjectParameter) As Integer
Dim parIDPolizaParameter As ObjectParameter
If (parIDPoliza.HasValue) Then
parIDPolizaParameter = New ObjectParameter("parIDPoliza", parIDPoliza)
Else
parIDPolizaParameter = New ObjectParameter("parIDPoliza", GetType(Global.System.Int64))
End If
Dim parFechaActualParameter As ObjectParameter
If (parFechaActual.HasValue) Then
parFechaActualParameter = New ObjectParameter("parFechaActual", parFechaActual)
Else
parFechaActualParameter = New ObjectParameter("parFechaActual", GetType(Global.System.DateTime))
End If
Return MyBase.ExecuteFunction("spCargaArchivos", parIDPolizaParameter, parFechaActualParameter, varError, varKontador)
End Function
#End Region
But when I try to execute the SP I get error:
The parameter at index 2 in the parameters array is null
Execution
Dim varMensaje As Objects.ObjectParameter = Nothing
Dim varError As Objects.ObjectParameter = Nothing
Dim varRespuesta As Integer = varEntidades.spCargaArchivos(parIDPoliza, Now.Date, varError, varMensaje)
Any suggestion??
Finally I could solve this.... after two hard days.
I don't know if it's a bug or not but the error is how to send OUTPUT variables.
The types are the same that Function Import in Model Browser properties
Dim varTotalRegistros As Objects.ObjectParameter = New Objects.ObjectParameter("varKontador", GetType(Global.System.Int64))
Dim varError As Objects.ObjectParameter = New Objects.ObjectParameter("varError", GetType(Global.System.String))
Dim varRespuesta As Integer = varEntidades.spCargaArchivos(parIDPoliza, Now.Date, varError, varTotalRegistros)
Good luck!!