C# Class Instance Holding String Reference But Not Object Reference - c#

Alright, so I have spend LITERALLY five or six hours working on this to no avail. I'm working in C# with ASP.NET MVC 4 and EF 5.0, I believe. Here's my problem: I have a method in my controller takes a custom class as a parameter (these names have been candy-themed obfuscated :P). The ViewError class looks like so:
public class ViewError<T> where T : class
{
public T ModelObject { get; set; }
public string Message { get; set; }
public ViewError(string message, T modelObject)
{
Message = message;
ModelObject = modelObject;
}
public ViewError()
{
Message = null;
ModelObject = default(T);
}
}
(It's a generic so I can figure out what type the ModelObject is from within the code. It's irrelevant, though; I've already tested it taking a generic 'object' and the same problem happens.)
The controller method looks like this. All it does is ask who is eating the candy, and if there's a ViewError, it displays the message. (The view has a paragraph that displays the ViewBag.Message).
public ActionResult EatCandy(ViewError<EatCandyViewModel> error)
{
ViewBag.BrandList = new SelectList(db.Brands, "ID", "Name");
// If there's a notification, pass it to the view, along with the model
if(error.Message != null)
{
ViewBag.Message = error.Message;
return View(error.ModelObject);
}
return View();
}
On the post:
[HttpPost]
public ActionResult EatCandy(EatCandyViewModel viewModel)
{
if(ModelState.IsValid)
{
CandyEater eater = (CandyEater)viewModel;
db.CandyEaters.Add(eater);
db.SaveDatabase(); // Custom convenience wrapper for SaveChanges()
return RedirectToAction("ChooseCandyToEat", eater);
}
return View(viewModel);
}
Pretty standard stuff. Now, in the ChooseCandyToEat method, it brings up a list of candy available to eat of a specific brand. If that brand doesn't have any available candy, I want it to send an error back to the EatCandy method (via ViewError object) telling that eater that they have no candy to eat, and send the model back so that the eater doesn't have to type in their info again, merely select a different brand of candy.
public ActionResult ChooseCandyToEat(CandyEater eater)
{
// Get the list of candy associated with the brand.
IEnumerable<Candy> candyList = db.Candies.Where(b => b.Brand == eater.DesiredBrand)
.Where(e => !e.Eaten);
// If the brand has no candy, return an error message to the view
if(candyList.Count() == 0)
{
EatCandyViewModel viewModel = (EatCandyViewModel)eater;
ViewError<EatCandyViewModel> viewError = new ViewError<EatCandyViewModel>("Oh noes! That brand has no candy to eat!", viewModel.Clone()); // This is a deep clone, but even if it wasn't, it would still be weird. Keep reading.
return RedirectToAction("EatCandy", viewError);
}
return View(candyList);
}
According to my understanding, this should all work. Now here's the weird part - I can confirm, through debug messages and the Watch window (using Visual Studio) that the ViewError is created properly and holds a deep clone of the viewModel in its ModelObject (I have to convert it back because the EatCandy method expects an EatCandyViewModel as a parameter). However, when I step forward and the ViewError is passed to EatCandy, the ModelObject within it is null! I originally thought this was because it only passed a reference of viewModel into the object and it was getting garbage collected, which is why I added the Clone() method. The string, which is also a reference type, is getting through okay, though. Why isn't the object? Does anyone know?
If you need more info, just ask. And disregard the ridiculousness of this database - I obfuscated it intentionally, not just to be silly.

This won't work. RedirectToAction results in a 302 to the browser which just is not capable of carrying complicated models.
There are some alternatives, you could use the TempData to store the model, do the redirect and retrieve the data. You could probably even call the EatCandy action directly instead of redirecting, however the auto matching the view won't work and you'd have to force the "EatCandy" view name at the end of the EatCandy action.

Related

How to pass two filled class models via an Angular http.post 'body' to the controller?

I am receiving an error message, "Sequence contains no elements" while trying to update a table in SQL from Angular 7 to an AspNet Core controller by passing two model parameters using an "http.post".
I am passing the data from the form to the class models with no problem because I can see the payload data in the browser console. However, when trying to pass the models as parameters in my api service to the controller, all of the parameters in the model are null. I usually don't have an issue when passing one model parm thru, but passing two of them to get to my controller with a [FromBody] doesn't seem to want to work for me.
I tried to wrap the models in curly brackets to pass them, to no avail:
UpdateService(serviceAddress: ServiceAddressModel, contact: ContactModel) {
let reqHeader = new HttpHeaders();
let body = { svc: serviceAddress, cnt: contact };
reqHeader.append('Content-Type', 'application/json');
return this.http.post(this.baseurl + 'api/customermanagement/update-service-address-info', body, { headers: reqHeader });
When I view the request / response in the browser console, I can see the data within the payload, so I know that the data is ready to pass.
My controller is set up as follows:
[Route("update-service-address-info")]
public bool UpdateServiceAddressAccount([FromBody] ServiceAddressEntity svc_id, [FromBody] ContactEntity cnt_id)
{
return serviceAddressService.UpdateServiceAddressAccount(svc_id, cnt_id);
}
Using breakpoints in this call shows null for all values.
If I can properly pass the parameters to my interface, I should be good-to-go. I am sensing that I am not structuring the parameters properly in the http.post body.
Your request body, { svc: serviceAddress, cnt: contact } is received as a json string, e.g. {"svc":{"serviceAddressProperty1":"value",...},"cnt":{"contactProperty1":"value",...}}. The parameters to your action method are bound via the default model binding mechanism (unless you provide your own custom model binding implementation). The default mechanism attempts to create instances by binding from the top level of the json object received with the request. enter code here
In simpler terms, lets assume you class ServiceAddressModel is defined like this:
public class ServiceAddressModel
{
public string Name { get; set; }
public string Property2 { get; set; }
}
the model binder looks for properties with the names "name" and "property2" at the top level of the json tree. If found, these are bound to the Name and Property2 properties of the created instance.
In your case, wrapping your models in a class that can make svc_id and cnt_id the top level properties would work fine. Like this example:
public class MyRequest
{
public ServiceAddressModel svc_id { get; set; }
public ContactEntity cnt_id { get; set; }
}
Then you can declare your action like
[Route("update-service-address-info")]
public bool UpdateServiceAddressAccount([FromBody] MyRequest request)
{
return serviceAddressService.UpdateServiceAddressAccount(request.svc_id, request.cnt_id);
}
Snake casing, camel casing should be allowed by default (you will have to try it, I havent tested that part). That is, if you declare your properties as SvcId and CntId (if you prefer more natural C# naming conventions) it should be able to bind correctly from JSONs with "svc_id" or "cnt_id".
Another option would be to implement custom model binders, but that might be a longer and more complex route.
Hope this helps.
Just try to pass the value like this and see
let body = { svc_id: serviceAddress, cnt_id: contact };

User is null when Function not called from UI

In my database table I have a column in which the values are manipulated before saving into the database. The logic for the manipulation was added at a later stage of development, after a lot of values were inserted to the table. Now I want to edit the contents of the table to manipulate the values of the existing contents.
My approach
To call the edit function for all the items in the table as the manipulation logic is added in the EDIT action method as well.
When I call the edit function while looping through the contents in the database, I get a null reference exception which is not there when I use the edit function from the UI.
EDIT Action method
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
// Valuestring from another function
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
The call to this function was made from the HomeController. I commented all the return statements in the EDIT method while calling it from the HomeController.
Exception
Object reference not set to an instance of an object.
Question
Why does the edit work while called from the UI without any Exception , but not from HomeController?
Why is the user null even when I call the function from a different controller? Using windows authentication. How do I get the user name if the user has already been authenticated?
EDIT - Added how the Edit function is called
//Home controller (But I have also tried calling the function from a different controller where the call to the method is from the UI
public ActionResult Index()
{
test();
return View();
}
public void test()
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
}
}
Update - General Overview of the problem
The question can be generalized so as to find an answer how the User property is defined when using Windows Authentication.
Is it that the controller gets access to the Windows user property only when it is called from the UI?
Is it not possible to access the User Property in a function that is not called via UI?
And the best thing as far as I know about the issue is that, the User is defined in the Controller where the testmethod is called but not in the Edit in another controller.
You are partially correct when you are saying you can only access the User property only accessing from the UI. The correct answer is -
When accessing the Edit method through the UI, it is actually going through the ASP.Net controller initializer pipeline and that environment takes care of the current browser session and assigns the User variable. HttpContext is available at this time.
But when you are creating the controller variable like this -
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
You are bypassing all those initialization codes. No HttpContext and this is just another normal object and thus it does not have the User property populated.
ANSWER TO QUESTIONS:
Is it that the controller gets access to the Windows user property only when it is called from the UI?
=> Yes, absolutely right, because you are going through the ASP.Net pipeline. But it is not only for Windows user, it is all those things that re in a HttpContext.
Is it not possible to access the User Property in a function that is not called via UI?
=> Only if, you manually assign it otherwise NO.
MORE INSIGHT:
Basically, what you are trying to achieve is a very poor design. Nowhere, remember nowhere, you are supposed to call a controller from inside a controller unless it is a subclass to base method call. The only way you can call another controller from inside another controller is redirecting the execution by using "Redirect" methods. No one will stop you from calling controller like this, but this shows poor design principle..
The best way to solve your situation is like this -
public class ModelService {
public void Edit(IPrincipal user, SetValue setValue){
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
Then in both the controllers constructors -
public class ControllerOne : Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
_modelService.Edit(User, Setvalue);
}
}
//controller 2
public class HomeController: Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Index()
{
foreach(var item in db.SetValue.ToList())
{
_modelService.Edit(User, item);
}
}
}
You can take help from IoC Container for dependency injection, that is even better approach.
Invoking a Controller from within another Controller is probably getting some data (as the User) to be missing. I wouldn't be making much effort understanding why (unless curious), since doing it this way might be considered as bad design. (I bet some other info would be missing as well, maybe cookies and such). the better thing you can do is to separate the logic from your Controllers into some service layer and call the methods with the IPrincipal User as parameter. If you are forced to do it the way you described, then send the user as a parameter to the other controller.
public ActionResult Index()
{
test(User);
return View();
}
public void test(IPrincipal user)
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item, user);
}
}
And the oter controller
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue, IPrincipal user = null)
{
var currentUser = User == null? user : User;//or somthing like that
}
Didn't check this code. but it should give you something to work with.

Why does MVC 5 create object with default constructor when specified as action parameter?

I have an action that accepts a model as a parameter:
public ActionResult stuff (string a, string b, MyModel mod)
{
if (mod != null)
{
// perform action based on existing model values
}
else
{
// perform action based on no model (i.e. first time hitting the page)
{
}
If no model is passed then for some reason instead of MyModel mod being null .net creates an object with parameterless constructor so my if always evaluates to true.
Why does this happen? I'd rather this object doesn't have a parameterless constructor for simplicity as my constructors do a few processes that need certain values. I thought that in MVC 5 you could specify parameters and it would figure it out if they are left off.
The reason I have this logic is because page first loads up and creates the model there are lots of requests being made and I don't want these repeated every time user submits a change.
Is it best to create a property like:
public bool initialized { get; set; }
And then set it to true in all constructors apart from the default constructor? Am I going about this whole thing the wrong way?

.Net MVC binding dynamic Type to a Model at runtime

I have a slightly long conceptual question I'm wondering if somebody could help me out with.
In MVC I've built a website which builds grids using kendoui's framework.
All the grids on my website are constructed exactly the same except for the model they use and the CRUD methods that need to be implemented for each model. I set things up where each Model implement an interface for CRUD methods like below to get the logic all in one place.
//Actual interface has variables getting passed
public interface IKendoModelInterface
{
void Save();
void Read();
void Delete();
}
public class Model1: IKendoModelInterface
{
[Key]
public int IdProperty1 { get; set; }
public int SomeProperty2 { get; set; }
public string SomeProperty3 { get; set; }
public void Save(){
//Implement Save
}
public void Read(){
//Implement Read
}
public void Delete(){
//Implement Delete
}
}
Then to speed up the writing of all the scaffolding Action methods needed to get the grids to work I created an abstract Controller that can call the interface methods of the Model that gets passed into it.
//Implement the AJAX methods called by the grid
public abstract class KendoGridImplController<T> : Controller where T : class, IKendoModelInterface
{
// Method called from kendo grid
public virtual ActionResult Create([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<T> createdRecords)
{
//Invoke Create Method for Model and return results
}
public virtual ActionResult Read([DataSourceRequest]DataSourceRequest request, int Id)
{
//Invoke read method for model and return results
}
//Update and Delete also implemented..
}
Then I just need a Controller per model that implements the abstract controller above passing in the type of Model being used.
public class ResponsibilityMatrixController : KendoGridImplController<Model1>
{
//Set up the page the grid will be on
public ActionResult Index(int id)
{
return View("SharedGridView", id);
}
//Can override abstract methods if needed but usually won't need to
}
I'm wondering if I can take this one step further or if I've reached the end of the road. To me it just seems like more repeated code if I have to create a controller per Model that does nothing but pass in the type to the abstract controller and calls the same View.
I attempted for quite a while yesterday to figure out if I could dynamically assign the type to the abstract controller. I setup something where I was sending back the type of model via strings and I could still invoke the methods needed. Where it failed, was that the mapping could no longer be done on any of the controller actions by default since the type isn't known at compile time. eg
public virtual ActionResult Create([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<T> createdRecords)
createdRecords can't be bound like this if T that's passed in is an interface and not the Model itself and I've found no real way to map the form data to an instance of a type that isn't known at compile time.
I'm wondering if there's an easy way to do this mapping between an instance of the type of object getting passed in that I can figure out at runtime, if there's some other way to set this up that I'm overlooking or if both those things are going to be way too much work and I should just not attempt something like this and build a controller per model like I do now?
In case anybody else finds this in the future here's what I've done so far to solve my issue. First I downloaded the impromptu-interface code lib which is incredibly helpful when dealing with dynamic types.
Then for the abstract controller's save methods where it was important that I could bind back to the original object type I did this.
// Method called from kendo grid
public virtual ActionResult Create([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<ExpandoObject> createdRecords)
{
Type originalGridType = GetTypeOfModelUsingCustomCodeIDevelopedEarlier();
foreach (ExpandoObject record in createdRecords)
{
var convertedType = Impromptu.InvokeConvert(record, originalGridType);
T objectInstance = Impromptu.ActLike(convertedType);
objectInstance.Save();
}
}
Then I just needed to add a cast in my model that could convert from the ExpandoObject to my model. An extra method that I still wish didn't have to be there but with some helper methods that I wrote it's not a lot more code to make happen.
public static implicit operator Model1(ExpandoObject expando)
{
Model1 model = new Model1();
//Set fields of model...
//....
return model;
}
From here everything works front to back. Maybe there's a better way but this is the best I could come up with so far.

Is this an MVC anti-pattern?

I’m very new to any kind of .NET web development (thus far I’ve worked primarily with Winforms and services.) I’ve started to work on an existing MVC3 project with two other developers. I’m conceptually familiar with MVC, and am trying to catch up on how it’s used in this project.
We have an AccountDto class to represent Accounts. There is a Response class that is inherited by another class for each Entity, i.e. AccountResponse:
public class Response
{
[DataMember]
public bool IsSuccess{get;set;}
[DataMember]
public string DisplayMessage { get; set; }
[DataMember]
public string DetailedMessage { get; set; }
[DataMember]
public ErrorType ErrorType { get; set; }
public Response(){
this.IsSuccess=true;
this.ErrorType = ErrorType.None;
}
}
public partial class AccountResponse : Response
{
[DataMember]
public IList<AccountDto> AccountList { get; set; }
}
There’s an AccountService which will return an AccountResponse to the Controller, with a list of the AccountDto object:
public AccountResponse GetAccountByAccountId(Int64 accountId)
{
_logger.Info("Executing GetAccountByAccountId()");
AccountResponse response = new AccountResponse();
try
{
Account item = AccountPersistence.GetAccountByAccountId(accountId);
AccountDto dto = Mapper.Map<AccountDto>(item);
response.AccountList = new List<AccountDto>() { dto };
response.IsSuccess = true;
}
catch (Exception ex)
{
response.IsSuccess = false;
response.ErrorType = ErrorType.GeneralFault;
response.DetailedMessage = ex.ExceptionMessageBuilder();
response.DisplayMessage = "System Failure: Failed to get Account by AccountId";
_logger.Error(ex);
}
return response;
}
I was told the Response thing is implemented to be able to handle success/failure messages. So in a controller, there’s code like the following (doesn’t happen to do anything special if a failure):
public ActionResult ToBeCalled(int id)
{
AccountDto dto = null;
var response = _accountService.GetAccountByAccountId(Convert.ToInt64(id));
if (response.IsSuccess)
{
dto = response.AccountList[0];
return View(dto);
}
return View(dto);
}
This made sense to me though I wasn’t sure where the success/error messages were going to be utilized. However, they now want to switch from using the DTO in views to using the Response, so success/failure will have to be handled in the views:
public ActionResult ToBeCalled(int id)
{
var response = _accountService.GetAccountByAccountId(Convert.ToInt64(id));
return View(response);
}
This seems off to me – instead of coding against a DTO as the model, I have to do something like the following for each page:
#{
if (Model.IsSuccess)
{
var account = Model.AccountList.FirstOrDefault();
if (account != null)
{
#Html.HiddenFor(x => account.AccountNumber)
}
}
The controllers’ ActionResult / HttpPost methods then have to also parse the DTO from these Response objects. This seems like an anti-pattern to me; are approaches like this normal?
Apologies if this is too lengthy, please migrate if it belongs on Code Review or another site.
I agree with you that this would be an anti-pattern. The View is supposed to be quite ignorant, especially of logic like this.
I can see why this would be tempting, if the difference between success and failure is a minor part of the UI, but imagine if that were to change. A view has little ability (without unnecessary nesting of partials) to switch to an entirely different view. It has no ability to issue a redirect or other error codes. In the event that you decide to change your UI, you may have to go back and rewrite your Controller yet again.
If the reasoning behind moving the logic to the view was to remove the response.IsSuccess logic from the Controller (and to be honest, that seems fine to me; it's pretty much the same as the classic Model.IsValid), you could consider another approach: refactor your Response class to inherit from ActionResult. Then you could move that logic into the ExecuteResult() method and it would be separate from your Controller.
Just use the coalesce operator and you can get rid of a whole lot of cruft (like that strange Response base class (which should be marked abstract if it continues to exist)) and avoid null-checking.
public ActionResult ToBeCalled(int id)
{
var response = _accountService.GetAccountByAccountId(id) ??
new AccountResponse();
return View(response);
}
Better yet, migrate that logic into your service class so that it guarantees return of an object (it doesn't necessarily make sense for a repository to do this when there's no backing entity, but it does for a service).
Either way, you don't need to include unsightly null-checking or if/else logic on your view. Move as much logic to places that you can test it as you can and you'll be happier.

Categories