User Input Check Before Insert to Database - asp.net-mvc

I have a form in my MVC 5 Webb App, a very simple form for "contact us":
-Name
-Email
-Subject
-Message (body)
I have to check the strings that the user input.
How can I check it in .NET ?
Update:
As Darin suggested, a Parameterizing Queries will take care of that, but I have a problem with implementation it with my architecture design of my web application:
I have a Ado Helper Class:
public class AdoHelper
{
static string connectionString = ConfigurationManager.ConnectionStrings["SQL_DB"].ConnectionString;
public static DataTable ExecuteDataTable(string query)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
SqlCommand command = new SqlCommand(query, con);
SqlDataAdapter tableAdapter = new SqlDataAdapter(command);
DataTable dt = new DataTable();
tableAdapter.Fill(dt);
return dt;
}
}
public static void ExecuteNonQuery(string query)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
SqlCommand command = new SqlCommand(query, con);
command.ExecuteNonQuery();
}
}
public static object ExecuteScalar(string query)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
SqlCommand command = new SqlCommand(query, con);
return command.ExecuteScalar();
}
}
}
And I have Data Queries Class: ( I display here only the relevant function to this question)
public class DataQueries
{
public static void InsertContactForm(ContactForm form)
{
try
{
string query = "INSERT INTO ContactForm (Name, Email, Subject, Message, Reply) VALUES ( '" + form.Name + "','" + form.Email + "','" + form.Subject + "','" + form.Message + "','" + form.Reply + "')";
AdoHelper.ExecuteNonQuery(query);
}
catch (Exception ex)
{
throw ex;
}
}
}
When I want to insert data to my DB I call to a Data Queries function that communicate with the Ado Helper Class.
So the query pass to Ado Helper function as string well formed and ready to go, this creates a problem because I cant use parameters in the Ado Helper class (where I have SQL command instance).
Are there any workaround to this problem ?
Thanks.

Looks like your AdoHelper class is currently vulnerable to SQL injection. In order to avoid that you need to use parametrized queries. So I would start by refactoring this AdoHelper class so that it suits better those needs:
public class AdoHelper
{
private static string connectionString = ConfigurationManager.ConnectionStrings["SQL_DB"].ConnectionString;
public static int ExecuteNonQuery(string query, IDictionary<string, object> parameters)
{
using (var con = new SqlConnection(connectionString))
using (var command = con.CreateCommand())
{
con.Open();
command.CommandText = query;
foreach (var p in parameters)
{
command.Parameters.AddWithValue(p.Key, p.Value);
}
return command.ExecuteNonQuery();
}
}
}
and then you could call this method in order to perform the INSERT statement:
AdoHelper.ExecuteNonQuery(
"INSERT INTO ContactForm (Name, Email, Subject, Message, Reply) VALUES (#Name, #Email, #Subject, #Message, #Reply)",
new Dictionary<string, object>
{
{ "#Name", "form.Name" },
{ "#Email", "form.Email" },
{ "#Subject", "form.Subject" },
{ "#Message", "form.Message" },
{ "#Reply", "form.Reply" }
}
);

What you need is parametrized queries. In the cmd object in ADO.NET, for example, there is a straight forward to do that:
using (var cmd = new SqlCommand())
{
// Add the input parameter and set its properties.
using (var parameter = new SqlParameter())
{
parameter.ParameterName = "#CategoryName";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = categoryName;
// Add the parameter to the Parameters collection.
cmd.Parameters.Add(parameter);
// Now you can execute query
}
}
http://msdn.microsoft.com/en-us/library/yy6y35y8%28v=vs.110%29.aspx

Related

How to refactor the connectionString in my Entity Framework & ASP.NET MVC project?

I have a large number of stored procedures to work with and I have to work with Entity Framework.
I got for example this controller where I'm just calling the database to show my table:
public class CarguioController : Controller
{
public ActionResult Index(int? page)
{
string cs = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (SqlConnection conn = new SqlConnection(cs))
{
// establece conneciĆ³n
SqlParameter param1 = new SqlParameter();
param1.ParameterName = "#MODO";
param1.SqlDbType = SqlDbType.Int;
param1.Value = 2;
SqlCommand cmdProcedure = new SqlCommand(#"Almacen.[PRC_Carguio]", conn);
cmdProcedure.Parameters.Add(param1);
conn.Open();
cmdProcedure.CommandType = CommandType.StoredProcedure;
SqlDataReader dr = cmdProcedure.ExecuteReader();
List<CarguioViewModel> lst = new List<CarguioViewModel>();
int pageNumber = page ?? 1;
int pageSize = 8;
if (dr.HasRows)
{
while (dr.Read())
{
lst.Add(new CarguioViewModel
{
Carguio_ID = dr.GetInt32(0),
Vehiculos_ID = dr.GetInt32(1),
ManifiestoCarga_ID = dr.GetInt32(2),
Guia_ID = dr.GetInt32(3),
Programaciones_ID = dr.GetInt32(4),
Numero = dr.GetInt32(5),
NroMobil = dr.GetString(6),
Fecha = dr.GetDateTime(7),
Usuarios_ID = dr.GetInt32(8),
Sucursales_IS = dr.GetInt32(9)
});
//display retrieved record
}
return View(lst.ToPagedList(pageNumber, pageSize));
}
else
{
Console.WriteLine("No data found.");
}
dr.Close();
conn.Close();
}
return View();
}
}
As you can see, I have to connect with the SQL Server database many times. Maybe you have done a similar job with ASP.NET MVC projects or have any idea to refactor my code?
I have more than 30 tables and everyone has more a Crud and other functions.
I've been searching for this but there is just the same example.
string cs = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
You can get create in General Utility class to read the connection, so that It is stay in one place in the code and read connection value from the Genral Utility class wherever you need it.
void Main()
{
string cn = GeneralUtility.getConnectionString();
}
public class GeneralUtility
{
public static string getConnectionString()
{
string cs = "";
try
{
cs = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
}
catch (Exception ex)
{
throw new Exception("Connection String Error " + ex.Message.ToString());
}
return cs;
}
}
I added a new element called ADO.Net Entity Data Model, where I retrieve all my Stored Procedures, It is helpful
I added a new element called ADO.Net Entity Data Model
Well, now my code is shorter than before:
public ActionResult Index(int? page)
{
List<CarguioModel> lst = new List<CarguioModel>();
int pageNumber = page ?? 1;
int pageSize = 8;
using (MarviBKPEntities prcAlm = new MarviBKPEntities())
{
List<PRC_Carguio_Result> prc = prcAlm.PRC_Carguio(2, null, null, null, null, null, null, null, null, null, null).ToList();
return View(prc.ToPagedList(pageNumber, pageSize));
}
return View();
}
Whta do you think? Could it cause some bad performance?

Crud operation in asp.net mvc

I have an assignment in ASP.NET MVC and I try to write a crud operation without Entity Framework, but the code is not working correctly.
This is my code:
List<bookModel> books = new List<bookModel>();
SqlConnection con = new SqlConnection("Data Source=DESKTOP-VKO8311;Initial Catalog=BookStore;Integrated Security=True");
string query = "SELECT * FROM books";
SqlCommand command = new SqlCommand(query, con);
try
{
con.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
books.Add(new bookModel
{
Title = reader["Title of book"].ToString(),
Author = reader["Author"].ToString(),
Price = reader["Price"].ToString()
});
con.Close();
}
}
catch (Exception ex)
{
con.Close();
}
return View(books);
[HttpGet]
public ActionResult NewBook()
{
ViewBag.Title = "Add New Book";
return View();
}
[HttpPost]
public ActionResult NewBook(bookModel model)
{
SqlConnection con = new SqlConnection("Data Source=DESKTOP-VKO8311;Initial Catalog=BookStore;Integrated Security=True");
string query = "insert into books values(#Ti, #au, #pr)";
SqlCommand command = new SqlCommand(query, con);
command.Parameters.Add("#Ti", System.Data.SqlDbType.VarChar);
command.Parameters["#Ti"].Value = model.Title;
command.Parameters.Add("#au", System.Data.SqlDbType.VarChar);
command.Parameters["#au"].Value = model.author;
command.Parameters.Add("#pr", System.Data.SqlDbType.VarChar);
command.Parameters["#pr"].Value = model.Price;
try
{
con.Open();
command.ExecuteNonQuery();
MessageBox.Show("insert was successful");
return RedirectToAction("books");
}
catch (Exception ex)
{
con.Close();
}
return View();
}
The books.cshtml does not show the result from the database and also the newbook.cshtml does not redirect the create result in the database also.
Any help please?
Your code needs refactoring, but the biggest issue is where you are closing your connection. You don't do it while you're iterating the data reader. Also, take the connection close out of your exception handler. You're better off enclosing it in a using block.
while (reader.Read())
{
books.Add(new bookModel
{
Title = reader["Title of book"].ToString(),
Author = reader["Author"].ToString(),
Price = reader["Price"].ToString()
});
}

StoredProcedure custom sink for Serilog

I want to call a stored procedure using Serilog. I am aware that there is no such sink so I am creating a custom Sink. I have my logic to call the stored procedure inside the Emit method of the StoredProcedureSink that implements ILogEventSink. Now, this stored procedure returns a value. How can I get this value when I use Log.Information();
class StoredProcedureSink : ILogEventSink
{
private string _connectionString;
public StoredProcedureSink(string connectionString)
{
_connectionString = connectionString;
}
public void Emit(LogEvent logEvent)
{
var conn = new SqlConnection(_connectionString);
conn.Open();
SqlCommand cmd = new SqlCommand(logEvent.MessageTemplate.ToString().Substring(0, logEvent.MessageTemplate.ToString().IndexOf('{')), conn);
cmd.CommandType = CommandType.StoredProcedure;
var properties = logEvent.Properties.GetValueOrDefault("SqlParams");
var json = JObject.Parse(properties.ToString().Substring(properties.ToString().IndexOf('{') - 1));
foreach(var kvp in json)
{
cmd.Parameters.Add(new SqlParameter(kvp.Key, ((JValue)kvp.Value).Value));
}
cmd.ExecuteNonQuery();
//I would like to read the value returned by the stored proc.
}
}
//I have a wrapper DBLogger in which I configure the serilog. I have published DBLogger as a nuget package so I can use it in all my apps.
public class DBLogger()
{
public DBLogger()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.StoredProcedureSink(
"connectionString")
.CreateLogger();
}
public void Information(string storedProcedureName, T parameters)
{
try
{
Log.Information(storedProcedureName, parameters);
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
}
}
public class Program
{
static void Main()
{
var logger = new DBLogger();
logger.Information("storedProcedureName", params); //need the Id returned by the stored proc here.
}
}
That's not likely to be workable in practice using the normal Serilog model.
The standard processing involves the LogEvent being captured on the main thread, then supplied to each sink in turn - usually asynchronously, and often buffered.
The other concern is that in general, a sink definitely will not be propagating exceptions to the caller either (there is Audit logging, but even that's not intended for this sort of communication).
It seems to me that the sort of auditing you're seeking to accomplish is some distance from Serilog's sweet spot (I may be wrong though - please add some more detail to your question if so).
If you absolutely must do this, you can add an Enricher when logging, which sequesters a callback Action into the LogEvent's Properties, and have that pass it back. Please think long and hard before actually doing that though!
I ended up creating a static variable of StoredProcedureSink class and assigning the return value to that variable. Not sure if this would be the best way to do it.
class StoredProcedureSink : ILogEventSink
{
private string _connectionString;
**public static int returnValue;**
public StoredProcedureSink(string connectionString)
{
_connectionString = connectionString;
}
public void Emit(LogEvent logEvent)
{
var outputParam = new SqlParameter()
{
Direction = ParameterDirection.Output
};
try
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand(logEvent.MessageTemplate.ToString().Substring(0, logEvent.MessageTemplate.ToString().IndexOf('{')), conn))
{
conn.Open();
cmd.CommandType = CommandType.StoredProcedure;
var properties = logEvent.Properties.GetValueOrDefault("SqlParams");
var jsonProp = JObject.Parse(properties.ToString().Substring(properties.ToString().IndexOf('{') - 1).Replace(#"\",""));
var lastParam = jsonProp.Last;
foreach (var kvp in jsonProp)
{
if(kvp.Key == lastParam.Path)
{
outputParam.ParameterName = kvp.Key;
outputParam.SqlDbType = SqlDbType.Int;
cmd.Parameters.Add(outputParam);
break;
}
cmd.Parameters.Add(new SqlParameter(kvp.Key, ((JValue)kvp.Value).Value));
}
cmd.ExecuteNonQuery();
}
**returnValue = (int)outputParam.Value;**
}
}
catch(System.Exception e)
{
Debug.WriteLine(e.Message);
}
}
}
public class DBLogger : ILogger
{
public DBLogger()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.StoredProcedureSink(
"connectionString")
.CreateLogger();
}
~DBLogger()
{
Log.CloseAndFlush();
}
public static IHost CreateHostBuilder(string[] args) =>
new HostBuilder()
.UseSerilog() // <- Add this line
.Build();
public int Information<T>(string storedProcedureName, T parameters)
{
try
{
Log.Information(storedProcedureName, parameters);
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
**return StoredProcedureSink.returnValue;**
}
}

ASP.NET Core 2 - How to call and manipulate a Database class from a controller

I am using ASP.NET Core 2 and I have a controller that runs Tasks.
For example, the one below does a simple file upload, I have others that run other Tasks or Actions and I want to be able to log the events or tasks into my database.
So I need to create a class to run INSERTs or UPDATEs on a database.
How do I create a class that does my database manipulation either using DbContext or call a stored procedure using a class (and not a controller)?
Here is a sample of one of my controller's code:
public async Task<IActionResult> Uploads(string fullName, IFormFile pic)
{
try {
string user = null;
try
{
user = User.Identity.Name.ToString();
}
catch (Exception err)
{
user = "anonymous";
}
if (user == null)
{
user = "";
}
string path = he.WebRootPath + "/uploads/" + user ;
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
DateTime date = DateTime.Now ;
var dates = date.ToLongDateString();
var ext = Path.GetExtension(path).ToLowerInvariant();
var fileName = Path.Combine(he.WebRootPath + "/uploads/" + "/" + user, Path.GetFileName(pic.FileName));
var f = Path.Combine(he.WebRootPath + "/uploads/" + "/" + user, dates+Path.GetFileName(pic.FileName));
int i = 0;
ViewData["fname"] = fullName;
if (pic != null || pic.Length == 0)
{
using (var stream = new FileStream(fileName, FileMode.Create))
{
await pic.CopyToAsync(stream);
}
ViewData["fileLocation"] = "/uploads/" + user + "/" + Path.GetFileName(pic.FileName);
// }
}
You can use dependency injection to inject your DbContext into your controller:
public class MyController : Controller
{
private readonly DbContext _db;
public MyController(DbContext db)
{
_db = db;
}
public async Task<IActionResult> Uploads(string fullName, IFormFile pic)
{
_db.ExecuteSql("INSERT ...");
}
}
Once you have your Db context, you can do what you need with it.

How to use Stored Procedure in asp.net core with boilerplate architecture?

I am using asp.net core application with abp(Asp.net BoilerPlate) framework. I want to use stored procedure to get the data and also to implement CRUD operations in this code first architecture. What will be best way to do so?
Thanks in advance
Here is an example that sends a parameter to a stored procedure to delete a user:
public async Task DeleteUser(EntityDto input)
{
await Context.Database.ExecuteSqlCommandAsync(
"EXEC DeleteUserById #id",
default(CancellationToken),
new SqlParameter("id", input.Id)
);
}
See: Using Stored Procedure, User Defined Function and Views in a Custom Repository with ASP.NET Boilerplate
Source code is published on Github: https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/StoredProcedureDemo
Create your custom repository, so you can access the dbcontext object and execute sql query by using this context. I have created some helper methods in my custom repository, hope it can help you:
/// <summary>
/// Map data from datareader to list object
/// </summary>
private List<T> MapToList<T>(DbDataReader reader)
{
var result = new List<T>();
if (reader.HasRows)
{
var props = typeof(T).GetRuntimeProperties();
var colMapping = reader.GetColumnSchema().Where(x => props.Any(p => p.Name.Equals(x.ColumnName, StringComparison.OrdinalIgnoreCase))).ToDictionary(key => key.ColumnName.ToLower());
while (reader.Read())
{
var item = Activator.CreateInstance<T>();
foreach (var prop in props)
{
var propValue = reader.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
prop.SetValue(item, propValue == DBNull.Value ? null : propValue);
}
result.Add(item);
}
}
return result;
}
/// <summary>
/// Execute command return empty result
/// </summary>
public int ExecuteSqlCommand(string sqlCommand, Dictionary<string, object> #params)
{
List<SqlParameter> sqlParams = new List<SqlParameter>();
foreach (var item in #params)
{
if (item.Value != null)
sqlParams.Add(new SqlParameter(item.Key, item.Value));
else
sqlParams.Add(new SqlParameter(item.Key, DBNull.Value));
}
if (#params.Count > 0)
sqlCommand += " ";
sqlCommand += String.Join(",", #params.Select(p => p.Key));
return Context.Database.ExecuteSqlCommand(sqlCommand, sqlParams.ToArray());
}
/// <summary>
/// Execute stored procedure return set of rows
/// </summary>
public IEnumerable<TResult> ExecuteStoredProcedureWithRowsResult<TResult>(string name, Dictionary<string, object> #params) where TResult : class
{
//Fix exception: ExecuteReader requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.
UnitOfWorkManager.Current.Options.IsTransactional = false;
using (var command = Context.Database.GetDbConnection().CreateCommand())
{
var result = new List<TResult>();
string sqlCmd = String.Format("{0} ", name);
if (command.Connection.State != System.Data.ConnectionState.Open)
command.Connection.Open();
try
{
foreach (var item in #params)
{
if (item.Value != null)
command.Parameters.Add(new SqlParameter(item.Key, item.Value));
else
command.Parameters.Add(new SqlParameter(item.Key, DBNull.Value));
command.CommandText = sqlCmd;
command.CommandType = System.Data.CommandType.StoredProcedure;
using (var reader = command.ExecuteReader())
{
result = MapToList<TResult>(reader);
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
command.Connection.Close();
}
return result;
}
}
And in application service, inject your custom repository, and can call stored procedure like:
var #params = new Dictionary<string, object>();
#params.Add("Id", 1);
var result = _customRepository.ExecuteStoredProcedureWithRowsResult<UserResult>("sp_getUsers", #params);
If you dont want to use EF, you can use Dapper, it is easy to use: http://www.c-sharpcorner.com/article/asp-net-core-web-api-with-dapper-and-vs-2017/

Resources