|
That code has the interesting issue that the returned array can easily, even accidentally, be used to mess up the state of the Package. That's not necessarily the Worst Thing Ever (it is always possible to mess up objects with reflection anyway), but it's something to at least be aware of.
|
|
|
|
|
I think the "best practice" ... the implementation details ... will be an "organic" function of the way you intend to use this data-structure; including what you want your code to communicate to yourself, and, possibly, others, in the future.
One suggestion: before you draw any simple one-size-fits-all conclusions about how .NET allocates memory for Structs, and other "Value Types" compared to "Reference Types," please read Eric Lippert's two articles on this topic: [^], [^]. Eric is a gurus'-guru who was a key player in creating .NET for many years, and I think these articles provide a very valuable insight into what actually goes on in memory in .NET ... perhaps not it's not as clear-cut as many people think.
There are many ways you could implement this, from simple to fancy, from one-shot single-purpose to general-use multi-purpose; a few years ago I wrote a multi-purpose class for maintaining a set of selected Char that was, perhaps, too fancy: I just looked at the code tonight and wondered why I made it so elaborate ... but I have a "thang" for writing multi-purpose
If you care to say more about your goals here, I'll respond, and I am sure others will.
«A man will be imprisoned in a room with a door that's unlocked and opens inwards ... as long as it does not occur to him to pull rather than push» Wittgenstein
|
|
|
|
|
TMattC wrote: Can it be done with an automatic property One further comment: an automatic property is one where the private backing-field is created for you by using the declarative syntax:
private char[] CharProp { set; get; }
The moment you specify a "body" for either the 'set or the 'get, it's no longer an automatic property, and, since it's clear your use-case would involved accessing characters within the Array here, you'd have to do that in the 'get, and, for that to be done, you'd have to have an value to use as an Index into the Array, and there's no way to "pass in" a value to be used internally in a 'get, or 'set.
The next version of C#, C#6, is going to add some features to Property declaration: [^].
Of course, with a Property whose Type is Array you can always use the 'SetValue and 'GetValue methods of the Array object; however, as you'll see in this example, 'GetValue returns a Type Object which will need to be cast to 'Char to be used in the way you probably want to use it.
private char[] CharProp { set; get; }
private void TestCharProp()
{
CharProp = new char[3] {'a', 'b', 'c'};
char value2 = (char) CharProp.GetValue(2);
CharProp.SetValue('x',2);
} That's not the type of code I'd write: no error check for null; no check for bounds; unnecessary cast required. But, you can write your own methods to 'set and 'get, of course that do validate and do avoid the cast.
«A man will be imprisoned in a room with a door that's unlocked and opens inwards ... as long as it does not occur to him to pull rather than push» Wittgenstein
|
|
|
|
|
I am working with a FileSystemWatcher and intercepting 'FileCreated and 'FileChanged events: no problem there.
The file is kept open in a simple text editor application.
I read the file using this code:
private string[] theFileContents;
private void ReadTheFile(string thePath)
{
theFileContents = File.ReadAllLines(thePath);
} If I start with the file inside the watched folder, the first time I change the text in the text editor and save, I get the notification, and can verify I am reading the changed text.
The second time I edit and save in the text editor I get the "in use" error System.IO.IOException:
"The process cannot access the file because it is being used by another process.
If I start with the file outside the watched folder and then drag-drop it in: I get the 'created notification, and can read the file.
If I then edit the file in the text editor and save, I get the "in use" error again.
Is there a way to keep the file open for reading during multiple FileSystemWatcher events ?
If there isn't, I am thinking that maybe copying the file, and then reading the copy, and then discarding the copy is one possible strategy.
Appreciate your thoughts !
«A man will be imprisoned in a room with a door that's unlocked and opens inwards ... as long as it does not occur to him to pull rather than push» Wittgenstein
modified 2-Jan-15 8:25am.
|
|
|
|
|
Whenever you open the file for writing, you establish an exclusive lock on it - and no other process can get access to the file for read or write. So you need to look at your "simple text editor" and find out exactly what it does with files - some (primitive) ones open the file for read and write and hold it until they are finished (which is a pain if you have automated backups running)
And that sample code opens the same file twice...first to create a FileStream, and then again to actually read the data. You don't need the stream at all:
theFileContents = File.ReadAllLines(thePath); creates it's own stream and reads the file through that. Try without the stream, and see if the problem goes away.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
Thanks, Griff,
Using only File.ReadAllText this happens:
Test 1
1. text file is open in NotePad++ and has been saved to the Desktop
2. drag-drop file into watched folder: created event fires, no error, file read as expected
3. change text in the file in NotePad++ and save without changing save-directory location: changed event fires; no error, file read as expected
4. change text in NotePad++ again: "in use" error
Test 2
1. text file is open in NotePad++ and has been saved to watched folder before the .NET is run
2. change text in the file in NotePad++:
first, and second edits/and/saves to same location:
changed event fires; no error, file read as expected
third edit and save to same location: "in use" error
Happy New Year !
«A man will be imprisoned in a room with a door that's unlocked and opens inwards ... as long as it does not occur to him to pull rather than push» Wittgenstein
|
|
|
|
|
I wonder if it's Notepad also monitoring the file it "has open" even though it's closed it so that it can track changes outside the editor - I use PSPad which does that - and the two processes are getting the notification at the same time and one is finding the file in use as a result?
Might be worth a sleep-retry-sleep-retry to give it another chance?
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
Thanks again, Griff,
I am able to work around the problem by simply copying the dragged-dropped/changed file, then reading/processing the copied file, then deleting the copy. So, obviously, I do not alter the original in this scenario.
cheers, Bill
«A man will be imprisoned in a room with a door that's unlocked and opens inwards ... as long as it does not occur to him to pull rather than push» Wittgenstein
|
|
|
|
|
If I helped, then you are welcome!
Somehow, I don't think I did...
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
Bill, can I ask why you're reading the file contents in this way? You do a File.Open , create a StreamReader and associate it with the file, then you don't do anything with it? Instead, you do File.ReadAllLines on the file. Why not just use File.ReadAllLines instead? It opens, reads and closes the file.
|
|
|
|
|
Thanks, Pete,
Yes, you're absolutely right; OriginalGriff pointed this out to me, and ... my bad ... I have not corrected the code.
cheers, Bill
«A man will be imprisoned in a room with a door that's unlocked and opens inwards ... as long as it does not occur to him to pull rather than push» Wittgenstein
|
|
|
|
|
In C++, we can add reference to an array element as shown below:
int aArray[50];
int& Ref = aArray[1];
How do we do the same in C#?
|
|
|
|
|
You don't.
An int is not a reference type - it's a value type. You cannot "take a reference" to a value type.
The closest you can come is to pass the array element by reference to a method:
public void ChangeIt(ref int x) { x += 1; }
...
int[] aArray = Enumerable.Range(1, 50).ToArray();
ChangeIt(ref aArray[3]);
C++ and C# just share sufficient syntax to look similar: they are very different languages, and you should not try to "force" c++ ways of working on C# - it will end in tears...
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
Just to add a minor-key note to this post:
If you are going to need to change a lot of values in an Array, you can pass the entire Array by reference.
«A man will be imprisoned in a room with a door that's unlocked and opens inwards ... as long as it does not occur to him to pull rather than push» Wittgenstein
|
|
|
|
|
You don't need to - an array is a reference type anyway (even if it's an array of value type elements) so you can do this and it'll work:
int[] arr = Enumerable.Range(1, 50).ToArray();
ChangeIt(arr);
...
public void ChangeIt(int[] arr)
{
for (int i = 0; i < arr.Length; i++)
{
arr[i]++;
}
}
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
There is no such thing as a "local variable which is a reference" in C#, but there is in the underlying MSIL and runtime. That doesn't do you much good, but it might be interesting to know.
|
|
|
|
|
|
My Database Development book includes a program for inserting Images into a database after studying the code and modifying just the connection string and image's directory. When i run the program even with Administrator it produces a Run Time error of "Denied" in the message box. Now the program does connect to the database and does insert values into the Primary Key column and 2 other columns. The problem is that it is not reading the Folder that contains the images and is not inserting them into the database image column.
I have modified the folder's permissions for Full Control but during execution of the software it automatically produces the error message of Denied.The Database has been setup for FILESTREAM and Filestream access level has been modified for level 2 and setup in the Sql Server Properties under FileStream.
Any idea's on what may be causing this issue? I am running Windows 8.1 Pro 64Bit
Class
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
namespace MusicStoreImageManager
{
class ProductDB
{
static string imagesPath = "C:/Users/Rattlerr/Pictures/Images/";
public static SqlConnection GetConnection()
{
SqlConnection connection = new SqlConnection();
connection.ConnectionString =
"Data Source=RATTLERR;Initial Catalog=Test;Integrated Security=False;User ID=sa;Password=SomePassword";
return connection;
}
public static void WriteImage(int productID, string imageName)
{
SqlConnection connection = null;
SqlTransaction transaction = null;
try
{
string filepath = imagesPath + imageName;
if (File.Exists(filepath) == false)
throw new Exception("File Not Found: " + filepath);
FileStream sourceStream = new FileStream(
filepath,
FileMode.Open,
FileAccess.Read);
connection = GetConnection();
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText =
"INSERT INTO ProductImages " +
"VALUES (@ProductID, " +
" CAST(@RowID AS uniqueidentifier), 0)";
Guid rowID = Guid.NewGuid();
command.Parameters.AddWithValue("@ProductID", productID);
command.Parameters.AddWithValue("@RowID", rowID);
connection.Open();
command.ExecuteNonQuery();
transaction = connection.BeginTransaction();
command.Transaction = transaction;
command.CommandText =
"SELECT ProductImage.PathName(), " +
" GET_FILESTREAM_TRANSACTION_CONTEXT() " +
"FROM ProductImages " +
"WHERE RowID = CAST(@RowID AS uniqueidentifier)";
command.Parameters.Clear();
command.Parameters.AddWithValue("@RowID", rowID);
SqlDataReader reader = command.ExecuteReader();
if (reader.Read() == false)
throw new Exception("Unable to get path and context for BLOB.");
string path = (string)reader[0];
byte[] context = (byte[])reader[1];
reader.Close();
SqlFileStream targetStream = new SqlFileStream(path, context, FileAccess.Write);
int blockSize = 1024 * 512;
byte[] buffer = new byte[blockSize];
int bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
targetStream.Write(buffer, 0, bytesRead);
bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
}
targetStream.Close();
sourceStream.Close();
transaction.Commit();
}
catch (Exception e)
{
if (transaction != null)
transaction.Rollback();
throw e;
}
finally
{
if (connection != null)
connection.Close();
}
}
public static Byte[] ReadImage(int imageID)
{
SqlConnection connection = null;
SqlTransaction transaction = null;
try
{
connection = GetConnection();
connection.Open();
transaction = connection.BeginTransaction();
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.Transaction = transaction;
command.CommandText =
"SELECT ProductImage.PathName(), " +
" GET_FILESTREAM_TRANSACTION_CONTEXT() " +
"FROM ProductImages " +
"WHERE ImageID = @ImageID";
command.Parameters.AddWithValue("@ImageID", imageID);
SqlDataReader reader = command.ExecuteReader();
if (reader.Read() == false)
throw new Exception("Unable to get path and context for BLOB.");
string path = (string)reader[0];
byte[] context = (byte[])reader[1];
reader.Close();
SqlFileStream sourceStream = new SqlFileStream(path, context, FileAccess.Read);
int blockSize = 1024 * 512;
byte[] buffer = new byte[blockSize];
List<byte> imageBytes = new List<byte>();
int bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
foreach(byte b in buffer)
imageBytes.Add(b);
}
sourceStream.Close();
return imageBytes.ToArray();
}
catch (Exception e)
{
throw e;
}
finally
{
if (connection != null)
connection.Close();
}
}
public static List<int> GetImageIDList()
{
SqlConnection connection = null;
try
{
connection = GetConnection();
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText =
"SELECT ImageID FROM ProductImages " +
"ORDER BY ImageID";
connection.Open();
SqlDataReader reader = command.ExecuteReader();
List<int> imageIDList = new List<int>();
while (reader.Read())
{
int imageID = (int)reader[0];
imageIDList.Add(imageID);
}
reader.Close();
return imageIDList;
}
catch (Exception e)
{
throw e;
}
finally
{
if (connection != null)
connection.Close();
}
}
}
}
Main Form Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace MusicStoreImageManager
{
public partial class ImageManagerForm : Form
{
public ImageManagerForm()
{
InitializeComponent();
}
private void LoadImageIDComboBox()
{
List<int> imageIDList = ProductDB.GetImageIDList();
foreach (int i in imageIDList)
imageIDComboBox.Items.Add(i);
}
private void ImageManagerForm_Load(object sender, EventArgs e)
{
this.LoadImageIDComboBox();
imageIDComboBox_SelectedIndexChanged(sender, e);
}
private void imageIDComboBox_SelectedIndexChanged(
object sender, EventArgs e)
{
try
{
int imageID = Convert.ToInt32(imageIDComboBox.Text);
Byte[] imageByteArray = ProductDB.ReadImage(imageID);
MemoryStream ms = new MemoryStream(imageByteArray);
imagePictureBox.Image = System.Drawing.Image.FromStream(ms);
ms.Close();
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error");
}
}
private void uploadButton_Click(object sender, EventArgs e)
{
try
{
int productID = Convert.ToInt32(productIDTextBox.Text);
string filename = filenameTextBox.Text;
ProductDB.WriteImage(productID, filename);
MessageBox.Show(this, "Image upload was successful!",
"Upload Confirmation");
imageIDComboBox.Items.Clear();
this.LoadImageIDComboBox();
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error");
}
}
}
}
modified 3-Jan-15 12:31pm.
|
|
|
|
|
It would help a great deal if you pointed out which line of code is generating the Access Denied message.
The difficult we do right away...
...the impossible takes slightly longer.
|
|
|
|
|
That's the whole problem the code doesn't produce an error or warnings during Debug or compilation just at Run time the error message pops up in a Message Box. The program is suppose to read the folder and load the image into the Image Picture Box nor will it upload the image to the database. The image column just shows 0x0000000 that is because none of the images are being loaded into the Image column for the table.
It will put information into the other columns just not the image, I cannot figure out if it is a permissions issue. Now when i select a different ImageID from the dropdown box the error will repeat itself here is the code below.
private void imageIDComboBox_SelectedIndexChanged(
object sender, EventArgs e)
{
try
{
int imageID = Convert.ToInt32(imageIDComboBox.Text);
Byte[] imageByteArray = ProductDB.ReadImage(imageID);
MemoryStream ms = new MemoryStream(imageByteArray);
imagePictureBox.Image = System.Drawing.Image.FromStream(ms);
ms.Close();
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error");
}
}
modified 31-Dec-14 17:34pm.
|
|
|
|
|
What you say makes no sense. "Debug" is runtime, is it not? You're running the program in the debugger, right?
The difficult we do right away...
...the impossible takes slightly longer.
|
|
|
|
|
Doesn't matter if i run it in the debugger or the compiled executable the same error repeats itself. I have modified the folder permissions to full control and even tried running the program under administrative privileges.
|
|
|
|
|
OK I think I understand what you mean. You mean the program is not breaking with an unhandled exception.
One big problem with your code is that you have so many different operations all encompassed within single try/catch blocks that it's hard to tell where the exception is coming from.
If you were to temporarily comment out the try/catch blocks, but leave the code they contain, then the exception will become plain.
The difficult we do right away...
...the impossible takes slightly longer.
|
|
|
|
|
I will try that this code is not mine it was written by the author of the book murachs Sql Server 2012 for Developers. When i run the Debugger a message box pops up and says Error "Access is Denied". If i press the Okay button the program continues and loads the main form but the picture box is empty because the image columns are empty.
Now if i enter a productID into the textbox and press the upload button it will insert the productID into the productID column and generate a ImageID and RowID. The program is setup to look in the images folder, you type the image name for example "picture.jpg" into the textbox put in the productID number into the textbox and press the upload button.
static string imagesPath = "C:/Users/Gerry/Pictures/Images/";
modified 31-Dec-14 17:57pm.
|
|
|
|
|
Well, finding out exactly which line of code is causing the exception is the first step to solving the problem.
Could it be that the path you point out does not exist on your machine?
The difficult we do right away...
...the impossible takes slightly longer.
|
|
|
|
|