This is my database table
DB Table
how to generate a sitemap?
controller is category
action is Id
that is look like it
Page
TreeMenu.cs:
using System;
using System.Collections.Generic;
namespace Pmvc.Models
{
public partial class TreeMenu
{
public int TreeId { get; set; }
public int TreeParentId { get; set; }
public string TreeName { get; set; }
public List<TreeMenu> Children { get; set; }
}
}
StoreDetailsDynamicNodeProvider.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MvcSiteMapProvider.Extensibility;
using Pmvc.Models;
namespace Pmvc
{
public class StoreDetailsDynamicNodeProvider : DynamicNodeProviderBase
{
PMVCEntities pmvcDB = new PMVCEntities();
public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
{
var nodes = new List<DynamicNode>();
foreach (var treemenu in pmvcDB.TreeMenu)
{
nodes.Add(CreateNode(treemenu));
}
return nodes;
}
private DynamicNode CreateNode(TreeMenu treemenu)
{
DynamicNode node = new DynamicNode();
node.Action = "ProductDetails";
node.Controller = "Product";
node.Title = treemenu.TreeName;
if (treemenu.Children != null)
{
foreach (var child in treemenu.Children)
{
node.Children.Add(CreateNode(child)); //This line is wrong!
}
}
return node;
}
}
}
wrong:
'MvcSiteMapProvider.Extensibility.DynamicNode' is not include 'Children' definition,and not find Extension Methods 'Children'
public class StoreDetailsDynamicNodeProvider : DynamicNodeProviderBase
{
PMVCEntities pmvcDB = new PMVCEntities();
public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
{
// Build value
var returnValue = new List<DynamicNode>();
// Create a node for each album
foreach (var treemenu in pmvcDB.TreeMenu)
{
DynamicNode node = new DynamicNode();
node.Action = "ProductDetails";
node.Controller = "Product";
node.Title = treemenu.TreeName;
node.RouteValues.Add("id", treemenu.TreeId);
returnValue.Add(node);
}
// Return
return returnValue;
}
}
This is my table
TreeId TreeParentId TreeName
1 0 Category 1
2 1 Menu Item X
3 1 Menu Item Y
4 1 Menu Item Z
5 0 Category 2
6 5 Other Menu 1
7 5 Other Menu 2
8 0 Empty Category
9 7 Menu Lvl 3
How do I write child node code?
Take a look at the MvcSitemap provider. It's pretty easy to write a dynamic provider that reads from your database.
Edit - did you read the documentation or attempt to implement anything? Copied almost verbatim from their site:
Within the sitemap config file, add the dynamic node where appropriate:
<mvcSiteMapNode
title="Details"
action="Details"
dynamicNodeProvider="MyProjectName.MyDynamicNodeProvider, MyProjectName" />
Then, write the actual node provider:
public class MyDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
{
// Build value
var returnValue = new List<DynamicNode>();
// ... Here, get values from your database and build up
// the list of nodes. They can be in a tree structure
// too since it appears that's how your data is - just
// build the nodes in that way with sub-lists.
// Return
return returnValue;
}
}
And that's it.
Edit 2 - addressing other answer.
The code that you provided flattens the tree structure. In your original post you showed that you are already able to print the tree structure. Whatever code you had in your view, you can apply the same principles of a depth-first traversal.
Just in case that was a mockup and not actual code, here's some quick psuedo code to demonstrate an depth-first traversal.
public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
{
// Build value
var nodes = new List<DynamicNode>();
// Get all categories without parents.
var rootCategories = db.GetRootCategories();
// Loop all root level categories, creating a node for each and adding
// to the return value.
foreach (var category in rootCategories)
{
nodes.Add(CreateNode(category));
}
return nodes;
}
private DynamicNode CreateNode(Category category)
{
// Create a new node for the category
DynamicNode node = new DynamicNode();
node.Name = category.Name;
// ... set node properties here ...
// If the category has children, then continue down the tree
// and create nodes for them. This will continue recursively
// to the bottom of the tree.
// Note that I'm just guessing on the property names because
// you didn't show us the code for what your entity actually
// looks like. This is why you should always show what code
// you've attempted - you can get better, more precise answers
// instead of complete guesses.
if (category.Children != null)
{
foreach (var child in category.Children)
{
// For each child, recursively branch into them.
node.Children.Add(CreateNode(child));
}
}
return Node;
}
Please note that this is no way tested or researched - it's only to show a tree traversal.
Related
When I try to index a doc of my defined type, having a list which is supposed to be mapped as a nested-object ("type":"nested"), it's getting mapped as a regular object type.
Take a look at the code:
I've got a simple class like this one:
[ElasticType()]
public class MyJob
{
[ValueFieldAttribute]
public int jobCode { get; set; }
[ValueFieldAttribute(Type = FieldType.nested)]
public IList<JobProfessionalFieldInfo> JobProfessionalFields { get; set; }
}
The code for the JobProfessionalFieldInfo class is:
[ElasticType()]
public class JobProfessionalFieldInfo
{
[ValueFieldAttribute]
public int JobId { get; set; }
[ValueFieldAttribute]
public int CategoryId { get; set; }
}
The code for the ValueFieldAttribute class is:
public class ValueFieldAttribute : ElasticPropertyAttribute
{
public ValueFieldAttribute()
: base()
{
this.Store = false;
this.Index = FieldIndexOption.not_analyzed;
}
}
My program:
static void Main(string[] args)
{
ConnectionSettings node = new ConnectionSettings(new Uri("http://localhost:9200"));
node.SetDefaultIndex("jobs");
ElasticClient client = new ElasticClient(node);
List<JobProfessionalFieldInfo> list = new List<JobProfessionalFieldInfo>();
list.Add(new JobProfessionalFieldInfo { CategoryId = 1, JobId = 1 });
list.Add(new JobProfessionalFieldInfo { CategoryId = 2, JobId = 2 });
var res = client.Index<MyJob>(new MyJob
{
jobCode = 1,
JobProfessionalFields = list
},"jobs", "MyJob",1);
}
Now, when I run it, it indexes the object successfully... BUT(!!) when I get the mapping of the index with GET jobs/MyJob/_mapping, I see that jobProfessionalFields has no "type":"nested" in its mapping.
That results in a query like the following one, returning the indexed doc while it's not supposed to get it back (that's what nested-type is for right?..):
GET jobs/_search
{
"query":
{
"bool":
{
"must":
[
{"match": {"jobId":1}},
{"match": {"categoryId":2}}
]
}
}
}
It's not the end:
I'd a look at here,
there the guy that answered tells that when we use annotations we need to manually call the createIndex and Map methods, but the problem is that I don't have any generic Map method...!
Take a look at here: (just to make you get into the link - here's its start..)
namespace Nest
{
public partial class ElasticClient...
And I don't know how to use the non-generic Map method to put the mapping of my MyJob class.
How can I cause this stuff to map the jobProfessionalFields as nested-type dudes?
Thanks for any help of you guys!
OK, got it LOL!
The MapFromAttributes<> is the right generic method for putting the mapping (at least in the current Nest version I'm using - 0.12.0).
But it demands a manual call for the index creationg, o.w it gives an IndexMissing exception (like the guy in the above mentioned link said).
client.CreateIndex("jobs", new IndexSettings { });
var res = client.MapFromAttributes<MyJob>("jobs","MyJob");
But that's really interesting why isn't it enough to just define the
[ElasticProperty(Type = FieldType.nested)],
in order to get the nested mapping though..
I would be glad to get an answer for that one.
I want to create a custom attribute that will be applied to classDeclarations. I can enumerate attributes from other methods on the class, but not the classDeclaration itself because it is some sort of special method.
I know it is possible though because SysObsoleteAttribute (called from the kernel) is placed in classDeclarations all over.
In Classes\CustCustomerService\create I just copied the attributes to Classes\CustCustomerService\classDeclaration at the top for this test.
[AifDocumentCreateAttribute, SysEntryPointAttribute(true)]
class CustCustomerService extends AifDocumentService
{
}
I created a static method on a new class:
static public void AttribsOfSysEntryPointAttributeOnMethod
(
str _sNameOfClass,
str _sNameOfMethod,
str _nameOfAttribute
)
{
int nClassId;
SysDictMethod sdm;
Object attributeAsObject;
SysDictClass sysDictClass;
Array attribArray = new Array(Types::Class);
int i;
nClassId = Global::className2Id(_sNameOfClass);
sysDictClass = new SysDictClass(nClassId);
sdm = new SysDictMethod(UtilElementType::ClassInstanceMethod, nClassId, _sNameOfMethod);
attribArray = sdm.getAllAttributes();
if (attribArray)
{
for (i=1; i<=attribArray.lastIndex(); i++)
{
attributeAsObject = attribArray.value(i);
info(strFmt("[%3] Attrib Class Id: %1 [%2]", classIdGet(attributeAsObject), classId2Name(classIdGet(attributeAsObject)), _sNameOfMethod));
}
}
else
{
// Unable to get attributes, try another way
error(strFmt("Unable to retrieve attribute array for method %1", sdm.name()));
// It is, so let's try and enumerate ALL attributes and output them directly from class dec
sdm = sysDictClass.objectMethodObject(1);
if (attribArray)
{
for (i=1; i<=attribArray.lastIndex(); i++)
{
attributeAsObject = attribArray.value(i);
info(strFmt("[%3] Attrib Class Id: %1 [%2]", classIdGet(attributeAsObject), classId2Name(classIdGet(attributeAsObject)), _sNameOfMethod));
}
}
else
error(strFmt("Still unable to retrieve attribute array for method %1", sysDictClass.objectMethod(1)));
}
}
Then created a job to call it, and you can see how it works for one method, but not the other.
static void Job5(Args _args)
{
AttributeReflection::AttribsOfSysEntryPointAttributeOnMethod("CustCustomerService", "create", "SysEntryPointAttribute");
AttributeReflection::AttribsOfSysEntryPointAttributeOnMethod("CustCustomerService", "classDeclaration", "SysEntryPointAttribute");
}
Any ideas how to enumerate Attributes from the classDeclaration??
The classDeclaration is not a method and cannot be called. Hence your sysDictClass variable is null.
Googling reveals that the getAllAttributes method exits on DictClass:
attribArray = sdm ? sdm.getAllAttributes() : sysDictClass.getAllAttributes();
I am new to ASP.NET MVC and I am stuck on a point. I am working on a classified site. My situation is, I have a lot of categories in which a user can post their ads and each ad category have different View. I have created a Controller Action like
public ActionResult PostAd(string CategoryName, string SubCategoryName)
{
if(categoryName == "Vehicle" && SubCategoryName == "Cars")
{
var model = new CarAdViewModel();
// set CarAdViewModel properties...
return View("CarAdCreateView", model);
}
else if(categoryName == "Vehicle" && SubCategoryName == "Bikes")
{
var model = new BikeAdViewModel();
// set BikeAdViewModel properties...
return View("BikeAdViewModel", model);
}
else if(categoryName == "Property" && SubCategoryName == "RentHouse")
{
var model = new RentHouseAdViewModel();
// set RentHouseAdViewModel properties...
return View("RentHouseAdViewModel", model);
}
else................... so on and so on
}
My problem is I have huge number of Categories and Sub Categories almost 60+. And if I keep on coding like above for 60+ categories and subcategories, my PostAd method is going to blast and become unmanageable.
Please tell me some best practice or pattern which can bring me out of this problem.
Unfortunately, some of what you are doing cannot be avoided. There needs to be some form of model and view selection based on category.
Use a factory pattern. Create a base class:
public abstract class BaseCategory
{
public abstract string GetViewName();
public abstract Object CreateModelFromFormData();
}
For each category, create a sub-class derived from BaseCategory and implement the abstract functions.
In your action, do the following:
public ActionResult PostAd(string categoryName, string subCategoryName)
{
BaseFactory factory;
if (categoryName == "Vehicle")
{
if (subCategoryName == "Cars")
{
factory = new CarsFactory();
}
else ...
}
else ...
return View(factory.GetViewName(), factory.CreateModelFromFormData());
}
I have a couple reasons for this schema:
I am purposefully using if/else for the factory selection. Your controller is going to be created and re-created for every action call. So pre-populating a list will constantly and needlessly create objects for categories that will not be selected. A simple if/else will be more efficient. If you want to prevent the if/else, you can put your factories in a Dictionary and select based on the categories, but that would be a lot of needless constructor actions.
I made the CreateModelFromFormData a function because I assume you'll need to copy data from the posted form data. This may require passing in data, but I left the function parameterless.
I used base/derived classes because the copying of the form data will probably need to be custom from the model being created and the form data being posted. Also, saving to persistent storage (file or database) may be category-specific as well.
It would be one of some possible solutions
public class PostAdData
{
public string CategoryName;
public string SubCategoryName;
public string ViewName;
public Type Model;
}
public class PostController : Controller
{
private readonly List<PostAdData> _theData;
public HomeController()
{
_theData = InitializeData();
}
public ActionResult PostAd(string categoryName, string subCategoryName)
{
var data = _theData.FirstOrDefault(c => c.CategoryName == categoryName && c.SubCategoryName == subCategoryName);
if (data != null)
{
var model = Activator.CreateInstance(data.Model);
return View(data.ViewName, model);
}
return View("Error");
}
[NonAction]
public List<PostAdData> InitializeData()
{
var result = new List<PostAdData>
{
new PostAdData
{
CategoryName = "Vehicle",
SubCategoryName = "Cars",
ViewName = "CarAdCreateView",
Model = typeof (CarAdViewModel)
}
};
return result;
}
}
You should make this data driven. You create a lookup table that has a compound primary key of category and subcategory. Then it has a table with View in it. Then you simply ad rows for each category/subcategory/view combination.
If you absolutely don't want a database, then you can use a simple hashset or dictionary.
var views = new Dictionary<Tuple<string,string>,string>();
views.Add(new Tuple<string,string>("Vehicle", "Cars"), "CarAdCreateView");
Then in your PostAd you just lookup the correct view.
What a beautiful solution on www.asp.net to my question, here is the link : http://forums.asp.net/t/1923868.aspx/1?ASP+NET+MVC+Conditional+ViewModel+Abstraction
Edit:
My code is :
public class AdsController : Controller
{
private readonly IAdService _adService;
public AdsController(IAdService adService)
{
_adService = adService;
}
public ActionResult PostAd(string Category, string SubCategory)
{
//Here I will call
var strategy = GetStrategy(CategoryName, SubCategoryName);
strategy.FillModel(_adService );
return View(strategy.ViewName, strategy.Model);
}
}
I'm very new to ASP.NET, but I know a little programming in Java. I want to use a ZIP code to query a database which will return a string, then use that string to query another database. I wanted to do this on the same control model. I thought it would be easy, and it sounds pretty easy.
When I created the controller, I put the model class of the first database, and, so far, I've gotten as far as querying the first database, but now that I have the string I want to query a second database through the DBEntities.
This displays an error saying:
> The model item passed into the dictionary is of type
> 'System.Collections.Generic.List`1[FinalBallot.Models.AgainCandidate]',
> but this dictionary requires a model item of type
> 'System.Collections.Generic.IEnumerable`1[FinalBallot.Models.ZipTable]'.
Is there a way to solve this in an easy way?
public class Default1Controller : Controller
{
private CandidatesDBEntities db = new CandidatesDBEntities();
public string districString = "";
//
// GET: /Default1/
public ViewResult Index(string searchString)
{
var queryZip = from s in db.ZipTables select s;
var queryCandidates = from s1 in db.AgainCandidates select s1;
double sT = 0;
//method so it doesnt display the whole db
if (String.IsNullOrEmpty(searchString))
{
queryZip = queryZip.Where(s => s.ZipL.Equals(0));
}
if (!String.IsNullOrEmpty(searchString))
{
sT = double.Parse(searchString);
queryZip = queryZip.Where(s => s.ZipL.Equals(sT));
try
{
districString = queryZip.ToList().ElementAt(0).District;
}
catch
{
}
if (!String.IsNullOrEmpty(districString))
{
queryCandidates = queryCandidates.Where(s1 => s1.District.Equals(districString));
}
}
return View(queryCandidates.ToList());
}
In your view, did you specify the model to be IEnumerable<ZipTable>? The model that you're passing to your view is IEnumerable<AgainCandidate>, so you would get an error if you specified your model as something else. You'd need to change the model in your view to be IEnumerable<AgainCandidate>.
UPDATE:
Based on your revised explanation, you can do a couple things:
1) create a "ViewModel" that has two properties for each of your collections you want to display on the page like so:
public class MyViewModel
{
IEnumerable<ZipTable> Zips { get; set; }
IEnumerable<AgainCandidate> Candidates { get; set; }
}
Instantiate that in your action method and return that as your model. This would be my preferred approach.
2) Stash your two collections in the ViewData bag in your action method:
ViewData["Zips"] = queryZip.ToList();
ViewData["Candidates"] = queryCandidates.ToList();
return View(ViewData);
You can pull this data in your view like this:
#foreach (var zip in ViewData["Zips"] as IEnumerable<ZipTable>)
{
...
}
The Goal is to have a list of options (that a user can chose through radio buttons) in one place(for eg: a yaml config file). No other place should have this list hard-coded
I've done something similar to create select elements, and I think enums worked just fine. Doing radio buttons should be very similar. I've set it up so that the labels can be defined in the messages file. I'm going to try to excerpt the relevant portions from my larger auto-form-generation code (using FastTags) the best I can. It's a bit heavy for this one case but it makes sense in the larger system.
I use the tag like #{form.selector 'order.status' /}, which looks find the variable named order in the template, sees that status is declared as public Status status, and then goes to find all the values of the Status enum and generate options for them in the select element.
First, I use a FieldContext object which just contains a bunch of info that's used by the other code to determine what to generate along with some utility methods:
public class FieldContext {
public final Map<?,?> args;
public final ExecutableTemplate template;
public final int fromLine;
public Class clazz = null;
public Field field = null;
public Object object = null;
public Object value = null;
private Map<String,String> attrs = new HashMap<String,String>();
private Map<String,Boolean> printed = new HashMap<String,Boolean>();
private List<Option> options;
...
Then I have this in another helper class (its info gets added to the FieldContext):
public List<Option> determineOptions(FieldContext context) {
List<Option> options = new ArrayList<Option>();
if (context.field.getType().isEnum()) {
for (Object option : context.field.getType().getEnumConstants()) {
options.add(new Option(option.toString(), Message.get(option.toString())));
}
}
return options;
}
then the tag declaration is
public static void _selector(Map<?,?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) {
String field_name = args.get("arg").toString();
TagContext.current().data.put("name", field_name);
SelectHelper helper = HelperFactory.getHelper(SelectHelper.class);
try {
FieldContext context = new FieldContext(field_name, args, template, fromLine);
helper.autoconfigure(context);
TagContext.current().data.put("selected", helper.determineValue(context));
out.print("<div class=\"formutil-field formutil-selector\">");
out.print("<label for=\"" + context.getAttr("id") + "\">");
out.print(helper.findOrCreateLabel(context));
out.print("</label>");
out.print("<select");
context.printAttribute(out, "id", "name");
out.print(">");
if (context.hasOptions()) {
for (Option option : context.getOptions()) {
out.print("<option value=\"" + option.value + "\">" + option.label + "</option>");
}
}
out.print("</select>");
context.printErrorIfPresent(out);
context.printValidationHints(out);
out.println("</div>");
}
...
}