ASP.NET MVC 3 areas and DDD aggregate roots - asp.net-mvc-3

I'm building a site and am considering using areas to cover a similar scenario to the one I'm about to describe.
I currently have a site with 4 sections, lets call these Create, Manage, Section 3 and Section 4
Create and Manage are actions on the domain object that I'm working with. The domain object has a number of collections of sub objects that relate to it. These need to be created and managed as well.
I am using Products as an example so as not to give anything away but it doesn't quite fit the same domain - so please don't say "Why don't you have a Products section"
My current implementation has a ManageController which has Actions like Categories, Category, ProductsForCategory
I'm thinking I need areas, however, some URLs will need to be scoped so I want
/Manage/Category/8/Products
/Manage/Category/8/Product/1
Is this possible using Areas? Do I need to set up new routing rules?
Would my CategoryController have 2 parameters on the action e.g.
public ActionResult Product(int categoryId, int productId)
{
//get category
var cat = GetCategory(categoryId);
//get product
var product = cat.Products.SingleOrDefault(x=>x.Id == productId);
if(product == null)
return RedirectToAction("Index","Manage");
return View(product);
}
Then I would have a routing rule that passed in the category id?
Is my thinking on this correct?

This is possible with Areas.. although it's my understanding that areas are mainly recommended for structuring your code into a meaningful folder structure to deal with large-ish MVC apps, whereas it looks like you want to use it for achieving nested routes?
To map your nested route to /Manage/Category/8/Product/1 you could create your "Manage" area, and then add a route like so:
context.MapRoute(null,
"Manage/{controller}/{categoryId}/{action}/{id}",
new
{
action = "Product",
id = "1",
categoryId = "2"
});
You then create an action method to accept those params:
public ActionResult Product(string categoryId, string id)
However, your question talks about aggregate DDD roots, so I suspect I've only answered part of the question?

Related

How do I bypass the limitations of what MVC-CORE controllers can pass to the view?

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.

How to show all products in nopCommerce?

I would like to show all products without creating any new category and mapping to it.
Can any one help me?
Thanks in advance.
It's true; in order for a product to be displayed in nopCommerce, it must be assigned to a category. Your best bet is to create a top-level umbrella category, like "All Products", and add all of your products to that umbrella category.
As far as I know their must be a category associated with product.
You can create a plugin, map a route to it (for example map to 'allproducts' route), and create your own Controllers, Actions and Views within the plugin. Then insert in the main menu a link to the route by mean of
#Html.RouteLink(routeName, null) //or similar overloads
The plugin creation part is too huge to be described here. http://www.nopcommerce.com/documentation.aspx is a good start.
PS:/ Regarding routing, each plugin can implement a route registrar by implementing the "IRouteProvider" interface.
:)
You can do that by modifying the code. I have done it before. It is actually quite simple.
Modify the Category action of the Catalogue controller to receive a nullable CategoryId:
public ActionResult Category(int? categoryId, CatalogPagingFilteringModel command){
modify the action to not break because of this nullable paramters.
The most important part to modify is where you build the list of category Ids to filter:
var categoryIds = new List<int>();
if (category != null)
{
categoryIds.Add(category.Id);
}
if (_catalogSettings.ShowProductsFromSubcategories)
{
//include subcategories
categoryIds.AddRange(GetChildCategoryIds(category.Id));
}
The mothod _productService.SearchProducts will receive an empty list of category Ids and will not filter any products.

reading related data after a selection of a foreign key - MVC3 and EF4

I am new to MVC and EF and I have a question.
I have built a site with models views controllers etc.
On an edit view for a Case (pretty big model so I won't post it here) I have a FK to a Customer model using CustomerID. When a user selects a customer id from a drop down list, I would like to display CustomerName, CustomerPhone etc after the selection of the ID. I think I might need to do a post back for this to work?
Also, do I need to Include the related entities as part of the initial data "get"? I have read some detail on that but I dont fully understand how that needs to work.
Please let me know if I should post more info. Thanks!
Here is my ActionResult for Edit
public ActionResult Edit(int id)
{
Cases cases = db.Cases.Find(id);
//related data needs to loaded to show related data fields
//include related data entities
var v = db.Cases.Include("Customers");
ViewBag.TechnicianID = new SelectList(db.Technicians, "TechnicianID", "LastName", cases.TechnicianID);
ViewBag.BranchID = new SelectList(db.Branches, "BranchID", "BranchName", cases.BranchID);
ViewBag.EngineModelID = new SelectList(db.EngineModels, "EngineModelID", "EngineModelName", cases.EngineModelID);
ViewBag.CaseCategoryID = new SelectList(db.CaseCategories, "CaseCategoryID", "CategoryName",cases.CaseCategoryID);
ViewBag.Qualified = new SelectList(new[] { "YES", "NO", "PARTIALLY" });
ViewBag.CaseStatus = new SelectList(new[] { "OPEN/IN PROCESS", "CLOSED" });
return View(cases);
}
The line
var v = db.Cases.Include("Customers")
is what I am trying to use to load related customer data and then show in my edit view like this:
#Html.EditorFor(model => model.Customer.CustomerName)
Well it depends on what you are trying to do. You could include a model which holds all the required data and send it with every call on that page (initial empty ofcourse)
Once you selected the customer, do post-back and send the customerId to your application and return the same page with the desired model.
You could do that via AJAX too.
Also, do I need to Include the related entities as part of the initial data "get"?
Not sure if I understand what you are trying to say. You mean that you think you would have to send all customer's data down to the page and select the related data on client side?

Achieving product/Apple-ipad in place of product/Index/23

I wasn't able to put the write words for the Title, so I explained what I'm looking after.
Currently, I am using following ActionLink code in View.
#Html.ActionLink(#product.Name, "Index", "Product", new { id = #product.Id }, null)
This code redirects to following action method in Product Controller
public ActionResult Index(int id)
{
Product product = pe.Products.Where(p => p.Id == id).First();
ViewBag.Title = product.Name;
ViewBag.Description = product.MetaDescription;
ViewBag.Keywords = product.MetaKeywords;
return View(product);
}
Now, what i want: instead of mysite.com/Product/Index/22 , my URL should be something like mysite.com/Product/Apple-ipad.
I know, I can use product Name instead of Id and pass it to the action method. But, this way i think that the queries will get slower since Id field is indexed but Name isn't. Is this the only option at my disposal. Let me know how will you handle this requirement.
Use the product name in the route instead of ID and put a nonclustered index on the product name column in the database.
That way you don't get a performance hit on the select (although you will get one on the insert/update/delete but I suspect those happen far less than the selects).
CREATE INDEX IX_[index_name]
ON [schema].[table_name] ([column_name]);
Another way I've seen used in a few places is using both the name and the id like mysite.com/Product/Apple-ipad/22. The name is not actually used by the code, and it's just there for SEO. One big disadvantage of this is that someone that does not like your site can put various urls leading to the same content all over the internet for google to find. Google doesn't like that so your site is penalized and you are worse than before.

ASP.Net MVC View returned, but how can I show additional information?

I have a SQL database with has the following: Customer, Item, Clothing and Food.
Item holds a key to Clothing or Food.
Item also holds a key to Customer. Therefore a customer can have an item, which may be of food or clothing.
I am using ADO.Net Entity Framework and have this generated automatically.
I currently have the following set-up: A person may enter their ID on the webpage and this is sent via a form post where the controller picks it up and queries the database using LINQ to get the customer. The customer view (details) is then returned. I can now see all the customer details etc.
However, what I want is to be able to see the items the customer has, the different food items and clothing items, but I am unsure how to do this. I also want to be able to allow the user to edit one field of the clothes and food items tables. Any idea how I would implement this?
Here is an ActionResult in my CustomerController:
public ActionResult Details(int id)
{
var cust = (from c in dataModel.Customers
where (c.MembershipID == id)
select c).First();
return View(cust);
}
I can also write cust.Items which is the entity which I want to display in the view with the customer (their items). How would I display this in the view also?
Hopefully this makes it a little more clear on what I am trying to achieve and how.
Thanks.
Using Entity Framework, if you're tables are linked properly with the right foreign keys and all that then your Customer entity should have a property that is a collection of Items.
You could also create your own strongly typed ViewModel that has a field for Customer and implement your own properties for Clothing and Food and populate those with another query.
This question was asked last night but its similar. The guy in the question wanted information to populate a dropdown passed in. You want something similar, not for a dropdown, but to fill in textboxes to edit. How to properly populate drop downs from ViewData in controller on multiple views in ASP.NET MVC
To create a ViewModel start by creating a new class and name it CustomerAndItemsViewModel, for example.
public class CustomerAndItemsViewModel
{
public Customer Customer { get; set; }
public IQueryable<Items> Items { get; set; }
}
public ActionResult Details(int id)
{
var cust = (from c in dataModel.Customers
where (c.MembershipID == id)
select c).First();
var items = (from i in dataModel.Items
where (i.MembershipID == cust.MembershipID)
select i;
return View(new CustomerAndItemsViewModel { Customer = cust, Items = items });
}
And don't forget that you will no longer be passing a Customer to your view. So you need to change the line at the top to something like:
#model Your.Path.To.CustomerAndItemsViewModel
Typically, if you want to pass back information that is not contained in just one of your entities, you have to create a class that encompasses more than one object. So, if you want a page that displays your customer information, and all their items (which they can then edit), you would need to have a the controller action pass back a "CustomerAndItems" object (or something similarly named). This object would hold a reference to the Customer as well as a collection of their Items. (You build the CustomerAndItems object within your Action.)
Then, your view would be strongly typed to CustomerAndItems, and you can then display each piece of information as you normally would.

Resources