MVC Areas and routing - asp.net-mvc-2

I'd like to have an area called "Products", where I can use routes such as
http://localhost/products/foo
http://localhost/products/bar
I would like to have the views and other assets organized into a folder structure like
/areas/products/views/foo/index.aspx
/areas/products/views/bar/index.aspx
I'd like to keep images, etc specifically related to each product (foo, bar) in their respective /area/products/views/(foo|bar)/ folder.
I also do not want to have to add a controller action for each product.
If I declare a route like
context.MapRoute(
"products-show-product"
, "Products/{id}"
, new { controller = "Products", action = "Index", id=UrlParameter.Optional }
);
and request the url
http://localhost/products/foo
then ProductsController.Index() is called, as I would expect. However, since the view "foo" is not in the views/products or views/shared folder, it isn't being found.
How can I do this so that I can keep each product's pages in a separate folder?

I don't have a concrete answer to your question since I am not sure about my understanding of it. However I have a general feeling for the direction for the solution.
When one starts to change locations of views, the corresponding methods that find those views also need to change. A simple approach would be to override the FindView and FindPartialView methods.
A simple demo. I created an Area called Blog, a Blog controller with an Index method. In my case I user the controller action as the SubFolder but I am sure that this can be extended to your case for each product folder. I assume that the product will be a request argument. Area http://www.freeimagehosting.net/uploads/85b5306402.gif
The basic idea is to interrogate the controllercontext for the controller, area, action and id and modify the what the default viewengine looks for. The default locations for area views looks like "~/Areas/{2}/Views/{1}/{0}.aspx", so we can basically inject values for the view name and in this case ActionName/Index. The view location will end up being ~/Area/Blog/Views/Blog/Index/Index.aspx.
This is just a rough outline, of the code that can be used. The string comparisons can definitely be updated to more robust methods. As it stands this method will work for the entire app as expected, except for the case when a request is made to the Blog area for the Index action.
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (controllerContext.RouteData.DataTokens ["Area"] == "Blog" )
{
if (String.Compare(controllerContext.RouteData.Values ["Action"].ToString(),"Index",true) == 0)
{
var viewLocation = String.Format("{0}/{1}", controllerContext.RouteData.Values["Action"].ToString(), viewName);
return base.FindView(controllerContext, viewLocation , masterName, useCache);
}
}
return base.FindView(controllerContext, viewName, masterName, useCache);
}

Related

OData error when bind to an element in a Master-Detail app

I have developed a Master-Detail app. In the Detail view, I am using a DynamicPage where within content, I am using an IconTabBar Element with 3 items. Each item is a different Fragment with a Smartform.
Master view is loading the data from an EntitySet (MasterEntity)
Detail view with a is using some fields from the Entity MasterEntity
Fragment 1: is using DetailEntitySet
Fragment 2: is using DetailEntitySet
Fragment 3: is using DetailEntitySet
Basically, when Detail View Controller is detecting the RouteMatch, I am receiving the selected line on Master View and I bind it to the View Detail. Once it is done, I am checking if the view was generated and then, calling a method to bind the Expanded entity to the iconTabBar Element which contains the 3 Fragments. The code is as follows:
function _onRoutePatternMatched(event) {
if (event.getParameter("name") === "detail") {
var path = event.getParameter("arguments").contextPath;
if (path !== " ") {
var path2 = "/" + path;
view.bindElement(path2);
if (view) {
this._setBindingToIconTab("Master2Detail");
}
} else {
view.unbindElement();
}
}
}
The _setBindingToIconTab function is as follows:
_setBindingToIconTab: function (sAssociation) {
view.byId("iconTabBar").bindElement(sAssociation);
}
iconTabBar is the ID I have assigned within the Detail view to the IconTabBar Element.
The problem is, when I execute it and it loads the first Fragment, all is ok. With the 2nd and 3rd, I got errors (but the values are displayed...). I am loading the Fragments when they are picked on the screen. If they were not generated, I instantiate and store them in an array. The errors I got are:
Assertion failed: The EDM property "DateFrom" was not found in the "ZZODATA_TEST_SRV.Master" entity type. -
sap.ui.comp.smartfield.ODataControlFactory
It is complaining about Fields from DetailEntitySet are not in MasterEntitySet.
Could you please give me a hand with this?
As there is no XML, running example etc. i can't tell you why this error occurs but..
Best practise is to biind also in the detail view the selected entity with expand to the detail and so on
MasterEntitySet->DetailEntitySet
As bindings are propagated to children there is no need to bind the iconTabBar again. Data is already there. Again i don't know your case, but most services look like this
MasterEntitySet->DetailEntitySet->DataVariantA(ForIconTab1)
->DataVariantB(ForIconTab2)
->DataVariantC(ForIconTab3)
In V4 your detail code looks like this
oView.bindObject({
path: "/MasterEntity(" + this._args.ID + ")",
parameters: {
$expand:`DetailEntityNavPath($expand=DataVariantANavPath()...`
},
events: {
dataReceived: (oEvent) => {...

How to copy a bsoncxx::builder::basic::document to another?

Is there any way to safely copy a bsoncxx document to another.
In following code I am not able to do that
class DocClass
{
private:
bsoncxx::builder::basic::document m_doc;
public:
bsoncxx::builder::basic::document& copy(bsoncxx::builder::basic::document& obj)
{
obj = m_doc; //Not allowed
//Error C2280 attempting to reference a deleted function
}
};
There should not be any harm to the object even after copy.
Please help.
Thanks,
Shibin
If you want to copy a bsoncxx::document::value, you can construct a new one from its view:
bsoncxx::document::value foo = ...;
bsoncxx::document::value bar{foo.view()};
bsoncxx::builder::basic::document is only movable, not copyable. However, you can get view to the underlying document from the builder with the view() method, which might be able to help you depending on your use cases. You'll still only be able to extract from the builder once though, so you'll have to rely on constructing a second document::value if you need more than one.

Separating classes

I have a class called UFDevice, in order to initialise it needs a location string.
I also have a class called UFResponse which among other things provides a location.
As the device only requires a location should I just take that in, so that it could be init'ed in some use case where there is no UFResponse.
Or should I pass in the whole response, in case later on it needs more info than just the location?
in pseudocode:
foundDevice(Data data) {
response = new UFResponse(data);
device = new UFDevice(response);
}
or:
foundDevice(Data data) {
response = new UFResponse(data);
device = new UFDevice(response.location);
}
or even should I encapsulate UFResponse in UFDevice, as currently it's only used to create UFDevices:
foundDevice(Data data) {
device = new UFDevice(data);
}
Future possibilities could include:
//maybe in the future I have saved a favourite location so need to do:
loadFavourite(String location) {
device = new UFDevice(location);
}
//or device needs more info
device = new UFDevice(location, color, ...20 more parameters...);
Where do I draw the line of separation? More importantly how can I decide this for myself in the future?
It sounds like a problem of interface segragation (https://en.wikipedia.org/wiki/Interface_segregation_principle). UFDevice is constructed from a UFResponse, but it doesn't need everything the UFResponse contains. It only needs a part of it, and you don't want UFDevice to be affected when UFResponse is changing in areas that should not affect UFDevice.
One approach is to have UFResponse inherit from an interface called UFDeviceParams, if this makes sense (might be multiple inheritance), and then UFDevice should get in its constructor a reference to UFDeviceParams.
This allows to initialize UFDevice based on the entire UFResponse, or based on a more light-weight instance of UFFavouriteParams (that also inherits from UFDeviceParams) that contains only the location + color etc...
foundDevice(Data data) {
response = new UFResponse(data);
device = new UFDevice(response);
}
loadFavourite(String location) {
params = new UFFavouriteParams(location);
device = new UFDevice(params);
}
To really know if this is the best approach for your case, one would need to learn more about your system, understand the use cases and the boundaries between modules. I recommend to watch Robert Martin's video on the Interface Segratation Principle and SOLID principles in general (https://cleancoders.com/category/solid-principles)

ASP.NET MVC2 - passing a parameter to a controller

This is for a project I'm doing in the university. We're using ASP.NET MVC2 to build a mini-prototype of a website just to show what we can do.
Now, I have a controller called ItemController.cs, a view Item/Index.aspx, a model Item.cs and a ViewModel ViewItems.cs. I use the ViewModel to pass information to the view from the controller. My question is - how do I call a controller method with parameters? Currently, I use
Html.ActionLink("View Event Items", "Index", "Item")
which points to ItemController's Index() method. Say I want it to take an int parameter (Index(int eventId)) How would I write the ActionLink to pass the parameter to it?
Also, if I have any errors in how I think this stuff works, please feel free to point them out.
You'll need to use the routevalues object (or RouteValuesDictionary) to pass values to your action.
In your example it would look like this:
Html.ActionLink("View Event Items", "Index", "Item", new { eventId = 1}, new {})
...where 2 is your event id. The second empty object (new {}) is for html attributes. Nate's answer is close, but I don't think there is an overload that takes a routevalues object as the second parameter.
I think that
Html.ActionLink("View Event Items", new { controller = "Item", action = "Index", id = 5 })
should get you in the vicinity of where you're looking to go.

MVC2 Add Collection of Object to Another Object

Not sure if this has been asked before, but I have an object (Restaurant) and I have a details view of that restaurant. On that details view, I want to have a list of cuisines that the restaurant offers. I want to also have a dropdownlist of the available cuisines and have the ability to click an "Add" button and it adds that cuisine to the Restaurant. I have a RestaurantCuisine table (using Entity Framework) that has a foreign key of the ID of the cuisine from a Cuisine table that has a primary key of ID.
So, now, my question, how do I do this? I sortof understand the concept behind the Create view and then the Create view post, but in this case, I'm not posting back the Restaurant object. So, how do I get the restaurant ID and the Cuisine ID so that I can add that to the restaurant cuisine collection?
Ok, so, now after investigating more, I believe I have asked the wrong question. My actual issue, is that I have a View that displays a Restaurant's details and I have a Details function in my controller for it. This works fine. The next step I want to do is have a dropdownlist with available Cuisines that this restaurant offers and have an 'Add' button next to it. And if you click on the 'Add' button, it adds the value of the item in the dropdownlist to the collection of cuisines setup in the Restaurant object.
Is this "easily" possible? I'm beginning to lose my faith in MVC2 :(
Ok, last try here. Let me ask this, does anyone know how to have a dropdownlist (I have now got this created) and have an "Add" button next to it, and get the selected value from that dropdownlist?
For those of you attempting to use VB.Net with MVC, I'm saying a prayer for you. It's rough. There are rarely any examples, and some syntax is not available that IS available in C#. So, as far as the answer to my original question. It seems as though it was my inexperience that caused the instant flare of the question.
After analying my problem, I figured out a way around it. First of all, let me clarify what I was attempting to do then explain what I did. The goal was to have an object (Restaurant in my case). And I wanted to have a list of properties (cuisines in my case) displayed on the Details view that I could assign to that object (Restaurant). The properties were cuisines that I had setup in a Cuisines table (CuisineId, Name, Description) and when you add a cuisine to a restaurant, it writes a record in another table RestaurantCuisine (RestaurantCuisineId, RestaurantId, CuisineId). So, getting the list of cuisines was the first task and have them display in a dropdownlist. That was done by creating a SelectList of cuisines in the Details view function by creating a CuisineRepository and calling a function that gets a list of all cuisines:
Dim cuiss As New CuisineRepository()
ViewData("Cuisines") = New SelectList(cuiss.FindAllCuisines().ToList(), "CuisineId", "Name")
Once you have this setup as a SelectList, it's easy to display that on the view by doing the following:
<h3>Cuisines:</h3>
<br />
<%= Html.DropDownList("Cuisines")%>
So, the problem after this was the real problem. I wanted to have an ActionLink that would be bound to the value of the DropDownList. I was unsuccessful in this attempt. But luckily, my jQuery is not dusty so I performed a little magic by doing the following:
<script type="text/Javascript">
$(document).ready(function () {
$('#Cuisines').change(function (e) {
$('#SelectedCuisine').val($('#Cuisines').val());
setHref($(this).val());
});
setHref($('#Cuisines').val());
});
function setHref(val) {
if (val) {
$("#addCuisine").attr('href', '/Restaurant/AddCuisine/' + $('#RestaurantId').val() + '?cuisineId=' + val);
}
}
</script>
<h3>Cuisines:</h3>
<br />
<%= Html.DropDownList("Cuisines")%>
<a id="addCuisine" href="">Add</a>
So, as you can see, I simply modify the href of the anchor as the user changes the selected Cuisine. Then, in my controller, I have a function setup to add a cuisine and to remove a cuisine from a restaurant:
Function AddCuisine(ByVal id As Guid, ByVal cuisineId As String) As ActionResult
Try
If ModelState.IsValid Then
'Dim selcuisInp As HtmlInputHidden = ViewData("SelectedCuisine")
Dim selectedCuisineId As Guid = New Guid(cuisineId)
Dim rc As New RestaurantCuisine
rc.RestaurantCuisineId = Guid.NewGuid
rc.RestaurantId = id
rc.CuisineId = selectedCuisineId
'rc.CuisineId = New Guid(selList.SelectedValue.ToString)
rc.CreatedDate = DateTime.Now()
'rc.CreatedBy =
db.AddToRestaurantCuisines(rc)
db.SaveChanges()
End If
Return RedirectToAction("Details", New With {.id = id})
Catch e As Exception
Dim innerE As String = e.InnerException.ToString
Return RedirectToAction("Details", New With {.id = id})
End Try
End Function
Function DeleteRestaurantCuisine(ByVal id As Guid) As ActionResult
Dim rc = (From rcs In db.RestaurantCuisines
Where rcs.RestaurantCuisineId = id
Select rcs).Single()
Dim rid As Guid = rc.RestaurantId
Try
db.RestaurantCuisines.DeleteObject(rc)
db.SaveChanges()
Return RedirectToAction("Details", New With {.id = rid})
Catch ex As Exception
Return RedirectToAction("Details", New With {.id = rid})
End Try
End Function
Notice the RedirectToAction. I had to pass the id of the object (Restaurant) that the Details view required and couldn't find the syntax anywhere. Finally, after a while of searching, found this site that the guy has a few examples of controllers and luckily he gives examples of both C# and VB. To add routeValues to the RedirectToAction, you declare a new list, and use an inline with and add your values with a period (.) preceding them:
Return RedirectToAction("Details", New With {.id = rid})
I hope this helps someone. It sure made the difference in my decision to use MVC. I am 100% sold even though I know I'm going to run into many roadblocks along the way.