Issues with MultipartFormDataStreamProvider - asp.net-mvc

I'm trying to follow this tutorial, and I'm realizing that even if I copy and past his code, I'm getting two compile errors in my ApiController.
IEnumerable<HttpContent> bodyparts = Request.Content.ReadAsMultipartAsync(streamProvider);
This tells me that the return of ReadAsMultipartAsync can't be cast to an IEnumerable of HttpContent.
IDictionary<string, string> bodypartFiles = streamProvider.BodyPartFileNames;
And this is telling me that BodyPartFileNames doesn't exist in streamProvider, which seems contrary to the tutorial as well as several other blog posts and StackOverflow questions I've seen.
Anyone have any idea what the deal is?
Full file:
using AsyncFileUpload.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace AsyncFileUpload.Controllers
{
public class UploadingController : ApiController
{
private const string PATH = "C:\\_projects\\learning";
[HttpPost]
public async Task<IList<FileDesc>> Post()
{
List<FileDesc> result = new List<FileDesc>();
if (Request.Content.IsMimeMultipartContent())
{
try
{
if (!Directory.Exists(PATH))
{
Directory.CreateDirectory(PATH);
}
MultipartFormDataStreamProvider streamProvider =
new MultipartFormDataStreamProvider(PATH);
IEnumerable<HttpContent> bodyparts = Request.Content.ReadAsMultipartAsync(streamProvider);
IDictionary<string, string> bodypartFiles = streamProvider.BodyPartFileNames;
IList<string> newFiles = new List<string>();
foreach (var item in bodypartFiles)
{
var newName = string.Empty;
var file = new FileInfo(item.Value);
if (item.Key.Contains("\""))
{
newName = Path.Combine(file.Directory.ToString(),
item.Key.Substring(1, item.Key.Length - 2));
}
else
{
newName = Path.Combine(file.Directory.ToString(), item.Key);
}
File.Move(file.FullName, newName);
newFiles.Add(newName);
}
var uploadedFiles = newFiles.Select(i =>
{
var fi = new FileInfo(i);
return new FileDesc(fi.Name, fi.FullName, fi.Length);
}).ToList();
result.AddRange(uploadedFiles);
}
catch (Exception e)
{
// NOOP
}
}
return result;
}
}
}

ReadAsMultipartAsync returns a Task<> object. Take the .Result property (which blocks) or use the await keyword to wait on the task (preferable).
BodyPartFileNames was changed in the RTM release, now use the FileData property.
See: http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2

Related

Stored procedure ADO.NET .NET Core Web API

I am building a Web API in ASP.NET Core, I am using stored procedures to be able to handle more complex queries, which with the Entity Framework is too complicated for me, I am using ADO.NET to make this connection.
I have managed to connect to a stored procedure and use the get and post methods, the point is that I don't know how to do it in order to call the other stored procedures and map a route to interact via get or post in the same project. I have only been able to do one, and I don't think it would be more convenient to create a Web API for each function that complies with a stored procedure.
My project is made up of three folders called Controller, Data, Models.
Within Models is the Value class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNETCore_StoredProcs.Models
{
public class Value
{
public int Id { get; set; }
public int Value1 { get; set; }
public string Value2 { get; set; }
}
}
Data folder has a class called ValueRepository
using ASPNETCore_StoredProcs.Models;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNETCore_StoredProcs.Data
{
public class ValuesRepository
{
private readonly string _connectionString;
public ValuesRepository(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("defaultConnection");
}
public async Task<List<Value>> GetAll()
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetAllValues", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var response = new List<Value>();
await sql.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
response.Add(MapToValue(reader));
}
}
return response;
}
}
}
private Value MapToValue(SqlDataReader reader)
{
return new Value()
{
Id = (int)reader["Id"],
Value1 = (int)reader["Value1"],
Value2 = reader["Value2"].ToString()
};
}
public async Task<Value> GetById(int Id)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetValueById", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id", Id));
Value response = null;
await sql.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
response = MapToValue(reader);
}
}
return response;
}
}
}
public async Task Insert(Value value)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("InsertValue", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#value1", value.Value1));
cmd.Parameters.Add(new SqlParameter("#value2", value.Value2));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
public async Task DeleteById(int Id)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("DeleteValue", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id", Id));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
}
}
and finally I have a controller class called ValuesController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCore_StoredProcs.Data;
using ASPNETCore_StoredProcs.Models;
using Microsoft.AspNetCore.Mvc;
namespace ASPNETCore_StoredProcs.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ValuesRepository _repository;
public ValuesController(ValuesRepository repository)
{
this._repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
// GET api/values
[HttpGet]
public async Task<ActionResult<IEnumerable<Value>>> Get()
{
return await _repository.GetAll();
}
// GET api/values/5
[HttpGet("{id}")]
public async Task<ActionResult<Value>> Get(int id)
{
var response = await _repository.GetById(id);
if (response == null) { return NotFound(); }
return response;
}
// POST api/values
[HttpPost]
public async Task Post([FromBody] Value value)
{
await _repository.Insert(value);
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public async Task Delete(int id)
{
await _repository.DeleteById(id);
}
}
}
This is my startup class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCore_StoredProcs.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace ASPNETCore_StoredProcs
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ValuesRepository>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
I appreciate if you can help me or if it is how I think I should create an API for each procedure that is performed thanks

Calling SignalR Hub method from client locks Xamarin android App

I finally got a SignalR Hub to work using the Microsoft.AspNet.SignalR vice the Microsoft.AspNetCore.SignalR, I was unable to get the Microsoft.AspNetCore.SignalR, no idea why. But I did get the other one to work. I am able to connect, link clients to connection id's using OnConnect and removing them using OnDisconnect. My Hub code is:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.SignalR;
using SignalrHub;
namespace SignalRChat
{
public class ChatHub : Hub
{
private static readonly List<User> Users = new List<User>();
public override Task OnConnected()
{
// string userName = Context.User.Identity.Name;
string userName = Context.QueryString["username"];
string org= Context.QueryString["organization"];
string dept = Context.QueryString["dept"];
string team = Context.QueryString["team"];
string firstname = Context.QueryString["firstname"];
string lastname = Context.QueryString["lastname"];
string connectionId = this.Context.ConnectionId;
// for now I just capture username and connection Id
var user = new User();
user.Name = userName;
user.ConnectionIds = connectionId;
try
{
Users.Add(user);
}
catch (Exception ex)
{
var msg = ex.Message;
}
// TODO: Broadcast the connected user
// send list of connected users to client
Send("Welcome " + userName, "Connected users are:");
foreach (var display in Users)
{
Send("",display.Name.ToString());
}
return base.OnConnected();
}
public override Task OnDisconnected(bool stopped)
{
string userName = Context.User.Identity.Name;
string connectionId = Context.ConnectionId;
var item = Users.Find(x => x.ConnectionIds == connectionId);
Users.Remove(item);
return base.OnDisconnected(true);
}
public void Send(string name, string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(name, message);
}
public List<String> GetConnectedUsers()
{
List<string> UserNames = new List<string>();
foreach (var ConnectedUser in Users)
{
UserNames.Add(ConnectedUser.Name);
}
return UserNames;
}
}
}
Everything works fine except when I call GetConnectedUsers(), when I call that from the client with this code ConnecteduserList = client.ConnectedUsers(); the app locks up, eg; the hub never returns from that method. Clearly I'm missing something. Can anyone tell me what?
The client code in the app is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Client;
namespace ChatClient.Shared
{
class Client
{
//public string username;
private readonly string _platform;
private readonly HubConnection _connection;
private readonly IHubProxy _proxy;
public event EventHandler<string> OnMessageReceived;
public Client(string platform, string username)
{
string _username = "username=" + username;
_platform = platform;
_connection = new HubConnection("https://MyApp.com/SignalRhub", _username);
_proxy = _connection.CreateHubProxy("chathub");
}
public async Task Connect()
{
await _connection.Start(); _proxy.On("broadcastMessage", (string platform, string message) =>
{
if (OnMessageReceived != null)
OnMessageReceived(this, string.Format("{0}: {1}", platform, message));
});
Send("Connected");
}
public List<string> ConnectedUsers()
{
List<string> Users = new List<string>();
// Locks up when this line is esecuted. The server log has nothing in it.
Users = _proxy.Invoke<List<string>>("GetConnectedUsers").Result;
return Users;
}
public Task Send(string message)
{
return _proxy.Invoke("Send", _platform, message);
}
}
}
Thanks to David Fowler over at GitHub who provided the link to this document (https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#avoid-using-taskresult-and-taskwait), I was able to get this to work by changing my code as follows:
On the client:
From:
public List<string> ConnectedUsers()
{
// Hangs on this line
List<string> Users = _proxy.Invoke<List<string>>("getConnectedUsers").Result;
return Users;
}
To:
public async Task <List<string>> ConnectedUsers()
{
List<string> Users = await _proxy.Invoke<List<string>>("getConnectedUsers");
return Users;
}
The call to the ConnectedUsers function in Client.cs was changed as well:
From:
List<string> userList = client.ConnectedUsers();
To:
List<string> userList = await client.ConnectedUsers();
No changes to the hub code were necessary.

ViewModel in MVC correct way of accessing data

Can anyone please tell me whether this is the correct way of creating a viewmodel. I'm using Ninject and the only way I can get the view model to work is with the code below.
Also I cannot seem to be able to pass the data from the viewmodel to the controller unless I create a 2nd interface.
The code below does work, but reading all the examples I have seen I seem to be duplicating a lot off code from my domain layer.
---------------------Code Data access layer------
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
namespace Web.Domain.SearchEngine
{
public class DisplaySearchResults
{
public string Title { get; set; }
public string Description { get; set; }
public string URL { get; set; }
}
public class GetSearchResults : IGetSearchResults
{
private string dbConn;
public GetSearchResults()
{
dbConn = ConfigurationManager.ConnectionStrings["Search"].ConnectionString;
}
public IEnumerable<DisplaySearchResults> SearchResults(string q, string option, int pagenumber)
{
List<DisplaySearchResults> Data = new List<DisplaySearchResults>();
string spName = "dbo.FTS_On_at_Websites";
using (SqlConnection cn = new SqlConnection(dbConn))
{
using (SqlCommand cmd = new SqlCommand(spName, cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#strSearchPhrase", SqlDbType.VarChar, 100));
cmd.Parameters.Add(new SqlParameter("#SearchMode", SqlDbType.Int, 4));
cmd.Parameters.Add(new SqlParameter("#intPageNumber", SqlDbType.Int));
cmd.Parameters.Add(new SqlParameter("#intRecordsPerPage", SqlDbType.Int));
cmd.Parameters.Add(new SqlParameter("#intTotalRecordsReturned", SqlDbType.Int));
cmd.Parameters["#strSearchPhrase"].Value = q;
cmd.Parameters["#SearchMode"].Value = 1;
cmd.Parameters["#intPageNumber"].Value = pagenumber;
cmd.Parameters["#intRecordsPerPage"].Value = 10;
cmd.Parameters["#intTotalRecordsReturned"].Value = 10;
cn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.Default))
{
if (rdr.HasRows)
{
while (rdr.Read())
{
Data.Add(new DisplaySearchResults
{
Title = (string)rdr["PageTitle"],
Description = (string)rdr["PageParagraph"],
URL = (string)rdr["PageURL"]
});
}
}
return Data;
}
}
}
}
}
}
-------------Code ViewModel--------------------
using Microsoft.Security.Application;
using System.Collections.Generic;
using System.Linq;
using Web.Domain.SearchEngine;
namespace Web.UI.ModelHelpers.Search
{
public class DisplaySearchResultsViewModel
{
public string Title { get; set; }
public string Description { get; set; }
public string URL { get; set; }
}
public class GetSearchResultsViewModel : IGetSearchResultsViewModel
{
private readonly IGetSearchResults _IGSR;
public GetSearchResultsViewModel(IGetSearchResults IGSR)
{
_IGSR = IGSR;
}
public IEnumerable<DisplaySearchResultsViewModel> SearchResultsViewModel(string q, string option, int pagenumber)
{
var searchResults = _IGSR.SearchResults(q, option, pagenumber).AsEnumerable();
List<DisplaySearchResultsViewModel> GetData = new List<DisplaySearchResultsViewModel>();
foreach (var details in searchResults.AsEnumerable())
{
GetData.Add(new DisplaySearchResultsViewModel()
{
Title = Sanitizer.GetSafeHtmlFragment(details.Title),
Description = Sanitizer.GetSafeHtmlFragment(details.Description).ToLower(),
URL = Sanitizer.GetSafeHtmlFragment(details.URL),
});
}
return GetData;
}
}
}
In controller I have
var DisplaySearchResults = _IGSR.SearchResultsViewModel(cleanText, "1", 1);
No, this is not the correct way to build a view model. A view model should not contain any data access logic in it. That's the responsibility of the model.
What you should do instead is use Ninject to inject the IGetSearchResults instance into your controller instead of having your GetSearchResultsViewModel view model take it as constructor dependency. Actually you do not need this GetSearchResultsViewModel at all. You already have the correct view model called DisplaySearchResultsViewModel. Then it's the responsibility of your controller to use your data access layer and build this view model.
For example:
public class SomeController : Controller
{
private readonly IGetSearchResults repository;
public SomeController(IGetSearchResults repository)
{
this.repository = repository;
}
public ActionResult SomeAction(string q, string option, int pagenumber)
{
// query your data access layer and build the view model that you will
// pass to the view
IEnumerable<DisplaySearchResultsViewModel> model = this.repository
.SearchResults(q, option, pagenumber)
.AsEnumerable()
.Select(details => new DisplaySearchResultsViewModel
{
Title = Sanitizer.GetSafeHtmlFragment(details.Title),
Description = Sanitizer.GetSafeHtmlFragment(details.Description).ToLower(),
URL = Sanitizer.GetSafeHtmlFragment(details.URL)
})
.ToList();
return View(model);
}
}

Request.Content.ReadAsMultipartAsync -- Error

With the below code I keep getting the error: Cannot implicitly convert type 'System.Net.Http.MultipartFormDataStreamProvider' to 'System.Threading.Tasks.Task>'
MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider("c:\\tmp\\uploads");
//Error line
Task<IEnumerable<HttpContent>> bodyparts = await Request.Content.ReadAsMultipartAsync(streamProvider);
I think this is an easy one, but I'm missing it.
I ended up using this and it works great. I found it here
http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}

Help Linq to Sql

Why am I getting a exception when ApplyPropertyChanges???
The code is almost the same when I'm editing a user table but is not working with my news table.
The create, delete and details are all working fine but when I try to edit a news I'm getting the exception below:
The ObjectStateManager does not contain a ObjectStateEntry 'MagixCMS.Models.noticia'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MagixCMS.Models
{
public class NoticiaRepository : INoticiaRepository
{
#region INoticiaRepository Members
magixcmsEntities _entities = new magixcmsEntities();
public noticia CreateNoticia(noticia noticiaToCreate)
{
_entities.AddTonoticiaSet(noticiaToCreate);
_entities.SaveChanges();
return noticiaToCreate;
}
public void DeletaNoticia(noticia noticiaToDelete)
{
var noticiaOriginal = GetNoticia(noticiaToDelete.Id);
_entities.DeleteObject(noticiaOriginal);
_entities.SaveChanges();
}
public noticia EditNoticia(noticia noticiaToEdit)
{
var noticiaOriginal = GetNoticia(noticiaToEdit.Id);
_entities.ApplyPropertyChanges(noticiaToEdit.EntityKey.EntitySetName, noticiaToEdit); //EXCEPTION HERE
_entities.SaveChanges();
return noticiaToEdit;
}
public noticia GetNoticia(int id)
{
return (from c in _entities.noticiaSet where c.Id == id select c).FirstOrDefault();
}
public IEnumerable<noticia> ListNoticias()
{
return _entities.noticiaSet.ToList();
}
#endregion
}
}
I google the exception and didn't found much help.
I solve it.
The problem is on the EF model.
To solve it you'll need a extension method to persist your data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace MagixCMS.Models
{
public static class Extensions
{
public static void Update(ObjectContext context, string entitySetName, IEntityWithKey entity)
{
entity.EntityKey = context.CreateEntityKey(entitySetName, entity);
context.Attach(entity);
var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
var propertyNameList = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name);
foreach (var propName in propertyNameList)
{
stateEntry.SetModifiedProperty(propName);
}
}
}
}
And in the Edit method you do:
public noticia EditNoticia(noticia noticiaToEdit)
{
//GET THE CONTEXT FOR THE ENTITY
ObjectContext _context = this._entities.noticiaSet.Context;
var noticiaOriginal = GetNoticia(noticiaToEdit.Id);
//UPDATE THE ORIGINAL ENTITY WITH THE NEW VALUES
Extensions.Update(_context, noticiaOriginal.EntityKey.EntitySetName, noticiaToEdit);
//PERSIST THE DATA
_entities.SaveChanges();
return noticiaToEdit;
}

Resources