I am sure there has to be an easy way to do this, but I just cant seem to get my head around it.
I am using the MVCContrib Grid control to display a number of grids in a 3 tier application I am working on (ASP.NET MVC3 PL -> BLL -> DAL). I also am using Ninject to automatically inject all my dependencies.
The problem I am having is that I am using a grid model to display grids in my Views like this:
#Html.Grid(Model).WithModel(new UserGridModel(Html)).Attributes(id => tableName)
and have the corresponding grid model defined:
public class UserGridModel : GridModel<User> {
public UserGridModel(HtmlHelper html)
{
Dictionary<int, string> userStatuses = /*TODO: GET ALL USER STATUSES*/;
Column.For(user => user.ID);
Column.For(user => html.ActionLink(user.Email, "Edit", new {id = user.ID})).Named(DtoResources.UserDto_Email);
Column.For(user => user.FirstName);
Column.For(user => user.LastName);
Column.For(user => userStatuses[user.StatusID]);
}
}
Now I need to inject a service into this model so it can pull in all of the applicable statuses from the service (BLL) level. Currently just to make sure this would work, I exposed the IKernel in the Bootstrapping code and just IKernel.Get() but I don't think that is the cleanest way to get it. I would use constructor injection, but if I put the IUserStatusService as a parameter in the constructor, I can't figure out how I would get Ninject to inject the correct parameter when I call new UserGridModel(Html) in the view without explicitly using the IKernel there.
I am either missing something or wiring this up all wrong. Either way I'm stuck ... any help? What is the proper way to get an instance of my service through Ninject
In my opinion the cleanest solution to your problem is to change your controller so that it creates a model that already contains the user status as string so that no convertions is required in the view. I would do as littel as possible in the view and grid model.
Another possibility is to property inject the service to your view an pass it to the grid model. But as I mentioned this way you are introducing logic to your view.
Related
Currently, we are investigating possibilities with ReactiveUI how to visualize content of data objects coming from a backend system.
In the ReactiveUI documentation it is mentioned, the recommended approach is using type-safe bindings.
Therefore,
the code behind consists of all bindings between view and view model
the view model is enriched with read-write properties or read-only properties necessary for the binding
the view model has to take the data object's content to make it accessible via the read-write or read-only properties
View:
this.Bind(ViewModel,
viewModel => viewModel.DiameterInInch,
view => view.MaterialDiameterInInch.Text)
.DisposeWith(disposableRegistration);
View Model:
constructor
{
// reading data object in constructor
this.WhenAnyValue(x => x.Material.MutableDataObject.DiameterInInch)
.Subscribe(diameter => DiameterInInch = diameter);
// writing to data object in constructor
this.WhenAnyValue(x => x.DiameterInInch)
.Subscribe(diameter =>
Material.MutableDataObject.DiameterInInch = diameter);
}
[Reactive]
public double DiameterInInch { get; set; }
With this approach, we see that we are facing a certain effort when we have to implement the complete binding chain in code behind and the view model for each content field / property of a data object.
What further approaches can you recommend to minimize or avoid such an implementation effort / code duplication?
Why not just bind directly to DiameterInInch?
The way I see it, Material is a property of your ViewModel and its property - MutableDataObject - has to be a ReactiveObject (or some other implementation of INPC; otherwise you wouldn't be able to use this.WhenAnyValue on it). Maybe try doing it this way:
View:
this.Bind(ViewModel,
vm => vm.Material.MutableDataObject.DiameterInInch,
v => v.MaterialDiameterInInch.Text)
.DisposeWith(disposableRegistration);
Please note that I'm not an expert user of ReactiveUI so I may be missing something important here.
From what I've read, I'm supposed to be using ViewModels to populate my views in MVC, rather than the model directly. This should allow me to pass not just the contents of the model, but also other information such as login state, etc. to the view instead of using ViewBag or ViewData. I've followed the tutorials and I've had both a model and a viewmodel successfully sent to the view. The original problem I had was that I needed a paginated view, which is simple to do when passing a model alone, but becomes difficult when passing a viewmodel.
With a model of
public class Instructor {
public string forename { get; set; }
public string surname { get; set; }
}
and a viewmodel of
public class InstructorVM {
public Instructor Instructors { get; set; }
public string LoggedIn { get; set; }
}
I can create a paginated list of the instructors using the pure model Instructor but I can't pass InstructorVM to the view and paginate it as there are other properties that aren't required in the pagination LoggedIn cause issues. If I pass InstructorVM.Instructors to the view, I get the pagination, but don't get the LoggedIn and as this is just the model, I may has well have passed that through directly.
An alternative that was suggested was to convert/expand the viewmodel into a list or somesuch which would produce an object like this that gets passed to the view
instructor.forename = "dave", instructor.surname = "smith", LoggedIn="Hello brian"
instructor.forename = "alan", instructor.surname = "jones", LoggedIn="Hello brian"
instructor.forename = "paul", instructor.surname = "barns", LoggedIn="Hello brian"
where the LoggedIn value is repeated in every row and then retrieved in the row using Model[0].LoggedIn
Obviously, this problem is caused because you can only pass one object back from a method, either Instructor, InstructorVM, List<InstructorVM>, etc.
I'm trying to find out the best option to give me pagination (on part of the returned object) from a viewmodel while not replicating everything else in the viewmodel.
One suggestion was to use a JavaScript framework like React/Angular to break up the page into a more MVVM way of doing things, the problem with that being that despite looking for suggestions and reading 1001 "Best JS framework" lists via Google, they all assume I have already learned all of the frameworks and can thus pick the most suitable one from the options available.
When all I want to do is show a string and a paginated list from a viewmodel on a view. At this point I don't care how, I don't care if I have to learn a JS framework or if I can do it just using MVC core, but can someone tell me how to do this thing I could do quite simply in ASP.NET? If it's "use a JS framework" which one?
Thanks
I'm not exactly sure what the difficulty is here, as pagination and using a view model aren't factors that play on one another. Pagination is all about selecting a subset of items from a data store, which happens entirely in your initial query. For example, whereas you might originally have done something like:
var widgets = db.Widgets.ToList();
Instead you would do something like:
var widgets = db.Widgets.Skip((pageNumber - 1) * itemsPerPage).Take(itemsPerPage).ToList();
Using a view model is just a layer on top of this, where you then just map the queried data, no matter what it is onto instances of your view model:
var widgetViewModels = widgets.Select(w => new WidgetViewModel
{
...
});
If you're using a library like PagedList or similar, this behavior may not be immediately obvious, since the default implementation depends on having access to the queryset (in order to do the skip/take logic for you). However, PagedList, for example has StaticPagedList which allows you to create an IPagedList instance with an existing dataset:
var pagedWidgets = new StaticPagedList<WidgetViewModel>(widgetViewModels, pageNumber, itemsPerPage, totalItems);
There, the only part you'd be missing is totalItems, which is going to require an additional count query on the unfiltered queryset.
If you're using a different library, there should be some sort of similar functionality available. You'll just need to confer with the documentation.
I'm building an MVC3 application using the Entity Framework. In the application my controllers talk to a service layer. One of the controllers is a TournamentController which uses the TournamentService. This service has some standard methods such as CreateTournament, UpdateTournament etc.
When I want to insert a new tournament I want the view to have a dropdownlist of possible sports for which the tournament can be organised. So in the TournamentController's Create method I fill the ViewBag.Sports with a list of possible sports. Now to obtain this list of sports I use _tournamentService.GetAllSports(). In this GetAllSports() method of the TournamentService I want to create an instance of the SportService so I can 'forward' the question to the right service.
All services use dependency injection in the constructor to inject their own repository, like so:
private ITournamentRepository _repo;
public TournamentService(ITournamentRepository repo) {
_repo = repo;
}
My GetAllSports() method in the TournamentService looks like this:
public IEnumerable<Sport> GetAllSports() {
ISportService sportService = new SportService();
return sportService.GetSports();
}
The problem is that by calling the new SportService() it expects me to hand it an ISportRepository like in the TournamentService, where ninject creates the TournamentRepository. Now I could do the following:
public IEnumerable<Sport> GetAllSports() {
ISportService sportService = new SportService(new SportRepository());
return sportService.GetSports();
}
But the problem with that is that each repository expects an IContext, which is normally handled by ninject as well. Furthermore, I don't want two separate contexts to be instantiated.
A possible solution I found myself is to do this:
private ITournamentRepository _repo;
private ISportService _sportService;
public TournamentService(ITournamentRepository repo, ISportService sportService) {
_repo = repo;
_sportService = sportService
}
But there's only one method in my TournamentService class that would actually use the _sportService so I figure this is a bit overkill to make it a class attribute.
Keep it simple, inject ISportService to your controller and call sportService.GetSports() directly from the controller!
Your last solution valid. Inject the necessary service as part of the constructor.
And if you're worried about multiple contexts, don't let two separate contexts be created then.
In your Ninject binding:
Bind<ITournamentRepository>().To<TournamentRepository>().InRequestScope();
See https://github.com/ninject/Ninject.Web.Common/wiki/InRequestScope
The last piece is the important part. It will only create one instance of TournamentRepository for the current request. Any other requesters of TournamentRepository will get this instance.
If you're already doing this, you're set, otherwise, just add InRequestScope and you're done. (keeping in mind you'll need a reference to Ninject.Web.Common)
Hope that helps.
EDIT:
Remo is correct, I wouldn't call this from a service either. Just call for your lookup data from the controller and populate your view model. The InRequestScope advice still holds.
I'm currently working on an MVC 3 project using Ninject as my DI, the business objects are stored in a separate assembly. I'm running into an issue with the controller parameters, when posting back for CRUD operations I'm getting the error "Cannot create an instance of an interface". I am aware that you can't create an instance of an interface, but it seems like the only way I can get around this is to use a custom model binder and pass the FormCollection through. This seems really messy and I want to keep as much type specific code out of the project as I can - hence interfaces everywhere and Ninject to DI the concretes. Not only does custom model binding seem messy - won't I also lose my DataAnnotations?
Some code to describe what I have:
public ActionResult Create()
{
// I'm thinking of using a factory pattern for this part
var objectToCreate = new ConcereteType();
return (objectToEdit);
}
[HttpPost]
public ActionResult Create(IRecord record)
{
// check model and pass to repository
if (ModelState.IsValue)
{
_repository.Create(record);
return View();
}
return View(record);
}
Has anyone run into this before? How did you get over it?
Thanks!
but it seems like the only way I can get around this is to use a custom model binder
A custom model binder is the correct way to go. And by the way you should use view models as action arguments, not domain models or interfaces.
Not only does custom model binding seem messy - won't I also lose my DataAnnotations?
I don't know why you think that a custom model binder would make things messy. For me it's a great way to separate mapping logic into a reusable class. And, no you will not lose DataAnnotations. They will work perfectly fine on the concrete instance that the custom model binder would return.
Data passed to controllers action are simply holders for values. There shouldn't be any logic in them so there is nothing to decouple from. You can use concrete types (e.g Record) instead of interface (IRecord)
I made the same simple mistake. Ninject injects parameters into your constructor, but you added parameters to the Index Controller action.
It should look like this:
public class HomeController : Controller
{
private IRecord _record;
public HomeController(IRecord record)
{
_record = record;
}
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application. " +
_record .HelloWorld();
return View();
}
}
Make sense?
I'n new at this, so apologies if this isn't explanatory enough. I want to prepopulate a field in a form in asp.net mvc 3. This works;
#Html.TextBox("CompName", null, new { #value = ViewBag.CompName })
But when I want to prepopulate it with a value and send that value to my model, like this;
#Html.TextBoxFor(model => model.Comps.CompName, null, new { #value = ViewBag.CompName })
It won't work. Any ideas?
Thanks in advance!
So, I would suggest is to move to using viewmodels rather than the ViewBag. I made a folder in my project called ViewModels and then under there make some subfolders as appropriate where I put my various viewmodels.
If you create a viewmodel class like so:
public class MyViewModel
{
public string CompName { get; set; }
}
then in your controller action you can create one of those and populate it, maybe from some existing model pulled from a database. By setting that CompName property in the viewmodel, it'll have that value in your view. And then your view can look something like this:
#model MyNamespace.ViewModels.MyViewModel
#Html.EditorFor(model => model.CompName)
or #Html.TextBoxFor would work too.
Then back in your controller action on the post, you've got something like this:
[HttpPost]
public ActionResult MyAction(MyViewModel viewModel)
{
...
// do whatever you want with viewModel.CompName here, like persist it back
// to the DB
...
}
Might be that you use something like automapper to map your models and viewmodels but you could certainly do that manually as well, though the whole lefthand/righthand thing gets quite tedious.
Makes things much easier if you do it this way and isn't much work at all.
Update
But, if you really want to pass that value in view the ViewBag, you could do this:
In your controller action:
ViewBag.CompName = "Some Name";
Then in your view:
#Html.TextBoxFor(model =>model.Comps.CompName, new {#Value = ViewBag.CompName})
And that'll pre-populate the textbox with "Some Name".
I'd still go with the viewmodel approach, but this seems to work well enough. Hope that helps!
From your controller, if you pass a model initialized with default values using one of the View(...) method overloads that accepts a model object, these values will be used by the view when rendered. You won't need to use the #value = ... syntax.