I am having trouble wrapping my head around the async/await functionality in .NET 4.5. I am using the code below in a Web API controller to catch multiple files from a form along with some other form data. I have no control over the form or how it sends the data.
What I want to do is receive the files, get data from the form, read a database based on that form data, move the file(s), and update another database table. With the below code I have no trouble getting the files or form data. I get data from the database based on the formID passed in the form data.
It is when I uncomment the code near the bottom for writing back to the database that I run into issues. If I had three files, only one of them gets moved before the catch block catches an exception. I am assuming that my problem is related to the fact that the PostFile method is async.
What is the proper way of writing this code so that it works?
public async Task<HttpResponseMessage> PostFile()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = GetRootPath();
var provider = new MyMultipartFormDataStreamProvider(root);
string logfile = root + "/form_data_output.txt";
try
{
// Read the form data and return an async task.
await Request.Content.ReadAsMultipartAsync(provider);
string form_id = provider.FormData.Get("FormId");
string driver_id = GetDriverID(form_id); // returns an int as a string
string location = ConfigurationManager.AppSettings["storagePath"];
location += form_id + "\\";
//// make sure the new directory exists
if (!Directory.Exists(location))
{
Directory.CreateDirectory(location);
}
var keys = provider.FormData.Keys.Cast<string>();
foreach (var k in keys.Where(k => k.StartsWith("FormViewer") == true))
{
string filename = provider.FormData.Get(k) + ".pdf";
string type_key = "FormType_" + k.Substring(k.IndexOf('_') + 1);
string type_value = provider.FormData.Get(type_key);
// setup the full path including filename
string path = root + "\\" + filename;
string newFullPath = location + filename;
// move the file
File.Move(path, newFullPath);
if (File.Exists(newFullPath))
{
if (File.Exists(newFullPath))
{
try
{
string conn_str = ConfigurationManager.ConnectionStrings["eMaintenanceConnection"].ConnectionString;
using (SqlConnection conn = new SqlConnection(conn_str))
{
SqlCommand cmd = new SqlCommand("INSERT INTO eSubmittal_Document VALUES (null,#driver_id,#location,#doctype)");
cmd.Parameters.AddWithValue("#driver_id", driver_id);
cmd.Parameters.AddWithValue("#location", location);
cmd.Parameters.AddWithValue("#doc_type", type_value);
conn.Open();
int c = await cmd.ExecuteNonQueryAsync();
conn.Close();
}
}
catch (Exception e)
{
LogEntry(logfile, e.Message);
}
}
}
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
async and await provide natural program flow for asynchronous code. So for the most part, you can just think about code the way you normally think about it:
It is when I uncomment the code near the bottom for writing back to the database that I run into issues. If I had three files, only one of them gets moved before the catch block catches an exception.
Here's what I get from this:
Your database code is throwing an exception.
When the exception is thrown, it leaves the foreach loop to go to the catch handler.
Nothing unexpected there...
It's hard to help very much without knowing the exception, but performing a synchronous database operation inside an async method seems like a bad idea to me. Try changing your code to use:
int c = await cmd.ExecuteNonQueryAsync();
Related
Iam building a website which uses a pivot viewer that uses a deep zoom collection to show images and the metadata from a database. Iam using the deezoomtools.dll from microsoft to create the deep zoom collection.
From this website i can start a thread that starts the creation of several collection. For small collections this process goes just fine, however when doing a large collection it fails regularly.
I added some debug lines to see what happens and the exception i get is:
Thread was aborted as the message and as stack trace where it came from.
I found that when the images are processed the exception occurs. I guess that the error occurs in this code sample in the function GetImageForDeepZoomComposer
private bool processImage(Item collectionItem, string backupImage,StreamWriter _logFile)
{
try
{
_logFile.WriteLine("start process");
if (string.IsNullOrEmpty(collectionItem.Image))
return false;
if (!File.Exists(collectionItem.ImagePath))
return false;
_logFile.WriteLine("File is there");
if (!AllImagesUnique) // check to see if we have processed this exact file before.
if (cacheOfImageIds.ContainsKey(collectionItem.ImagePath)) // we already have have processed this image so don't do it again
{
collectionItem.ImageId = cacheOfImageIds[collectionItem.ImagePath];
return true;
}
_logFile.WriteLine("Get Image: " + collectionItem.ImagePath);
string workingImage = GetImageForDeepZoomComposer(collectionItem.ImagePath, collectionItem.Id);
_logFile.WriteLine("Image aquired");
_logFile.WriteLine("collection name and id");
string deepZoomImage = DeepZoomImageDir + collectionItem.Id;
sendAction(string.Format("\tConverting image {0} to Deep Zoom Output file {1} ", workingImage,
deepZoomImage));
//imageCreator.Create(workingImage, deepZoomImage);
_logFile.WriteLine("set image surrogate");
SurrogateImageInfo sii = new SurrogateImageInfo(workingImage, "DeepZoomImages/" + Path.GetFileName(deepZoomImage) + ".xml");
_logFile.WriteLine("image surrogate set");
images.Add(sii);
if (!AllImagesUnique) // if we want to make sure we don't use the Deep Zoom Composer on the same file twice then add this image to the cache
cacheOfImageIds.Add(collectionItem.ImagePath, collectionItem.Id);
return true;
}
catch (Exception ex)
{
_logFile.WriteLine(ex.Message);
sendAction(
string.Format(
"\tSkipping current item cause of execption encountered while processing the image\r\n\t\t{0}",
ex.Message));
return false; // this item will not be added to the collection
}
}
internal string GetImageForDeepZoomComposer(string imageFile,int id)
{
if (imageFile.StartsWith("http://") || imageFile.StartsWith("https://"))
{
if (_client == null) // if we havent used the WebClient for this collection yet create one
_client = new WebClient();
string tempFile = ImageDownloadDir + Guid.NewGuid();
sendAction(string.Format("\tDownloading image '{0} to {1}", imageFile, tempFile));
_client.DownloadFile(imageFile, tempFile);
return tempFile;
}
else if (imageFile.StartsWith("\\") || imageFile.StartsWith("E:\\") || imageFile.StartsWith("e:\\"))
{
var dir = Path.Combine(DeepZoomImageDir, id.ToString() + "_files");
//if (!Directory.Exists(dir))
// Directory.CreateDirectory(dir);
var fixedSizeFileName = imageFile.Replace("_thumb", "_fs");
//create a fixed size image if it's not already created
if (!File.Exists(fixedSizeFileName))
FixedSize(new System.Drawing.Bitmap(imageFile), 512, 512).Save(fixedSizeFileName,codecInfo,parameters);
//copy the fixed size image to the DeepZoomImages folder so it can be used when zooming in to the deepest level
var localFixedSizeImage = Path.Combine(DeepZoomImageDir, id + ".jpg");
File.Copy(fixedSizeFileName, localFixedSizeImage);
return localFixedSizeImage;
}
return imageFile;
}
I think it has something to do with images that are locked when it tries to get the image and tries to save it with a different name such that it could be used. Iam truly at a loss here.
Thank you in advance for the information
I previously made a post asking how to send a .3gpp audio file up to the parse cloud here:
Xamarin C# Android - converting .3gpp audio to bytes & sending to parseObject
I have managed to do this successfully, on parse's data manager, I can click the file's link and play the sound sent from my android device successfully.
Here's the code for uploading the data to the cloud:
async Task sendToCloud(string filename)
{
ParseClient.Initialize ("Censored Key", "Censored Key");
string LoadPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
string savetheFile = sName + ".3gpp";
string tempUserName;
LoadPath += savetheFile;
Console.WriteLine ("loadPath: " + LoadPath);
try
{
byte[] data = File.ReadAllBytes(LoadPath);
ParseFile file = new ParseFile(savetheFile, data);
await file.SaveAsync();
var auidoParseObject = new ParseObject("AudioWithData");
//Console.WriteLine(ParseUser.getUserName());
if (ParseUser.CurrentUser != null)
{
tempUserName = ParseUser.CurrentUser.Username.ToString();
}
else{
tempUserName = "Anonymous";
}
//tempUserName = ParseUser.CurrentUser.Username.ToString();
Console.WriteLine("PARSE USERNAME: " + tempUserName);
auidoParseObject["userName"] = tempUserName;
auidoParseObject["userName"] = tempUserName;
auidoParseObject["file"] = file;
await auidoParseObject.SaveAsync();
}
catch (Exception e)
{
Console.WriteLine("Failed to await audio object! {0}" + e);
}
}
So as you can see, I'm sending a ParseObject called "AudioWithData".
This object contains two children:
-The username of the user who uploaded the file (string)
-The parseFile called "file" (which has the following two children)
---SaveTheFile (A string containing the name of the audio file, input by the user, with the .3gpp extension added on the end, for example "myAudioFile.3gpp"
---data (this contains the bytes of the audio file)
I need to be able to download the file onto my android device, and play it through a mediaplayer object.
I've checked over the documentation on the parse website, but I haven't managed to do this:
(excuse my pseudo querying syntax here)
SELECT (audio files) FROM (the parseObject) WHERE (the username = current user)
I then, eventually, want to place all of these files into a listview, and when the user clicks the file, it plays the audio.
I've tried the following but I don't really know what I'm doing with it...
async Task RetrieveSound(string filename)
{
ParseClient.Initialize ("Censored key", "Censored key");
Console.WriteLine ("Hit RetrieveSound, filename = " + filename);
string username;
var auidoParseObject = new ParseObject("AudioWithData");
if (ParseUser.CurrentUser != null) {
username = ParseUser.CurrentUser.Username.ToString ();
} else {
username = "Anonymous";
}
string cloudFileName;
Console.WriteLine ("username set to: " + username);
var HoldThefile = auidoParseObject.Get<ParseFile>("audio");
//fgher
var query = from audioParseObject in ParseObject.GetQuery("userName")
where audioParseObject.Get<String>("userName") == username
select file;
IEnumerable<ParseFile> results = await query.FindAsync();
Console.WriteLine ("passed the query");
//wfojh
byte[] data = await new HttpClient().GetByteArrayAsync(results.Url);
Console.WriteLine ("putting in player...");
_player.SetDataSourceAsync (data);
_player.Prepare;
_player.Start ();
}
Any help would be GREATLY APPRECIATED! Even a point in the right direction would be great!
Thanks!
EDIT--
I'm actually getting a query error on the following lines
(I can't post images because of my reputation - I lost access to my main stackOverflow account :/ )
Links to images here:
first error: http://i.stack.imgur.com/PZBJr.png
second error: http://i.stack.imgur.com/UkHvX.png
Any ideas? The parse documentation is vague about this.
this line will return a collection of results
IEnumerable<ParseFile> results = await query.FindAsync();
you either need to iterate through them with foreach, or just pick the first one
// for testing, just pick the first one
if (results.Count > 0) {
var result = results[0];
byte[] data = await new HttpClient().GetByteArrayAsync(result.Url);
File.WriteAllBytes(some_path_to_a_temp_file, data);
// at this point, you can just initialize your player with the audio file path and play as normal
}
I have a problem coping a file. I need to copy a .db file and put it in a new folder (called "directory",selected previously with FolderPicker).
The code that i have is: (this is for a store app for Windows 8.1)
try{
StorageFile newDB = await StorageFile.GetFileFromPathAsync(directory);
StorageFile originalDB = await StorageFile.GetFileFromPathAsync(Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "AFBIT.db"));
await newDB.CopyAndReplaceAsync(originalDB);
}
catch(Exception ex){
}
I have a exception in neDB, and said "Value does not fall within the expected range."
I dont know another way to copy a file in xaml, if u know what is the problem or another way to do this i llbe very grateful.
I have something similar that I currently use when copying a file CopyFileAsync method I have created see if this can help you in regards to refactoring your code to a working model
public static async Task CopyFileAsync(string sourcePath, string destinationPath)
{
try
{
using (Stream source = File.Open(sourcePath, FileMode.Open))
{
using (Stream destination = File.Create(destinationPath))
{
await source.CopyToAsync(destination);
}
}
}
catch (IOException io)
{
HttpContext.Current.Response.Write(io.Message); //I use this within a web app change to work for your windows app
}
}
I'm not sure what your truly inquiring but I believe your attempting is:
public static bool CopyFile(string source, string destination)
{
if(!File.Exist(source))
return false;
if(string.IsNullOrEmpty(destination))
return false;
try
{
using(var reader = File.Open(source))
using(var writer = File.Create(destination))
reader.CopyTo(writer);
return true;
}
catch(IOException ex) { return false; }
}
Bare in mind this will eat your exception, then return false if it fails at any point for any reason.
That would essentially copy the file, I noticed that your trying to read your local application folder. Be careful, as it often requires Administrator Privileges when it resides in several locations within the Operating System.
I am in a Highschool club where we create windows store apps. I am in charge of the code that allows the user to either download files from their online onedrive storage, or upload files. So far I have successfully logged the user in and gained access to onedrive and display the users name with the following code:
private async void LoadProfile()
{
bool connected = false;
string text = "No Error:";
try
{
var authClient = new LiveAuthClient();
LiveLoginResult result = await authClient.LoginAsync(new List<string>() {"wl.signin", "wl.skydrive"});
if (result.Status == LiveConnectSessionStatus.Connected)
{
connected = true;
var connectClient = new LiveConnectClient(result.Session);
var meResult = await connectClient.GetAsync("me");
dynamic meData = meResult.Result;
Textblock_profilename.Text = meData.name;
}
}
catch (LiveAuthException ex)
{
//Set text to corresponding error
text = ex.ToString();
}
catch (LiveConnectException ex)
{
//Set text to corresponding error
text = ex.ToString();
}
if (text[0].ToString() != "N")
{
var dialog = new Windows.UI.Popups.MessageDialog(text);
await dialog.ShowAsync();
}
}
I gained the code from the following MSDN tutorial: http://msdn.microsoft.com/en-us/library/dn631823.aspx
However when I try to follow the next step, downloading and uploading files, I cannot get it to work. Right now I am just trying to press a button, and have the code download a test file:
private async void Button_downloadFile_Click(object sender, RoutedEventArgs e)
{
try
{
LiveDownloadOperation operation = await connectClient.CreateBackgroundDownloadAsync("skydrive/documents/enter_path");
var result = await operation.StartAsync();
//DO SOMETHING WITH RESULT HERE
}
catch
{
// Handle any errors.
}
}
However this code throws the following errors:
This is straight from the MSDN tutorial, and can't figure out how to fix the error. My best guess is I'm missing a "using" statement, but can't figure out what I am missing. Thanks for any and all help!
Make sure you're updated to use the Live SDK 5.6 binary. Be sure to let us know if you have any other problems with OneDrive integration!
this is my first post on this site and I searched high and wide to get my code to work.
Like the title says, it's a WinRT App and I'm having difficulty with File IO. What I want to do is read in a text file stored in a folder that is inside the application installation directory and that contains lines of data that I'll feed into an List<>.
public static async void GetStations()
{
try
{
using (var stream = await Windows.Storage.ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(#"MyApp\Data\file.txt"))
{
using (var streamReader = new StreamReader(stream))
{
while (streamReader.Peek() >= 0)
{
string line = await streamReader.ReadLineAsync();
//do something with
}
}
}
}
catch (Exception e)
{
...
}
finally
{
...
}
}
the problem is I am getting file not found errors when trying to run it. Can anyone help? If you require that I post more information, I can...
Thanks in advance.
If you are distributing your file as a part of your application package then Package.Current.InstalledLocation is the right location. ApplicationData.Current.LocalFolder contains only files that have been put there by your application.
The correct code would be:
public static async void GetStations()
{
try
{
using (var stream = await Windows.ApplicationModel.Package.Current.InstalledLocation.OpenStreamForReadAsync(#"Data\file.txt"))
{
using (var streamReader = new StreamReader(stream))
{
while (streamReader.Peek() >= 0)
{
string line = await streamReader.ReadLineAsync();
//do something with
}
}
}
}
catch (Exception e)
{
//...
}
finally
{
//...
}
}
The file must be included in you project inside Data folder and have Build Action set to Content.
Instead of opening from ApplicationData, you probably need:
Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync
This will get the file in the package's installation folder, instead of the Application's Data folder.