I know what you're thinking, but hear me out.
I have a model:
public partial class DealerBudget
{
public int DealerBudgetID { get; set; }
public int DealerID { get; set; }
public int BudgetYr { get; set; }
public int BudgetMonth { get; set; }
public decimal BudgetAmt { get; set; }
public bool Confirmed { get; set; }
public short BudgetTypeID { get; set; }
}
A list of these is in a vm:
public class DealerBudgetVM
{
public List<CNHi.StMarys.ND.DBP.Web.Models.DealerBudget> YrBudget { get; set; }
}
They're filled in a Get:
public ActionResult Add()
{
DealerBudgetVM result = new DealerBudgetVM(); //initialised to have all 24 months
List<DealerBudget> dbList = db.DealerBudgets.Where(x => x.DealerID == dbp.dealerID && x.BudgetTypeID == (short)2 && (x.BudgetYr == DateTime.Now.Year || x.BudgetYr == DateTime.Now.Year + 1)).ToList();
//use the data from the DB if it exists
foreach (DealerBudget bud in result.YrBudget)
{
foreach (DealerBudget budDB in dbList)
{
bud.DealerID = dbp.dealerID;
bud.BudgetTypeID = 2; //Service
if (budDB.BudgetYr == bud.BudgetYr && budDB.BudgetMonth == bud.BudgetMonth)
{
bud.DealerBudgetID = budDB.DealerBudgetID;
bud.BudgetAmt = budDB.BudgetAmt;
bud.Confirmed = budDB.Confirmed;
}
}
}
return PartialView(result);
}
And displayed in a view. If each month's value is Confirmed, the textbox is disabled:
#for( int i = 0; i < 12; ++i)
{
#Html.HiddenFor(model => model.YrBudget[i].DealerBudgetID)
#Html.HiddenFor(model => model.YrBudget[i].BudgetTypeID)
#Html.HiddenFor(model => model.YrBudget[i].DealerID)
#Html.HiddenFor(model => model.YrBudget[i].BudgetYr)
#Html.HiddenFor(model => model.YrBudget[i].BudgetMonth)
#Html.HiddenFor(model => model.YrBudget[i].Confirmed)
<div class="pure-u-1-3 pure-u-md-1-6 pure-u-lg-1-12 s-box">
<span class="pure-u-1">#Html.DisplayFor(model => model.YrBudget[i].MonthDesc)</span>
#Html.TextBoxFor(model => model.YrBudget[i].BudgetAmt, (Model.YrBudget[i].Confirmed == false) ? (object)new {type="number", Value=Model.YrBudget[i].BudgetAmt.ToString("0.00")} : (object)new {disabled = "disabled", Value=Model.YrBudget[i].BudgetAmt.ToString("0.00")} )
</div>
}
The user enters new budget figures and saves:
[HttpPost]
public ActionResult Added(DealerBudgetVM vm)
{
if (ModelState.IsValid)
{
this.ModelState.Clear();
foreach (DealerBudget budVM in vm.YrBudget.Where(x => x.Confirmed == false && x.BudgetAmt > 0).ToList())
{
//If it's not in the DB, add it.
if (budVM.DealerBudgetID == 0)
{
DealerBudget budNew = new DealerBudget { BudgetYr = budVM.BudgetYr, DealerID = budVM.DealerID, BudgetMonth = budVM.BudgetMonth, BudgetTypeID = budVM.BudgetTypeID, BudgetAmt = budVM.BudgetAmt };
if (budNew.BudgetAmt > 0)
{
budNew.Confirmed = true;
budVM.Confirmed = true;
}
db.DealerBudgets.Add(budNew);
db.SaveChanges();
budVM.DealerBudgetID = budNew.DealerBudgetID;
}
else
{
//update and confirm
DealerBudget budDB = db.DealerBudgets.Where(x => x.DealerBudgetID == budVM.DealerBudgetID).FirstOrDefault();
if (budDB == null)
{
}
else
{
budDB.BudgetAmt = budVM.BudgetAmt;
budDB.Confirmed = true;
db.SaveChanges();
budVM.Confirmed = true;
}
}
}
return RedirectToAction("Index", "ServicePerformance");
}
else
{
return PartialView(vm);
}
}
...and nothing changes on the view. The new (confirmed) budget textboxes should be disabled, and they aren't.
"No problem!" you say. "this guy just needs to..."
Use ModelState.Clear(). Tried it, didn't work.
Remove keys from the ModelState. Tried it, didn't work.
Use Post-Redirect-Get. Tried it(both redirecting to the partial and to the master. Current code above still using it). Didn't work.
Clear the cache. Tried many, many methods and iterations. Nothing worked.
After the Post and save, the subsequent Get has all the correct saved values being sent to the View in the VM, but the old ones (ie user-entered values with confirmed flag=false) are still shown.
If the user then goes to another page and then returns, the page displays as expected.
All the posts I've seen here (and elsewhere) confidently declare that one of the four solutions above will fix it. I've wasted a day now banging my head against a wall trying to disable a stupid textbox.
Any other ideas? Is it because of nesting or partial views or something?
Related
I have a class that should represent a controller's action parameter and I'd like its properties to be "required" (meaning, you get a status code 400 or something in case it's passed as null). I managed to get it done using System.ComponentModel.DataAnnotations, but the ErrorMessage that I pass to the constructor of the Required attribute is never shown.
[XmlRoot(ElementName = "root")]
public class Request
{
[XmlElement(ElementName = "prop")]
[Required(ErrorMessage = "The property is required.")]
public string Property { get; set; }
[XmlElement(ElementName = "another")]
[Required(ErrorMessage = "The property is required.")]
public string Another { get; set; }
}
Action:
[HttpPost]
public IActionResult Post([FromBody] Request value)
{
return Ok(value); //ignore this, it's just for testing purposes...
}
However, if I don't pass the Property value, I get a 400 that doesn't contain the ErrorMessage I passed earlier. Am I missing something here?
<ValidationProblemDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Title>One or more validation errors occurred.</Title>
<Status>400</Status>
</ValidationProblemDetails>
My Startup has Xml formatters added to it:
services.AddMvc(options =>
{
options.RespectBrowserAcceptHeader = true;
options.InputFormatters.Insert(0, new XmlSerializerInputFormatter(options));
options.OutputFormatters.Insert(0, new XmlSerializerOutputFormatter());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
The body of the request looks like this, and it doesn't have "Property":
<root>
<another>Test</another>
<!-- Property "Property" is missing here -->
</root>
Kudos to Code Rethinked for the huge help - Customizing automatic HTTP 400 error response in ASP.NET Core Web APIs.
An approach that I managed to figure out eventually includes the use of services.Configure in my Startup.ConfigureServices method.
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
return new OkObjectResult(new CustomResponse(someStatusCode, context))
{
ContentTypes = { "application/xml" }
};
};
});
So, I made a class named CustomResponse that holds the status code I want to retrieve and all the validation errors (including the ones where my Required property was not passed to the API).
[XmlRoot(ElementName = "rcemsTrxSubReqAck")]
public class CustomResponse
{
[XmlElement(ElementName = "Status")]
public string Status { get; set; }
[XmlArray(ElementName = "Errors"), XmlArrayItem(ElementName = "Error")]
public string[] Errors { get; set; }
public CustomResponse(int status, ActionContext context)
{
Status = status;
Errors = ConstructErrorMessages(context);
}
private string[] ConstructErrorMessages(ActionContext context)
{
if (context == null)
{
return null;
}
string[] arr = new string[context.ModelState.ErrorCount];
int i = 0;
foreach (var keyModelStatePair in context.ModelState)
{
var key = keyModelStatePair.Key;
var errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
if (errors.Count == 1)
{
var errorMessage = GetErrorMessage(errors[0]);
arr[i] = $"{key}: {errorMessage}";
}
else
{
var errorMessages = new string[errors.Count];
for (var j = 0; j < errors.Count; j++)
{
errorMessages[j] = GetErrorMessage(errors[j]);
}
arr[i] = $"{key}: {errorMessages.ToString()}";
}
i++;
}
}
return arr;
}
private string GetErrorMessage(ModelError error)
{
return string.IsNullOrEmpty(error.ErrorMessage) ? "The input was not valid." : error.ErrorMessage;
}
}
I am trying to access my page at: https://localhost:44319/Analyze/Index/6
The problem is that my drop down list always selects the first item in the list instead of the one provided by ID. While stepping through the debugger, before the View() is returned, I see that the SelectList was populated correctly.
AnalyzeController.cs
public IActionResult Index(int? Id)
{
return Index(Id ?? getStatementEndingById(Id).StatementEndingId);
}
[HttpPost]
public IActionResult Index(int StatementEndingId)
{
var statementEnding = getStatementEndingById(StatementEndingId);
ViewBag.StatementEndingId = new SelectList(
_context.StatementEnding.OrderByDescending(s => s.StatementEndingId),
"StatementEndingId",
"Name",
statementEnding);
return View(getPayments(statementEnding));
}
private StatementEnding getStatementEndingById(int? statementEndingId)
{
StatementEnding statementEnding;
if (statementEndingId.HasValue)
{
statementEnding = _context.StatementEnding.FirstOrDefault(s => s.StatementEndingId == statementEndingId);
}
else
{
statementEnding = _context.StatementEnding.OrderByDescending(s => s.StatementEndingId).FirstOrDefault();
}
return statementEnding;
}
Setting DropDownList in Razor
#Html.DropDownList("StatementEndingId", null, new { #class = "form-control mb-2 mr-sm-2" })
I am using ASP.NET Core 2.1.
Any suggestions are much appreciated. Thanks in advance.
First i would recomend to create a typed model, something like this one :
public class StatementViewModel
{
public int StatementEndingId { get; set; }
public List<SelectListItem> StatementEndings { get; set; }
}
Second fill the Model with all dropdown options (StatementEndings) and the selected one (StatementEndingId)
public IActionResult Index()
{
var model = new StatementViewModel();
model.StatementEndingId = getStatementEndingById(Id).StatementEndingId;
model.StatementEndings = _context.StatementEnding.OrderByDescending(s => s.StatementEndingId).Select(p => new SelectListItem() { Text = p.Name, Value = p.StatementEndingId }).ToList();
return View(model);
}
And for the last, in the view
#model StatementViewModel
#Html.DropDownListFor(m => m.StatementEndingId, Model.StatementEndings, null, new { #class = "form-control mb-2 mr-sm-2" })
I've looking all over for something similar, couldn't find nothing..
I'm using ASP.NET MVC 4. I'm building a page so the users in my app can manage the permissions associated with each role.
So i have a view with #htmlDropDownList to show all the available roles, and below, one #Html.CheckBox for each Permission of the role wich is selected above.
The first time the view is rendered, the checkboxes are all set to true or false, according to the permission of that role.All is fine, life is good :) . When the value of the drop is changed, i post the SelectedRoleId using $.ajax. Then, i fetch all the permissions of the new selected role.
While in debug, in the razor view, i can confirm the new values (true or false) inside the model are correct. The problem is that the checkboxes show the old values, before the role was changed..
This is my first question asked, so i'll have to apologize if the question is not being made the best way.
And thx in advance to all of you :)
So here's my Controller:
public ActionResult Index(int ? SelectedRoleId)
{
ManagePermissionsViewModel model = new ManagePermissionsViewModel();
if (SelectedRoleId == null)
{
model.SelectedRoleID = 1; // value 1 is the supervisor Role
}
else
{
model.SelectedRoleID = SelectedRoleId;
}
//values for the dropdownlist of Roles
var items = from x in db.UserRoles
select x;
model.RoleList = new SelectList(items, "Id", "DESCRIPTION");
//gets all the permissions of the selected role
model.EntirePermissionList = (from k in db.Permissions
select new Permission
{
IdPermission = k.Id,
PermissionDescription = k.Description,
IsSet = db.RolePermissions.Any(n => n.RoleId == model.SelectedRoleID && n.PermissionId == k.Id),
PermissionGroupId = (int)k.PermissionGroupId
}).ToList();
//Gets all the groups of Permissions
model.ListPermissionGroups = (from l in db.PermissionGroups
select new PermissionGroup
{
Id = l.Id,
Description = l.Description
}).ToList();
return View(model);
}
[HttpPost]
public ActionResult Index(FormCollection form) {
switch (form["SubmitButton"])
{
case "Save":
SavePermissions();
break;
default:
return RedirectToAction("Index", new RouteValueDictionary(new { controller = "ManagePermissions", action = "Index", SelectedRoleId = Convert.ToInt32(form["SelectedRoleId"]) }));
}
return View();
}
And here is my View:
'#model AML.Web.Models.ManagePermissionsViewModel
#using (Html.BeginForm("Index", "ManagePermissions", FormMethod.Post, new { id = "MyForm" }))
{
#Html.Label("Role :", htmlAttributes: new { #class = "control-label col-md-2" })
#Html.DropDownList("RoleId", Model.RoleList, new { id = "RoleId" })
<div>
#foreach (var item in Model.ListPermissionGroups)
{
<h3> #item.Description</h3>
foreach (var permission in Model.EntirePermissionList.Where(n => n.PermissionGroupId == item.Id))
{
<h5>
#permission.PermissionDescription
#Html.CheckBox("Chk_Permisssion", permission.IsSet)
</h5>
}
}
</div>
<input type="submit" value="Save" name="SubmitButton" class="btn btn-default" />
}
#section Scripts {
<script type="text/JavaScript">
$(document).ready(function () {
$("#RoleId").change(function (e) {
e.preventDefault();
$.ajax({
url: "/ManagePermissions/Index",
cache: false,
type: "POST",
data: { 'SelectedRoleId': $(this).val() },
dataType: "json",
success: function (result) { console.log("Sucess!"); },
error: function (error) { console.log("Error!"); }
})
});
});
</script>
}
And my viewModel:
public class ManagePermissionsViewModel
{
public int? SelectedRoleID { get; set; }
public string SelectedRoleDescription { get; set; }
public SelectList RoleList { get; set; }
public List<Permission> EntirePermissionList { get; set; }
public List<PermissionGroup> ListPermissionGroups { get; set; }
}
public class Permission
{
public int IdPermission { get; set; }
public bool IsSet { get; set; }
public string PermissionDescription { get; set; }
public int PermissionGroupId { get; set; }
}
public class PermissionGroup {
public int Id { get; set; }
public string Description{ get; set; }
}
UPDATE 1 -
Well, i think i got it. Let me post my approach
In the View:
#Html.DropDownListFor(n => n.SelectedRoleID, Model.RoleList,null,
new { onchange = "document.location.href = '/ManagePermissions/Index?SelectedRoleId=' + this.options[this.selectedIndex].value;" })
<div>
#foreach (var item in Model.ListPermissionGroups)
{
<h3> #item.Description</h3>
foreach (var permission in Model.EntirePermissionList.Where(n => n.PermissionGroupId == item.Id))
{
<h5>
#permission.PermissionDescription
<input type="checkbox" id="#permission.IdPermission" checked="#permission.IsSet">
</h5>
}
}
</div>
And in the Controller:
public ActionResult Index(int? SelectedRoleId)
{
ManagePermissionsViewModel model = new ManagePermissionsViewModel();
ModelState.Clear();
if (SelectedRoleId == null)
{
model.SelectedRoleID = 1;
}
else
{
model.SelectedRoleID = SelectedRoleId;
}
var items = from x in db.UserRoles
select x;
model.RoleList = new SelectList(items, "Id", "DESCRIPTION");
model.EntirePermissionList = (from k in db.Permissions
select new Permission
{
IdPermission = k.Id,
PermissionDescription = k.Description,
IsSet = db.RolePermissions.Any(n => n.RoleId == model.SelectedRoleID && n.PermissionId == k.Id),
PermissionGroupId = (int)k.PermissionGroupId
}).ToList();
model.ListPermissionGroups = (from l in db.PermissionGroups
select new PermissionGroup
{
Id = l.Id,
Description = l.Description
}).ToList();
ModelState.Clear();
return View(model);
}
Now each time the Drop changes value, the permissions in the checkboxes are updated. I got it to work with the attribute on the drop, "on change = Document.location.hef = URL". Is this a good approach? Or should i use something like ajax request ?
UPDATE 2
The Controller:
public async Task<ActionResult> Index(int? SelectedRoleId)
{
if (SelectedRoleId == null)
{
SelectedRoleId = 1;
}
var model = await GetSelectedPermissions(SelectedRoleId);
return this.View("Index",model);
}
[HttpGet]
public async Task<ActionResult> GetPermissions(string Id)
{
var SelectedRoleId = int.Parse(Id);
var model = await this.GetSelectedPermissions(SelectedRoleId);
return PartialView("_ManagePermissions", model);
}
private async Task<ManagePermissionsViewModel> GetSelectedPermissions(int? SelectedRoleId)
{
ModelState.Clear();
ManagePermissionsViewModel model = new ManagePermissionsViewModel();
model.SelectedRoleID = SelectedRoleId;
var items = from x in db.UserRoles
select x;
model.RoleList = new SelectList(items, "Id", "DESCRIPTION");
model.EntirePermissionList = await (from k in db.Permissions
select new Permission
{
IdPermission = k.Id,
PermissionDescription = k.Description,
IsSet = db.RolePermissions.Any(n => n.RoleId == model.SelectedRoleID && n.PermissionId == k.Id),
PermissionGroupId = (int)k.PermissionGroupId
}).ToListAsync();
model.ListPermissionGroups = await (from l in db.PermissionGroups
select new PermissionGroup
{
Id = l.Id,
Description = l.Description
}).ToListAsync();
return model;
}
The View
<h2>Permissions - Ajax with Partial View</h2>
#using (Html.BeginForm("SaveData", "ManagePermissions", FormMethod.Post, new { id = "MyForm" }))
{
#Html.Label("Role :", htmlAttributes: new { #class = "control-label col-md-2" })
#Html.DropDownListFor(n => n.SelectedRoleID, Model.RoleList, null, null)
<div id="target">
#Html.Partial("~/Views/Shared/_ManagePermissions.cshtml", Model)
</div>
<input type="submit" value="Save" name="SubmitButton" class="btn btn-default" />
}
#section Scripts {
<script type="text/javascript">
$(document).ready(function () {
$("#SelectedRoleID").change(function () {
var SelectedRoleID = $("#SelectedRoleID").val();
$("#target").load('#(Url.Action("GetPermissions","ManagePermissions",null, Request.Url.Scheme))?Id=' + SelectedRoleID);
});
});
</script>
}
And the Partial View:
<div>
#foreach (var item in Model.ListPermissionGroups)
{
<h3> #item.Description</h3>
foreach (var permission in Model.EntirePermissionList.Where(n => n.PermissionGroupId == item.Id))
{
<h5>
#permission.PermissionDescription
<input type="checkbox" id="#permission.IdPermission" checked="#permission.IsSet">
</h5>
}
}
</div>
I'm very new to ASP.NET MVC and I'm having trouble with something that seems like it should be a no-brainer.
With this ViewModel:
public enum Step
{
One = 1,
Two = 2,
Three = 3
}
public class TestViewModel
{
public string Description
{
get
{
return "Current step is " + this.Step;
}
}
public Step Step { get; set; }
public string Dummy{ get; set; }
public TestViewModel()
{ }
public TestViewModel(Step step)
{
this.Step = step;
}
}
and this view:
#using MvcApplication1
#model TestViewModel
#using (Html.BeginForm("Test", "Home"))
{
if (Model.Step == Step.One)
{
#Html.HiddenFor(m => m.Step)
#Html.HiddenFor(m => m.Dummy)
<p>#Model.Description</p>
}
else if (Model.Step == Step.Two)
{
#Html.HiddenFor(m => m.Step)
#Html.HiddenFor(m => m.Dummy)
<p>#Model.Description</p>
}
else if (Model.Step == Step.Three)
{
#Html.HiddenFor(m => m.Step)
#Html.HiddenFor(m => m.Dummy)
<p>#Model.Description</p>
}
<input type="submit" value="Continue" />
}
and this controller:
public ActionResult Test()
{
TestViewModel model = new TestViewModel(Step.One);
return View(model);
}
[HttpPost]
public ActionResult Test(TestViewModel model)
{
Debug.Print("Enter: Step = {0}", model.Step);
switch (model.Step)
{
case Step.One:
model.Step = Step.Two;
model.Dummy = "2";
break;
case Step.Two:
model.Step = Step.Three;
model.Dummy = "3";
break;
case Step.Three:
model.Step = Step.One;
model.Dummy = "1";
break;
}
Debug.Print("Enter: Step = {0}", model.Step);
return View(model);
}
On the first click of the button the controller sets model.Step to Step.Two and my view is updated correctly.
But on the second (and any subsequent) click of the button model.Step is read as Step.One instead of Step.Two so nothing is updated on my view.
Is there anything obvious that I'm missing here? Why are the values not being read/saved correctly?
You don't need if else blocks in your view. You are basically doing the same thing. This will also work:
#using (Html.BeginForm("Test", "Home"))
{
#Html.HiddenFor(m => m.Step)
<p>#Model.Description</p>
<input type="submit" value="Continue" />
}
After posting the form, you are returning a view in the same action. ASP.NET MVC only uses values from the POST request in HTML helpers, ignoring the updated values in your action. You can see it in HTML after you make the first request and here's the reason why it's implemented that way.
I would suggest implementing Post-Redirect-Get pattern. After updating the value, make a redirection to other action.
[HttpPost]
public ActionResult Test(TestViewModel model)
{
Debug.Print("Enter: Step = {0}", model.Step);
switch (model.Step)
{
case Step.One:
model.Step = Step.Two;
break;
case Step.Two:
model.Step = Step.Three;
break;
case Step.Three:
model.Step = Step.One;
break;
}
Debug.Print("Enter: Step = {0}", model.Step);
return RedirectToAction("SomeAction", model);
}
This will serialize the model into querystring. Better way would be to pass some ID as a parameter.
I have a small demo application to work around Binary data stored in the database. See below for related code:
Entity Class:
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
[Display(Name = "Full Name:")]
public string Name { get; set; }
[DataType(DataType.Upload)]
[Display(Name = "Photo:")]
public byte[] ImageData { get; set; }
[HiddenInput(DisplayValue = false)]
public string ImageMimeType { get; set; }
Edit Action:
[HttpGet]
public ActionResult Edit(int id)
{
var mensPlayer = _dataSource.MensPlayers.FirstOrDefault(p => p.Id == id);
return View(mensPlayer);
}
[HttpPost]
public ActionResult Edit(MensPlayer mensPlayer, HttpPostedFileBase image)
{
if (ModelState.IsValid)
{
//Added line below
_dataSource.ImageTemp(mensPlayerInDb, mensPlayer);
if (image != null)
{
mensPlayer.ImageMimeType = image.ContentType;
mensPlayer.ImageData = new byte[image.ContentLength];
image.InputStream.Read(mensPlayer.ImageData, 0, image.ContentLength);
}
//Added line below
mensPlayer.ImageData = mensPlayerInDb.ImageData;
//Save Player
_dataSource.Update(mensPlayer);
_dataSource.Save();
TempData["message"] = string.Format("{0} has been saved", mensPlayer.Name);
return RedirectToAction("Detail", "MensPlayer", new {id = mensPlayer.Id});
}
return View(mensPlayer);
}
GetImage Method:
public FileContentResult GetImage(int id)
{
var image = _dataSource.MensPlayers.FirstOrDefault(p => p.Id == id);
if (image != null)
{
return File(image.ImageData, image.ImageMimeType);
}
return null;
}
Display Image:
#if (Model.ImageData != null) {
<div >
<img width="300" height="400" src="#Url.Action("GetImage", "MensPlayer",
new { Model.Id })" alt="Player Image"/>
</div>
}
DataSource class
public interface IDataSource
{
IQueryable<MensTeam> MensTeams { get; }
IQueryable<MensPlayer> MensPlayers { get; }
IQueryable<MensHome> MensHomes { get; }
void Save();
void Update(MensPlayer mensPlayer);
void Delete();
void ImageTemp(MensPlayer mensPlayerInDb, MensPlayer mensPlayer);//I added this
}
In Db class
void IDataSource.Save()
{
SaveChanges();
}
void IDataSource.Update(MensPlayer mensPlayer)
{
Entry(mensPlayer).State = EntityState.Modified;
}
//Added code below
void IDataSource.ImageTemp(MensPlayer mensPlayerInDb, MensPlayer mensPlayer)
{
Entry(mensPlayerInDb).CurrentValues.SetValues(mensPlayer);
}
My problem is, every time i try to edit a player, every data is nicely retrieved from the database but when i hit save after editing, the ImageData is lost, more like replacing the original data with null value. It's like, the application expects me to re-upload the image at every edit i attempt.
What can i do to clean this up?
Well, if image is null then in your entity mensPlayer.ImageData is null. Later when you call _dataSource.Update(mensPlayer) you set the state of the entity to Modified (at least I guess that Update just sets the state to Modified), so you are telling EF that your have modified the image too (namely set it to null) and EF will save that change.
In order to solve the problem you can either load the entity including the image from the database first...
var mensPlayerInDb = context.MensPlayers.Find(mensPlayer.Id);
mensPlayer.ImageData = mensPlayerInDb.ImageData; // save the ImageData
// copy changed values to loaded entity and save it, ImageData is unchanged
context.Entry(mensPlayerInDb).CurrentValues.SetValues(mensPlayer);
context.SaveChanges();
...or you can try:
context.Entry(mensPlayer).State = EntityState.Modified;
context.Entry(mensPlayer).Property(m => m.ImageData).IsModified = false;
The latter approach didn't work with EF < 5.0 (because it was forbidden to set the Modified state of a property to false once the entity was marked as Modified) but it should work with EF 5.
You need to integrate one of the solutions into your _dataSource service class, possibly by introducing new methods because it won't work with your general Update method.