I am trying to create asp.net mvc app with users for biliard amateur league (this is my practice project), and store players data into MongoDb, but i don't know how to insert picture for player. For now i can put field like Name, Surname etc. Here's my code for:
Create function:
public void Create(Player player)
{
playerCollection.InsertOne(player);
}
Create controller:
public ActionResult Create(Player player)
{
playerModel.Create(player);
return RedirectToAction("Index");
}
Player object:
public class Player
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("Name")]
public string Name{ get; set; }
[BsonElement("Surname")]
public string Surname{ get; set; }
//[BsonElement("Picture")]
//public byte[] Picture{ get; set; } <- I commented on that until I found a solution
}
I guess the HttpPostedFileBase must be used, but ... I do not know how to implement logic. (and if possible without GridFS).
Cure my headaches! :)
Edit
Something like this:
public ActionResult Create(Player player, HttpPostedFileBase choosenPicture)
{
if (ModelState.IsValid)
{
if (choosenPicture != null)
{
player.Picture = new byte[choosenPicture.ContentLength];
Stream str = choosenPicture.InputStream;
str.Read(player.Picture, 0, choosenPicture.ContentLength);
}
try
{
db.Players.Add(player);
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception)
{
ViewBag.Error = "Error :(";
}
}
return View(player);
}
Related
I got an error while getting json data from POST method, am I doing something wrong
C# Code:
public IActionResult signupapi(UserSignUp user)
{
var model = new Models.SignUpModelAPI(HttpContext);
if (user == null)
{
return Content(model.ResponseJsonText(false, string.Format(model.Language("empty"),
HttpContext.Request.Method, HttpContext.Request.Path.Value), Class.CodeResponse.ERROR), new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
}
if (!model.isAllowMethod("POST"))
{
return Content(model.ResponseJsonText(false,string.Format(model.Language("notallowmethod"),
HttpContext.Request.Method,HttpContext.Request.Path.Value),Class.CodeResponse.ERROR),new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
}
return Content(JsonConvert.SerializeObject(user));
}
public class UserSignUp
{
public string fullname { get; set; }
public string username { get; set; }
public string email { get; set; }
public string password { get; set; }
}
And this is the result when i try on reqbin every value i get is null
You need to add FromBody attribute to get your data for the POST operation:
public IActionResult signupapi([FromBody]UserSignUp user)
You can read more on parameter binding on MSDN docs.
I have the following controller's action:
public async Task<IActionResult> Create([FromForm]LanguageViewModel viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
var newLanguage = new Language()
{
Sort = viewModel.Sort,
LangCode = viewModel.Code,
LangName = viewModel.Name
};
await _languageRepository.SaveAsync(newLanguage);
return RedirectToAction("Index");
}
And the following viewModel:
public class LanguageViewModel
{
public int Sort { get; set; }
[Required(ErrorMessage = "Language code is required")]
public string Code { get; set; }
[Required(ErrorMessage = "Language name is required")]
public string Name { get; set; }
}
Note, that LanguageViewModel is not my domain (Entity framework) object. For map on my database I have another one:
public class Language
{
public int LanguageId { get; set; }
public int Sort { get; set; }
public string LangName { get; set; }
public string LangCode { get; set; }
}
So, I'm working with ASP.NET MVC Core 3.1 and I need to check if Language with given code already exists or not. If it exists, I would like to show validation error about it. You can say that I can solve it like:
public async Task<IActionResult> Create([FromForm] LanguageViewModel viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
var lang = await _languageRepository.All.FirstOrDefaultAsync(x =>
x.LangCode.ToUpper() == viewModel.Code.ToUpper());
if (lang != null)
{
ModelState.TryAddModelError("Code", $"The language with code {viewModel.Code} already exists");
}
if (!ModelState.IsValid)
{
return View(viewModel);
}
var newLanguage = new Language()
{
Sort = viewModel.Sort,
LangCode = viewModel.Code,
LangName = viewModel.Name
};
await _languageRepository.SaveAsync(newLanguage);
return RedirectToAction("Index");
}
Ok. it works. But it's ugly :( Maybe there is better solution?
Maybe that would be a little bit cleaner solution.
if (_languageRepository.Any(o => o.LangCode.Equals(txnId, StringComparison.InvariantCultureIgnoreCase)))
{
ModelState.TryAddModelError("Code", $"The language with code {viewModel.Code} already exists");
}
Better to try this introduced after EF Core 5:
https://learn.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations
[Index(nameof(Url), IsUnique = true)]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
Created the API using entity framework model to get the request data from other system and want to update those information into database.
//API
[AllowAnonymous]
public string UploadUser()
{
return "Welcome To SMART Web API";
}
and its accessible using: http://localhost:5958/api/UploadUser
Created the View Model to store the request data:
public class UserRequestViewModel
{
public UserRequestViewModel()
{
REQDTL = new List<Line>();
}
public List<Line> REQDTL { get; set; }
public int? REQLEN { get; set; }
public class Line
{
[Description("Id")]
public int? ID { get; set; }
public int? Id
{
get
{
return this.ID;
}
}
[Description("UserName")]
public int? USERNAME { get; set; }
public int? UserName
{
get
{
return this.USERNAME;
}
}
[Description("Password")]
public string PASSWORD { get; set; }
public string Password
{
get
{
return this.PASSWORD;
}
}
}
public class Summary
{
public int? Id { get; set; }
public int? UserName { get; set; }
public string Password { get; set; }
}
public List<Summary> GetSummary()
{
List<Summary> ret = this.REQDTL
.Where(x => x.Id != null ) //Filter to applicable records
.Select(x => new Summary()
{
Id = x.Id,
UserName = x.UserName,
Password = x.Password,
}
).ToList();
return ret;
}
}
}
My question is how to handle the below request data using my controller (Do I need to use PostAsync / GetAsync/ any other)
{"REQLEN":4,"REQDTL":[{"ID":"48490","USERNAME":"Test1","PASSWORD":"Test1"},{"ID":"48491","UserName":"Test2","Password":"Test2"}]}
In general, to accept a post and use the data to talk to a SQL database, if you are using Web API, then you first create a model for the data format that you expect:
public class MyModel
{
public int ID { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
Then you create an action in your controller like this:
public IHttpActionResult Post([FromBody]MyModel model)
{
if (ModelState.IsValid)
{
string myConnectionString = "...";
string mySqlCommand = "UPDATE MyTable SET...";
using (SqlConnection conn = new SqlConnection(myConnectionString))
using (SqlCommand cmd = new SqlCommand(mySqlCommand, conn))
{
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
return Ok();
}
else
return BadRequest("Invalid data");
}
(Of course, the sample data you posted looks more like a login attempt than something you would want to stick into a database, so I am guessing that you aren't really asking the right question. If you are just learning, I would recommend finding a tutorial that is similar to what you are trying to do. The ASP.Net templates have user authentication built in - it would be much more secure to use what they offer than to try to create your own if you are brand new to this type of work. If you just start from a blank ASP.Net project and click "Run", there will already be a way to login, sign up, etc if you do it right.)
I have a controller which is used to save data in database. The controller looks like below:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = DateTime.Parse(string.Format("{0} {1}", viewModel.Date, viewModel.Time))
};
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
I want to remove this line of code
DateTime.Parse(string.Format("{0} {1}", viewModel.Date, viewModel.Time))
and make this calculations somewhere else in order to keep the controller clean.
Which is the best way to archive this?
From the data given I see that you have used a ViewModel called EmployeeFormViewModel to saperate the logic from the model. I would guess that your ViewModel looks something like below:
public class EmployeeFormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
}
Now, in order to make the changes in controller, i would suggest you make it look like below:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = viewModel.DateTime
};
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
and after that go to your ViewModel and add the new property that you added in the Controller (DateTime). Now your ViewModel should look something like below:
public class EmployeeormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public DateTime DateTime
{
get
{
return DateTime.Parse(string.Format("{0} {1}", Date, Time));
}
}
}
I hope this solves your problem.
To offer a different perspective, I'd suggest you could put it in an extension method. The concept of combining date and time strings doesn't really feel like it should belong to your domain model, it feels like a generic thing that you might want to use across your application (or even in other applications). I would do this...
public static class DateTimeExtensions
{
public static DateTime ParseToDateTime(this string date, string time = null)
{
return string.IsNullOrEmpty(withTime) ? DateTime.Parse(date) : DateTime.Parse($"{date} {time}");
}
}
And in the controller...
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = viewModel.Date.ParseToDateTime(viewModel.Time)
};
EDIT: Additionally...to incorporate etr's answer, which is also a good approach, you could combine the two...
public class EmployeeormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public DateTime DateTime
{
get
{
return Date.ParseToDateTime(Time);
}
}
}
Rich domain is the way.
public class Employee
{
public Employee(int id, object date, object time)
{
Id = id;
DateTime = DateTime.Parse(string.Format("{0} {1}", date, time))
}
public int Id { get; protected set; }
public DateTime DateTime { get; protected set; }
}
And them:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
_context.Employees.Add(new Employee(User.Identity.GetUserId(), viewModel.Date, viewModel.Time));
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
I like strong type binding and a Post method as follows:
public ActionResult Create(EmployeeFormViewModel viewModel)
{
viewModel.Post(User.Identity.GetUserId());
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
The view model looking like this:
public class EmployeeFormViewModel
{
Employee Employee { get; set; }
DateTime Date { get; set; }
DateTime Time { get; set; }
public void Post(int empid)
{
Employee= new Employee
{
EmployeeID = empid,
DateTime = DateTime.Parse(string.Format("{0} {1}", Date, Time))
};
return;
}
}
This is all possible because of the nice MVC Binding engine which generates the EmployeeFormViewModel based on query strings, prior to calling the action method.
I put a "Post" method in all of my ViewModels and let MVC do the work.
I have two models with a one to many relationship
public class Team
{
public int TeamID { get; set; }
public string Name { get; set; }
public virtual ICollection<Player> Players { get; set; }
}
public class Player
{
public int PlayerID { get; set; }
public string Name { get; set; }
public virtual Team Team { get; set; }
}
I would like to change the Team's details view to enable adding a new Player to the team
// GET: /Team/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Player player = db.Players.Find(id);
if (player == null)
{
return HttpNotFound();
}
return View(player);
}
It has been a little hard to find any info on this but from what I can gather the best way to go about this would be to add a viewmodel
public class TeamViewModel
{
public Team team { get; set; }
public Player player { get; set; }
}
and send that to my details view, I have not had much success with this and am not too sure if this is even the right approach.
Any links or guidance would be greatly appreciated.
Update
I have managed to get the details view working but now need to sort out the post method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(TeamViewModel tvm)
{
if (ModelState.IsValid)
{
tvm.player.team = tvm.team;
db.Players.Add(tvm.player);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(tvm);
}
This will create the new player but will not store the relationship so the column TeamID in the player table is empty.
Update 2
I have done some debugging and it seems that my teamViewModel is not storing the team data during the post.
// GET: /Team/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Team team = db.Teams.Find(id);
if (team == null)
{
return HttpNotFound();
}
var teamViewModel = new TeamViewModel()
{
team = team,
player = new Player() { Team = db.Team.Find(id) }
};
return View(teamViewModel);
}
// POST: /Team/Details/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(TeamViewModel teamViewModel)
{
if (ModelState.IsValid)
{
db.Players.Add(teamViewModel.player);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(teamViewModel);
}
So what you want to do is the following:
Display a view with the Team's data.
On the same view have the possibility to add a new Player.
Your view model:
public class TeamViewModel
{
public Team team { get; set; }
public Player player { get; set; }
}
Can handle this job.
Team data should be fetched before generating the view.
Player can be empty - it will just be used for the input fields generation purposes of your view.
You should change your action more less like this:
// GET: /Team/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Team team= db.Team.Find(id); //assuming that this is the id of the team
if (team== null)
{
return HttpNotFound();
}
var teamViewModel = new TeamViewModel() { Team = team, Player = new Player() };
return View(teamViewModel);
}
It is a common practice to use ViewModels. Thanks to this you separate your business models from the models that are used when presenting the data to a user. What is more you can then aggregate all the data you need and you are not limited to passing just one object to the view.