How do I display customer specific information avoiding using xslt? - asp.net-mvc-3

My place of work currently maintains a website for several customers which is written using classic asp. Each customer requires specific parts of the website to be written specifically to them.
For example, customer A requires an Address to be input, displayed and stored in the following format:
Address Line 1
Address Line 2
Address Line 3
Address Line 4
Address Line 5
Postcode
whereas customer B requires the Address to be input, displayed and stored as:
Street
Town
City
Postcode
and so forth...
Therefore, my place of work took the path of storing the data as xml in the database and using xsl (of which I currently know little) to transform the data to html.
So if we require information from the user via a html form, the xml is transformed using xsl. The user then enters the information and submits the data via the form. An asp page is then used to validate the data. This asp page is specific to the xsl page used to display the form. Therefore, we are now in a postion where for each customer we have many xsl pages and many customer specific asp pages (where much of the code is duplicated).
I have been asked to move the site over to asp.net mvc3 and to remove much of the duplication and was wondering what would be the best way to cater for this customer specific field functionality. My preference would be to keep the data stored as xml as the database layer is accessed using com components which I would like to reuse without changing.
I have read that I could keep the xsl pages and develop an xslt view engine to display the html. However, I am not sure how I would validate the data when the user submits the form?
What would be the best way to display customer specific fields if I was to remove the xsl completely? Or would I have to have customer specific views and view models?
Any thoughts would be much appreciated.

If you really want to use MVC's built in validation / model functionality I think your best bet would be to use the XmlSerializer or use DataContracts to develop something that serializes to and from your XML (once its retrieved from the COM objects, so you don't need to re-code those), then you can use those classes as Models for MVC and use the standard data annotations for taking advantage of the richer MVC model functionality and skip the XSL step entirely.
To couple this with a custom specific view, what I typically do is override the default view engine to have one that actually will try names that are more specific to the customer/object and then fallback to a general one.
This view engine would allow you to pass a view to pass a view name (ie. FallbackViewEngine.BuildViewName("General", "Customer Name") and it would look for "General.Customer Name.cshtml" first and then "General.cshtml" as a fallback. This way you can actually use customer specific views in your view folder.
public class FallbackViewEngine : RazorViewEngine
{
const string NameSeparator = "==";
const string FileSeparator = ".";
public static string BuildViewName(string root, params string[] fallbackList)
{
if (string.IsNullOrWhiteSpace(root)) throw new ArgumentNullException("root");
if (fallbackList == null) throw new ArgumentNullException("fallbackList");
var sb = new StringBuilder(root);
foreach (var s in fallbackList)
{
if (string.IsNullOrWhiteSpace(s)) continue;
sb.Append(NameSeparator);
sb.Append(s);
}
return sb.ToString();
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (string.IsNullOrWhiteSpace(viewName)) throw new ArgumentNullException("viewName");
var names = viewName.Split(new string[] {NameSeparator}, StringSplitOptions.None);
var searched = new List<string>();
//iterate from specific to general
for (var i = names.Length; i >= 1; i--)
{
var result = base.FindView(controllerContext, string.Join(FileSeparator, names, 0, i), masterName, useCache);
if (result.View != null)
{
return result;
}
else
{
searched.AddRange(result.SearchedLocations);
}
}
return new ViewEngineResult(searched);
}
}

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.

Understanding relations between model view and controller

I went through a lot of readings about MVC and what each of these does is more or less clear. What I haven't understood yet is how they relate. I mean, I know about these relationships
but how are they implemented? What happens in an MVC framework?
I also have a few questions:
I read that a view can't be coupled with the controller, in other words it can't have a controller object inside, but then how does it use the proper controller if a view is supposed to trigger something in it?
How can the model update the view if its unique job is to represent data?
Is the business logic inside the controller or the model? I have read conflicting points of view
The most basic explination of MVC would be that you have each of the 3 layers.
Model
This contains your data. i.e database or set of classes.
View
This displays data to the user i.e your HTML page.
Contains controls for user interaction.
Controller
All access to data should go through this layer. i.e load data from your data source(model) and save data to your data source.
Carries out any data manipulation before saving or loading.
This create a separation of concerns theoretically allowing you to change anything in either layer without the other layer knowing or caring making for far more maintainable and readable code.
In practice this can become more complicated depending on how you wish to access data and display it although the basic principles still apply, occasionally meaning that each part of MVC pattern could be made up of smaller parts.
In terms of implementing it a good example would be ASP.Net MVC http://www.asp.net/mvc. the following could be a simple implementation of displaying some data via MVC using C#.
Model (C# class)
public class Person{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Controller
public ActionResult Index(){
return View(new Person() { FirstName = "Person", LastName = "1" });
}
View (Index.cshtml)
#model Person
Full name: #Html.Raw(Model.FirstName + " " + Model.LastName)
This would output onto the web page
Full name : Person 1
Please forgive me for any syntax errors, not tested.
More detailed post: http://www.tutorialspoint.com/design_pattern/mvc_pattern.htm

the best way to maintain html snippets in ASP.NET MVC to return in ajax calls

i'm looking for a best practices type answer here. basically i have a very chatty application which will be returning bits of data to the client very often. the bits of data returned eventually will end up being html added dynamically to the dom. so i'm trying to choose between the following 2 ways:
return just json data, create the html on the client side using jquery and possibly jquery templates
return the actual html that is build on the server side
i would like to make the choice that is most easily maintained. that is, i want the best way that will allow me to make updates to the html snippets very often.
i'm actually looking for a way to do #2 using ASP MVC partial views and want the ability to use string formatting. essentially i'm looking to make a call like this:
string sHtml = string.Format(GetNewTradeHtml(), "GOOG", "100", "635.50");
and I want GetNewTradeHtml() to actually get the html from a ASP MVC view instead of a string constant that might look like:
const string cNewTradeHtml = "<li><span>Symbol: {0}</span><span>Qty: {1}</span><span>Price: {2}</span></li>";
the string constants seems to be a popular way to do these kinds of things and i hate maintaining those...
basically i think i'm looking for a way to manage view several view templates that i can call ToString() on and get the raw html and use string formatting on it. and i'm hoping there is a suggested way to solve my particular problem natively in ASP MVC (without some hack). but perhaps (unfortunately) the string constants + string.format is the best way to maintain server side dynamic html...
UPDATE:
here's what i've learned since i've posted this question:
there are LOTS of posts here on SO about rendering a view into a string. a lot of different ways, some work with different versions of MVC some don't. some are cleaner than others, some are pretty heavy... ALL of which are normally some type of solution that require a controller context. so in most cases the solutions work great as responses to requests. but for my case, i need to do it outside of the context of a controller so now i need to either mock the controller or make a bunch of fake objects, neither of which i really want to deal with.
so i've determined that there is actually NO easy way to render a razor partial into its string representation without using a controller in a response. they really need to make an easy way to do this without mocking up controller context and request objects.
What are Views in asp.net mvc? They are just html templates, nothing more. They take model and replace template placeholders with model values. And indeed there's no more natural way to render html in asp.net mvc than using Views.
First, declare your view model
public class NewTradeViewModel
{
public string Symbol { get; set; }
public decimal Quantity { get; set; }
public decimal Price { get; set; }
}
than your controller action
public ViewResult GetNewTrade()
{
NewTradeViewModel model = new NewTradeViewModel;
model.Symbol = "GOOG";
model.Quantity = "100";
model.Price = 635.50m;
// PartialView, as you want just html snippets, not full layouts with master pages, etc
return PartialView("TemplateViewName", model);
}
and the very ordinary view - you may have any number of these, just change controller action to return specific one
#model NewTradeViewModel
<li><span>Symbol: #Model.Symbol</span><span>Qty: #Model.Quantity</span><span>Price: #Model.Price</span></li>
Since you mentioned that your app was "chatty" you should probably consider returning Json and rendering on the client side with a template engine.
This is really a toss up though because it looks like your snippets are pretty small.
If you do go with a sending JSON back and forth, I can recommend jquery templates or mustache
backbone.js can also help you better organize your client side components. It is pretty easy to get up and running with it. By default it works with jquery templates, but you can also plug in other templates if you like.
Here is a simple approach to storing templates in separate files, http://encosia.com/using-external-templates-with-jquery-templates/
ijjo,
Just looked at your question again and notice that you are referring to returning the html partialview as a string. there are loads of references here on SO to this type od function, but below is my version taken from an 'old' mvc app that's still in production. without further ado, it's an extension method that hooks into the controller:
public static class ExtensionMethods
{
public static string RenderPartialToString(this ControllerBase controller, string partialName, object model)
{
var vd = new ViewDataDictionary(controller.ViewData);
var vp = new ViewPage
{
ViewData = vd,
ViewContext = new ViewContext(),
Url = new UrlHelper(controller.ControllerContext.RequestContext)
};
ViewEngineResult result = ViewEngines
.Engines
.FindPartialView(controller.ControllerContext, partialName);
if (result.View == null)
{
throw new InvalidOperationException(
string.Format("The partial view '{0}' could not be found", partialName));
}
var partialPath = ((WebFormView)result.View).ViewPath;
vp.ViewData.Model = model;
Control control = vp.LoadControl(partialPath);
vp.Controls.Add(control);
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
using (var tw = new HtmlTextWriter(sw))
{
vp.RenderControl(tw);
}
}
return sb.ToString();
}
}
usage (as per archil's example above):
public ViewResult GetNewTrade()
{
NewTradeViewModel model = new NewTradeViewModel;
model.Symbol = "GOOG";
model.Quantity = "100";
model.Price = 635.50m;
// PartialView, as you want just html snippets, not full layouts with master pages, etc
return this.RenderPartialToString("TemplateViewName", model);
}
good luck and enjoy...

Few questions... ModelState.IsValid and Grouped CheckBox Values

Using ASP.NET MVC when I create my model, then a controller based on the model with CRUD operations, the CRUD views are generated. I added some code using Fluent API to require certain fields but for some reason the ModelState.IsValid passes even when these fields are not completed. What determines whether this passes or not? I thought it was based on your model property data types and other things like being required or maxlength, etc....
Also, I have manually added code to grab a list of Categories from the database and generate a checkbox for each one in the View. This is a navigation property for the Project model where there is a many-many relationship. To get the group of checked values in the Create(Project project) method in the controller I use:
var selected = Request["categories"].Split(',');
This however, throws the classic Object reference not set to an instance of an object error if no values are checked. So what I want to know is, how can I determine that this does not have any values so I can do something else once detected?
I added some code using Fluent API to require certain fields but for
some reason the ModelState.IsValid passes even when these fields are
not completed.
ASP.NET MVC doesn't know anything about the Fluent API of Entity Framework and doesn't evaluate this configuration. You only can use the data annotations which MVC will recognize:
[Required]
public string SomeProperty { get; set; }
...how can I determine that this does not have any values so I can do
something else once detected?
Not sure if I understand it correctly but I'd say:
var categories = Request["categories"];
if (categories != null)
{
var selected = categories.Split(',');
// ...
}
else
{
// do something else
}

ASP.NET MVC 3 areas and DDD aggregate roots

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?

Resources