Casting Object depending on its type - c#

I made a validation method for business rules that are not verified from my rules in the model, but I'm having a problem to make it work. Since there are two possible scenarios(customer or seller registration), they need to be treated separately in their own views and models. The seller registration inherits from customer registration for the basic info, so all fields in customer are also in seller. But since I'm working with 2 different models, even though both have the same fields that I'm doing the validation upon, I needed to use Object to use the same validation method. But unfortunately I'm having trouble to do so.
[CustomHandleError]
private bool ValidateRegistrationForm (Object registerViewModelObject) {
if (registerViewModelObject is RegisterViewModel)
{
RegisterViewModel registerViewModel =
(RegisterViewModel)registerViewModelObject;
}
else
{
RegisterSellerViewModel registerViewModel =
(RegisterSellerViewModel)registerViewModelObject;
}
if (ModelState.IsValid)
{
string [] names = registerViewModel.Name.Split (
new string [] {" "}, StringSplitOptions.RemoveEmptyEntries);
if (names.Length == 1)
ModelState.AddModelError ("Name", "Fill your full name");
if (CustomerUtilities.IsCpf (registerViewModel.Identity) == false)
ModelState.AddModelError ("Identity", "Invalid CPF value");
if (this.AuthenticatorService.IsExistentUser (registerViewModel.Email))
ModelState.AddModelError ("Email", "Email already registered");
}
}
As you can see, after the if (ModelState.IsValid) the IntelliSense doesn't find registerViewModel in the current context. I wonder why this happens, since that variable is defined inside the if AND the else above, so there is no way to reach that code without it being defined.
Is there any workaround for this(other than creating a new method or passing 2 variables)?

Declare RegisterViewModel outside of the if block scope, and assign it within the if block.
RegisterViewModel registerViewModel;
if (registerViewModelObject is RegisterViewMOdel)
{
registerViewModel = // ...
}
else
{
registerViewModel = // ...
}

If you wish to have two separate variables then declare both outside of the if statement and test for null after.
RegisterViewModel registerViewModel;
RegisterSellerViewModel sellerModel;
if (registerViewModelObject is RegisterViewModel)
{
registerViewModel = (RegisterViewModel)registerViewModelObject;
}
else
{
sellerViewModel = (RegisterSellerViewModel)registerViewModelObject;
}
However, defining an interface to use instead of Object would be the better option.
public interface IRegisterViewModel
{
public string Name { get; set;}
public ... Identity {get; set;}
...
}
public class RegisterViewModel : IRegisterViewModel
{
...
}
public class RegisterSellerViewModel : IRegisterViewModel
{
...
}
Then use ValidateRegistrationForm(IRegisterViewModel registerViewModel) and you can get rid of the if statement entirely.

You should define RegisterViewModel outside from your if statemtent. And make assignment inside your if statement.
Like;
RegisterViewModel registerViewModel;
if(...)
{
//make your assigment here.
}

You probably need to extract methods that are common for RegisterViewModel and RegisterSellerViewModel into an interface and implement it in both classes. Then cast registerViewModelObject to this interface regardless of its actual type.

The problem occurs since you don't have a single variable defined in the main scope of the function. In the way that you have written your code, you define two variables that are inside different scopes.
How I would go about with the solution:
I would make a base class.
class RegisterModel
{
public string Name;
public IdentifyType Identify;
public string Email;
}
And then both your classes can inherit from the base class. Like this:
class RegisterViewModel
: RegisterModel
{...}
class RegisterSellerViewModel
: RegisterModel
{...}
Now you can actually covert the Object variable in your function a single time. Like this:
private bool Validate(Object viewModel)
{
var castViewModel = (RegisterModel)viewModel;
if(ModelState.IsValid)
{
...
}
}
Note that this will cause a run-time error if viewModel is not of type RegisterModel.

Related

Model bound complex types must not be abstract or value types and must have a parameterless constructor

I have the following problem, I created an application to add game categories and the games themselves to the database. I created a relationship and unfortunately when I add to the database I get an error.
Model bound complex types must not be abstract or value types and must
have a parameterless constructor.
Game Category Model :
using System.Collections.Generic;
namespace relationship.Models
{
public class GameCategory
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Game> Game { get; set; }
}
}
Game Model :
namespace relationship.Models
{
public class Game
{
public int GameId { get; set; }
public string Name { get; set; }
public GameCategory Category { get; set; }
public int CategoryId { get; set; }
}
}
ViewModel :
using relationship.Models;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace relationship.ViewModels
{
public class AddGameViewModel
{
[Required]
public string GameName { get; set; }
public int CategoryID { get; set; }
public List<SelectListItem> Categories { get; set; }
public AddGameViewModel(IEnumerable<GameCategory> categories)
{
Categories = new List<SelectListItem>();
foreach (var catData in categories)
{
Categories.Add(new SelectListItem { Text = catData.Name.ToString(), Value = catData.Id.ToString() });
}
return;
}
}
}
GameRepository :
using System.Collections.Generic;
using System.Linq;
namespace relationship.Models
{
public class GameRepository : IGameRepository
{
private readonly AppDbContext appDbContext;
public GameRepository(AppDbContext dbContext)
{
appDbContext = dbContext;
}
public void AddGame(Game game)
{
appDbContext.Games.Add(game);
appDbContext.SaveChanges();
}
public IEnumerable<Game> Games()
{
return appDbContext.Games.ToList();
}
}
}
and last is GameController :
using Microsoft.AspNetCore.Mvc;
using relationship.Models;
using relationship.ViewModels;
namespace relationship.Controllers
{
public class GameController : Controller
{
private readonly IGameRepository gameRepository;
private readonly ICategoryRepository categoryRepository;
public GameController(IGameRepository gameRepo, ICategoryRepository catRepo)
{
gameRepository = gameRepo;
categoryRepository = catRepo;
}
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Add()
{
var addGameViewModel = new AddGameViewModel(categoryRepository.GameCategory());
return View(addGameViewModel);
}
[HttpPost]
public IActionResult Add(AddGameViewModel addGameViewModel)
{
if (ModelState.IsValid)
{
GameCategory gameCategory = categoryRepository.GetDetails(addGameViewModel.CategoryID);
if(gameCategory == null)
{
return NotFound();
}
Game game = new Game
{
Name = addGameViewModel.GameName,
Category = gameCategory
};
gameRepository.AddGame(game);
return RedirectToAction("Index");
}
return View(addGameViewModel);
}
}
}
I don't have any idea what is wrong.
My error screen :
Could not create an instance of relationship.ViewModels.AddGameViewModel. Model bound complex types must not be abstract or value types and must have a parameterless constructor.
Let's try and break this error down.
Could not create an instance of relationship.ViewModels.AddGameViewModel.
Pretty self-explanatory: the model-binding components are trying to create an instance of your type, but failed.
Model bound complex types
"Model bound" refers to that they're being bound by the ASP.NET pipeline. "complex types" are basically any types which aren't "basic" like string or int. Your model classes are complex types.
must not be abstract
The model-binding system is going to want to be able to create instances of the class, so it cannot be abstract; it must be concrete. All of the types you've show are concrete so this isn't the problem.
or value types
You can't use struct types with model-binding; it's just one of its limitations. Fortunately your types are all classes, so you can ignore this.
and must have a parameterless constructor.
ASP.NET doesn't know how to supply parameters to model constructors. It can only do the equivalent of new T(), so all your model types must define a constructor which has zero parameters. This is the reason you're seeing the error; your AddGameViewModel class only defines this constructor:
public AddGameViewModel(IEnumerable<GameCategory> categories)
One of the C# language features is that when you don't specify a constructor manually, it adds a default one for you. When you define a constructor in your code, this default constructor is not added.
In all of your other models, you aren't defining any constructors so the compiler is adding the default one for you. In the case of AddGameViewModel you have added a constructor, so to fix the problem you must also add the default constructor:
public AddGameViewModel()
{
}
you need add [FromBody] to the parameter so that asp.net core know how to bind the model.
[HttpPost]
public IActionResult Add([FromBody] AddGameViewModel addGameViewModel)
As of this writing, I experienced this issue in an Asp.NET Core 2.2 Controller where the type was injected on one of the methods. Moving the type to the Controller's constructor worked around it. Since this wasn't really acceptable we eventually refactored the offending class out of the Controller and into the processing layer where singletons are already used extensively. Adding one more at that point cleared up our problem.
Note this is the OOB IoC container that is built-in to Asp.Net Core. Other IoC providers may be better able to handle injecting properties on methods.
Lamar might be an alternative.
Using a model binder might also have worked since the binder could probably use the singleton and/or support constructor injection more cleanly.
In my case, I was naively binding a complex object (a complex object without a no-args constructor):
Edit.cshtml.cs:
namespace MyNamespace.Pages.CSDA
{
public class EditModel : PageModel
{
...
[BindProperty]
public MyComplexClass WorkflowItem { get; set; }
...
I got this runtime error when I clicked "Save":
System.InvalidOperationException: Could not create an instance of type 'MyNamespace.MyComplexClass'.
Model bound complex types must not be abstract or value types and must have a parameterless constructor.
Alternatively, set the 'WorkflowItem' property to a
non-null value in the 'MyNamespace.EditModel' constructor. at
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(ModelBindingContext
bindingContext) at
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.BindModelCoreAsync(ModelBindingContext
bindingContext, Int32 propertyData)
I needed the object (it had information I wanted to display to the user), but I didn't need to "update" it (at least not in this edit menu).
SOLUTION:
Simply removing [BindProperty] eliminated the error.
I had this same error. Constructor was internal, I returned it back as public, and the model was passed normally.
Adding [ApiController] at the top of my Controller's class fixed it for me:
[Route("api/[controller]")]
[ApiController]
public class ProductController : Controller
{
...
}

Specify a unique identifier attribute for an object across webapi Models

In a POST call to a WebApi I am trying to return a Created(newobject) thing. But there is no signature for Created in ApiController that can only take the object and do the rest.
It works fine if I return something like:
return Created(newobject.blahid.ToString(), newobject);
or if I do a
return CreatedAtRoute("DefaultApi", new { controller = ControllerContext.ControllerDescriptor.ControllerName, id = newobject.blahid.ToString()}, newobject);
I want to simplify this to:
return Created(newobject);
I would need to implement a method in a BaseController
public class BaseController : ApiController
{
protected new CreatedNegotiatedContentResult<T> Created<T>(T content)
{
var id = GetId(content);//need help here
return base.Created(id, content);
}
}
I don't want to worry about the Unique Identifier for an object being called differently in different models e.g. myobjguid, someblahguid etc. I would just want to find it out and mark it as "id".
say if my model is
public class Model_A
{
public List<Model_A> ChildModels { get; set; }
[LookForThisAttribute]//I want something like this
public Guid Model_AGuid { set; get; }
public Guid ? ParentGuid { set; get; }
public List<SomeOtherObject> OtherObjects { set; get; }
}
Is there an attribute([LookForThisAttribute]) or something I can set on all my models to specify that this is the guy to be assumed as THE unique identifier if I ever look for it.
Just like the [Key] attribute in Entity Framework. No matter what you call it, Entity Framework know its going to be the primary key.
So the GetId(T content) method can take the object and return the value of the property that has a [LookForThisAttribute] set?
I ended up writing my own Attribute and then looking up for it in the BaseController.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class UniqueIdAttribute: Attribute
{
}
And in the BaseController Created method:
protected CreatedNegotiatedContentResult<T> Created<T>(T content)
{
var props =typeof(T).GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(UniqueIdAttribute)));
if (props.Count() == 0)
{
//log this
return base.Created(Request.RequestUri.ToString(), content);
}
var id = props.FirstOrDefault().GetValue(content).ToString();
return base.Created(new Uri(Request.RequestUri + id), content);
}
Mark Gravell's post here helped me with getting the value of the property that has my custom attribute:
How to get a list of properties with a given attribute?
Along with a corresponding unit test for the controllers works fine for me.
Now I can just call Created(anyobject); from all ApiControllers without bothering about the different names people put for their IDs as long as they decorate it with my custom attribute.

How to initialise BaseModel in BaseController

I have a class of properties which are set from a service which I need available on every view of my MVC application.
Therefore I've created a "Base View Model" which my view models will inherit from.
public class BaseModel
{
public BaseModel()
{
foo = "foo value";
bar = "bar value";
}
public string foo { get; set; }
public string bar { get; set; }
}
public class HomeIndexViewModel : BaseModel
{
}
I have then created a "Base Controller" which all my controllers will inherit from:
public class BaseController : Controller
{
public BaseController()
{
}
}
public class HomeController : BaseController
{
public ActionResult Index()
{
HomeIndexViewModel model = new HomeIndexViewModel();
return View(model);
}
}
This is working as expected and I can call #Model.foo in my view and get foo value.
However I don't believe I should be initialising the values of BaseModel in it's constructor as this isn't using Dependency Injection and will become difficult to unit test.
How can I move the setting of the values foo and bar into the BaseController?
Of course I could set the values in the HomeController, but I would rather abstract this from the controller as the logic will always be the same and would bloat all my controllers.
I think the problem is that you are creating the instance of your models inside of the action, so the base controller has no reference to the object to set the properties.
Personally I would probably opt for some 'factory-type' function in the base controller that is responsible for creating the models as you need them.
Something like this for example:
public class BaseController : Controller
{
public T CreateBaseModel<T>() where T : BaseModel, new()
{
return new T
{
foo = "foo value",
bar = "bar value"
};
}
}
Then when you create your models in the actions you can do them like this:
HomeIndexViewModel model = CreateBaseModel<HomeIndexViewModel>();
If for some reason you need to pass parameters to your model constructor then you can have an overload like this:
public T CreateBaseModel<T>(params object[] args) where T : BaseModel
{
T model = (T)Activator.CreateInstance(typeof(T), args);
model.foo = "foo";
return model;
}
HomeIndexViewModel model = CreateBaseModel<HomeIndexViewModel>(param1, param2, etc);
Alternative
The main benefit of the above method is that you can access the foo and bar properties within the action code. However, if you don't care about this and only need the values to be accessible from within the View page, then you can override the OnActionExecuted method and apply the values in there. The benefit of this approach is that you don't need to change the way your models are created in the actions...
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
BaseModel model = filterContext.Controller.ViewData.Model as BaseModel;
if (model != null)
{
model.foo = "foo value";
model.bar = "bar value";
}
base.OnActionExecuted(filterContext);
}
Having the null check in there means it will only try to apply the values for models that inherit from BaseModel, which means you can still use other models without worry.
With this approach, your action code goes back to how it was originally:
HomeIndexViewModel model = new HomeIndexViewModel();
return View(model);

Passing an interface to an ASP.NET MVC Controller Action method

In my ASP.NET MVC app, I have an interface which acts as the template for several different view models:
public interface IMyViewModel
{
Client Client1 { get; set; }
Client Client2 { get; set; }
Validator Validate();
}
So, my view models are defined like this:
public interface MyViewModel1 : IMyViewModel
{
Client Client1 { get; set; }
Client Client2 { get; set; }
// Properties specific to MyViewModel1 here
public Validator Validate()
{
// Do ViewModel-specific validation here
}
}
public interface MyViewModel2 : IMyViewModel
{
Client Client1 { get; set; }
Client Client2 { get; set; }
// Properties specific to MyViewModel2 here
public Validator Validate()
{
// Do ViewModel-specific validation here
}
}
Then I currently have a separate controller action to do the validation for each different type, using model binding:
[HttpPost]
public ActionResult MyViewModel1Validator(MyViewModel1 model)
{
var validator = model.Validate();
var output = from Error e in validator.Errors
select new { Field = e.FieldName, Message = e.Message };
return Json(output);
}
[HttpPost]
public ActionResult MyViewModel2Validator(MyViewModel2 model)
{
var validator = model.Validate();
var output = from Error e in validator.Errors
select new { Field = e.FieldName, Message = e.Message };
return Json(output);
}
This works fineā€”but if I had 30 different view model types then there would have to be 30 separate controller actions, all with identical code apart from the method signature, which seems like bad practice.
My question is, how can I consolidate these validation actions so that I can pass any kind of view model in and call it's Validate() method, without caring about which type it is?
At first I tried using the interface itself as the action parameter:
public ActionResult MyViewModelValidator(IMyViewModel model)...
But this didn't work: I get a Cannot create an instance of an interface exception. I thought an instance of the model would be passed into the controller action, but apparently this is not the case.
I'm sure I'm missing something simple. Or perhaps I've just approached this all wrong. Can anyone help me out?
The reason why you cannot use the interface is because of serialization. When a request comes in it only contains string key/value pairs that represent the object:
"Client1.Name" = "John"
"Client2.Name" = "Susan"
When the action method gets invoked the MVC runtime tries to create values to populate the method's parameters (via a process called model binding). It uses the type of the parameter to infer how to create it. As you've noticed, the parameter cannot be an interface or any other abstract type because the runtime cannot create an instance of it. It needs a concrete type.
If you want to remove repeated code you could write a helper:
[HttpPost]
public ActionResult MyViewModel1Validator(MyViewModel1 model)
{
return ValidateHelper(model);
}
[HttpPost]
public ActionResult MyViewModel2Validator(MyViewModel2 model)
{
return ValidateHelper(model);
}
private ActionResult ValidateHelper(IMyViewModel model) {
var validator = model.Validate();
var output = from Error e in validator.Errors
select new { Field = e.FieldName, Message = e.Message };
return Json(output);
}
However, you will still need a different action method for each model type. Perhaps there are other ways you could refactor your code. It seems the only difference in your model classes is the validataion behavior. You could find a different way to encode the validation type in your model class.
You could check this: http://msdn.microsoft.com/en-us/magazine/hh781022.aspx.
This is caused because DefaultModelBinder has no way of knowing what concrete type of IMyViewModel should create.
For solution that, you create custom model binder and indicate how to create and bind an instance of interface.
I think I would create an abstract base class that implemented IMyViewModel. I would make Validate an abstract method and require overriding in my concrete view models that inherited from MyAbstractViewModel. Inside your controller, you can work with the IMyViewModel interface if you want, but binding and serialization really needs a concrete class to bind. My $.02.
You could consider using a base class instead of the interface.

C#/ASP.NET MVC: I set the property's value, but it's still null

I've been struggling for this for an hour and I don't understand why there's a problem.
I have an abstract class
public abstract class IValidated
{
public bool IsValid { get { return (GetRuleViolation() == null); } }
public abstract RuleViolation GetRuleViolation();
}
And a validation class
public class RegisterModel : IValidated
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public MyDataContext DB { get; set; }
// ....
}
When I use the validation, I get an error
public ActionResult Register(RegisterModel model)
{
model.DB = DB;
if (model.IsValid) // an exception here
}
DB is null! I need to pass the datacontext object to the validation to check if the e-mail is unique and stuff like that.
Here's how the DB is initialized:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
DB = new MyDataContext();
}
The DB property is set in the Register method, I debugged that. But in the IsValid method of the class it's null...
UPDATE
Looks like the MVC framework for some reason runs the IsValid method before the Register ActionResult (and before the DB object is initialized). I think it's because it's doing all "magic" stuff with passing the RegisterModel parameters to the view. So I just put
if (DB != null)
and it helped.
Apparently, the IsValid method is run again when I call it, and the DB object is not null by then.
UPDATE 2
Since IsValid was a property, ASP.NET MVC was binding it, after turning it into a method, the problem disappeared.
You may want to rename IValidated to ValidationBase or something - the I prefix denotes an interface rather than a base class.
Is DB being set up in your controller's constructor? I'm guessing it's not, which leads to the Register method assigning a null reference to model.DB which leads to your NullReferenceException.
Also, MVC2 will read properties during databinding to try to validate them. That's probably what's biting you. If you change IsValid from a property to a method, the problem will go away.
Where are you creating the instance variable DB?
model.DB = DB
Where is the right DB getting initialized?

Categories

Resources