|
I don't think the problem is with the WriteRaw method.
When GetChars first returns, the value of retval is 1265918. However, the outChar array is only filled up to half of that amount (up to index 632958). I know this because all the values past that index are null (0x00). Also, when I convert the outChar char array to a string and remove the null values, the length of the resulting string is 632959.
On the second call to GetChars, the value of retval is 632959, but the outChar array is only filled up to index 316478.
And so on and so forth.
So again, my question is why does GetChars not fill the entire outChar array?
|
|
|
|
|
No idea why it's not filling the buffer, but creating two new strings on every pass through the loop is going to have a noticeable impact on your method's performance.
Since the real problem seems to be the performance of the code, it's got to be worth trying!
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Possibly, but how would I determine the index in the outChar array where the null values begin? I don't feel comfortable assuming that it will always be half what retval returns. That seems to be asking for trouble. And looping through the entire outChar array until I encounter a null (0x00) value would probably be just as inefficient as converting the outChar array to a string and then stripping the null values, right?
|
|
|
|
|
Are you expecting to get null characters returned from the database?
As far as I can see, the only reason you're getting null characters in your string is because you're ignoring the retval value, and creating a string that represents the entire array. Since all of the array entries beyond retval will be set to the null character, you're ending up with a long list of nulls on the end, which you're then having to remove.
With your current code, you could use:
rawStr = New String(outChar, 0, retval)
which would avoid adding those null characters to the string in the first place.
With my version, the line:
myxml.WriteRaw(outChar, 0, retval)
also avoids writing null characters to the output.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
You've touched upon the source of my problem, though. The value of retval is incorrect.
For example, the first time GetChars returns, it puts the value 1265918 into retval. However, if I actually try to access the value of the outChar array at index 1265917, I get a null value. This is because GetChars does not actually seem to be buffering as much as it says it does. In fact, it is only buffering up to index 632958.
GetChars is only filling my buffer array to half of what it says it did. The value that GetChars returns seems to be the actual size of the entire CLOB, not the number of characters that it filled the buffer with.
|
|
|
|
|
OK, so try an Array.IndexOf call to find the index of the first null character:
retval = reader.GetChars(0, startIndex, outChar, 0, bufferSize)
Do Until retval = 0
Dim nullIndex = Array.IndexOf(outChar, Chr(0), 0, retval)
If nullIndex = -1 Then nullIndex = retval
myxml.WriteRaw(outChar, 0, nullIndex)
myxml.Flush()
startIndex += retval
Array.Clear(outChar, 0, retval)
retval = reader.GetChars(0, startIndex, outChar, 0, bufferSize)
Loop
That will perform a linear search on the array, but it will be much faster than creating a string containing a complete copy of the array, then iterating through every character in the string, copying every non-null character to a new string.
If the retval value is incorrect, you might also need to adjust the increment of the startIndex variable; otherwise, you might be skipping characters. You'd need to compare the final output with the database column to see if that's the case.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
But the very first value of retval with a startIndex of 0 is incorrect... I know this because the first call to GetChars does not fill the outChars as far is retval says it did. Given this, how could retval possibly be correct for any other value of startIndex?
|
|
|
|
|
One other thing that's worth considering: the System.Data.OracleClient assembly / namespace has been deprecated by Microsoft:
The types in System.Data.OracleClient are deprecated and will be removed in a future version of the .NET Framework.
There is a newer alternative from Oracle called ODP.NET[^], which is still supported. There are two NuGet packages available:
You might also want to install the developer tools[^].
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
OK, one more suggestion!
Try using the GetOracleLob method[^] instead. This will return an OracleLob object[^] which you can use to read the value.
Using lob As OracleLob = reader.GetOracleLob(0)
Using sr As New StreamReader(lob, Text.Encoding.Unicode)
retval = sr.Read(outChar, 0, bufferSize)
Do Until retval = 0
myxml.WriteRaw(outChar, 0, retval)
myxml.Flush()
Array.Clear(outChar, 0, retval)
retval = sr.Read(outChar, 0, bufferSize)
Loop
End Using
End Using
You might need to adjust the encoding passed to the StreamReader constructor. The GetChars method uses Unicode by default, but I suspect your string might be in a different encoding.
EDIT: Looking at the code in dotPeek, this will be much more efficient than calling GetChars :
- The
GetChars method creates a new OracleLob on each call, and reads its Value property. - The
Value property reads the entire string into memory on each call. - The
GetChars method then copies the string to a Char array. - The
GetChars method then copies the specified section of the array to the buffer you've passed in.
This is horrendously inefficient, and it's no surprise that your code is running slowly!
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
modified 23-Jul-15 12:50pm.
|
|
|
|
|
Thank you this is working perfectly! Moving to a different namespace was not an option for me at this moment in time, so this solution is perfect.
|
|
|
|
|
Richard Deeming wrote: EDIT: Looking at the code in dotPeek, this will be much more efficient
than calling GetChars :
Did the same thing and looked like a regular byte-array being read. Either I have looked at the wrong method, or really have missed a few important 'hints' in the code.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
The code from my version (slightly tidied up) looks like:
public override long GetChars(int i, long fieldOffset, char[] buffer2, int bufferoffset, int length)
{
return this._columnInfo[i].GetChars(this._buffer, fieldOffset, buffer2, bufferoffset, length);
}
internal long GetChars(NativeBuffer_RowBuffer buffer, long fieldOffset, char[] destinationBuffer, int destinationOffset, int length)
{
int result;
if (this.IsLob)
{
using (OracleLob oracleLob = new OracleLob(this._lobLocator))
{
string str = (string) oracleLob.Value;
int length1 = str.Length;
int startIndex = (int)fieldOffset;
result = length1 - startIndex;
if (destinationBuffer != null)
{
result = Math.Min(result, length);
if (0 < result) Buffer.BlockCopy(str.ToCharArray(startIndex, result), 0, destinationBuffer, destinationOffset, result);
}
}
}
else
{
}
return (long) Math.Max(0, result);
}
public object Value
{
get
{
long num = this._currentPosition;
try
{
this.Seek(0L, SeekOrigin.Begin);
if (this._lobType == OracleType.Blob || this._lobType == OracleType.BFile)
{
}
return new StreamReader(this, Encoding.Unicode).ReadToEnd();
}
finally
{
this._currentPosition = num;
}
}
}
So basically:
- The
OracleLob.Value property reads the entire CLOB value into memory as a string; - The
OracleColumn.GetChars method calls ToCharArray which copies (a portion of) the string to a new array; - It then uses
Buffer.BlockCopy to copy the characters from the new array to the destinationBuffer array;
It's not quite as bad as I thought - it's only copying a portion of the string to a new array when it calls ToCharArray , rather than the whole string.
But it's still pretty bad compared to using a StreamReader over the OracleLob object.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
..if a string is large enough, it will be thrown into the large object heap?
Ouch
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Hello !
On my application I use the
System.Windows.Forms.OpenFileDialog
System.Windows.Forms.FolderBrowserDialog
But on these default windows there are some buttons or labels that are in English. Is there any possibility to access and rename these in anther language ?
Thank you !
|
|
|
|
|
They are determined by the system, and react to the systems settings. It would be possible, but it won't be easy. There are some articles on customizing the OpenFileDialog here on CodeProject.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
Any link to these articles ?
|
|
|
|
|
|
Hello !
I'm using the .net 4.5 compression library.
I can create .zip files from my program , and also I can get zip files to do several actions with files inside.
The problem is this :
I want to detect if the zip file is the one created from my application , or is another zip file.
Is there any way to add a validation information ( so a hidden information ) inside a zip file so I can detect this ?
Thank you !
|
|
|
|
|
You can add something, but not hide it. You could encrypt the information and deduce that it cannot be yours, but I could also copy that part into a new zipfile.
You "could" add a text-file that gets zipped; fill it with checksum for each file, adding salt that is unique to your application. That would provide "some" guarantee that it was a file created by your application; I'd need to hack into the code to find out how to build one that looks like it.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
The .ZIP format allows to extra data (metadata) to be added to the archive and to each file.
The classes in the .NET Framework do NOT support this.
You have to use a third party library to do this, like DotNetZip or SharpZipLib. Using either those libraries will let you add data to each file as a "comment". So long as your data can be represented as a string, it'll work.
|
|
|
|
|
Hello !
I have followed your suggestions , and I'm using comments inside the zip file. But the problem is that some programs like 7Zip can make this comment visible , and also let the users add comments. So a file created with this program can add comments and a zip file that seems created with my software , may be created with this other software.
What can I do ?
Thank you !
|
|
|
|
|
I never said the solution was perfect.
Yes, all comments are visible in just about any ZIP program.
Metadata is supported in the ZIP format outside of comments. The problem is I don't know of a single ZIP library that supports it. You'd have to write your own ZIP library to do this and that's a HUGE undertaking.
|
|
|
|
|
I have some SQL Linq that I need date ranges for. But I remember when I wrote them in TSQL, I had to specify the hours, minutes and seconds to compare to a DateTime Column.
So I have this for the current day, not that hard to figure this one out.
Dim DateStart As New DateTime(Now.Year, Now.Month, 1, 0, 0, 0, 0)
Dim DateStop As New DateTime(Now.Year, Now.Month, DateTime.DaysInMonth(Now.Year, Now.Month), 23, 59, 59, 0)
I wrote this for Last Months first and last day, but I only get the date, and not the time
This one is harder to figure out.
Dim DateStart As DateTime = DateAdd("m", -1, DateSerial(Year(Today), Month(Today), 1))
Dim DateStop As DateTime = DateAdd("m", 0, DateSerial(Year(Today), Month(Today), 0))
I wrote this for last years first day and last day, but once again I only get the date
Dim DateStart As DateTime = DateAdd("y", -1, DateSerial(Year(Today), Month(Today), 1))
Dim DateStop As DateTime = DateAdd("y", 0, DateSerial(Year(Today), Month(Today), 0))
I haven't written complex date and time in vb, only in TSQL and I had to get help on that as well. I could use some help or a nudge on this to get going in the right direction.
|
|
|
|
|
Then easy way to do it is to just assemble the DateTime from Month, Day 1, Year, 00:00:00 (midnight), like you have in your first line of code.
The end DateTime is actually very easy. Just create a DateTime for the Month + 1, Day 1, Year, 00:00:00 (midnight).
For example,
' Get the current Date/Time in a variable we can manpulate consistently
' This is because each call to DateTime.Now or DateTime.Today will return
' a different time, and if executed at midnight, different days!
Dim baseDate As DateTime = DateTime.Today()
' Build a start date based on the current month and year.
Dim startDate As DateTime = New DateTime(baseDate.Year, baseDate.Month, 1, 0, 0, 0, 0)
' Build a end date based on the current month and year.
' Add a month to the current DateTime to easier get the end date.
' This also accounts for rolling over the year.
baseDate = baseDate.AddMonth(1)
Dim stopDate = New DateTime(baseDate.Year, baseDate.Month, 1, 0, 0, 0, 0)
Now, in your query the WHERE clause would look like this:
WHERE dateTime >= startDate AND dateTime < endDate
This accounts for records that appear from midnight on the start day and up to, but not inclusive of, the end date which starts at midnight.
|
|
|
|
|
Thanks Dave, I'll give that a try. I need to build some data up in order to fully test it.
But I did write this for last month and last year, but wasnt' able to fully test it.
Dim DateStart As DateTime = DateAdd("m", -1, DateSerial(Year(Today), Month(Today), 1))
Dim DateStop As DateTime = DateAdd("m", 0, DateSerial(Year(Today), Month(Today), 0))
Dim DateStart As DateTime = DateAdd("yyyy", -1, DateSerial(Now.Year, 1, 1))
Dim DateStop As DateTime = DateAdd("yyyy", -1, DateSerial(Now.Year, 12, 31))
|
|
|
|
|