Umbraco 8: Get all content of specific type in WebAPI class - umbraco

Q) How do I access my website content, e.g. books collection in my WebAPI class?
In an Umbraco 8 website project, I am using the following in my razor view to get content, which works fine, allowing me to then iterate over the collection and build my page, all great.
#inherits Umbraco.Web.Mvc.UmbracoViewPage
#using ContentModels = Umbraco.Web.PublishedModels;
#using System.Text.RegularExpressions;
#{
var books = Model.Root().DescendantsOfType("Book")
.Cast<Book>().Where(x => x.ShowInWebsite);
}
//...
However, this doesn't compile in my WebAPI class - I don't know how to fix the references needed.
Error I'm getting:
The name 'Model' does not exist in the current context
Other things I've tried:
var books = Umbraco.ContentAtRoot().DescendantsOrSelf().OfTypes("Book");
// Which gives error:
IEnumerable<IPublishedContent>' does not contain a definition for 'DescendantsOrSelf' and the best extension method overload 'PublishedContentExtensions.DescendantsOrSelf(IPublishedContent, string)' requires a receiver of type 'IPublishedContent

You WebApi class doesn't have a model, but as long as it inherits from UmbracoApiController you should be able to do
Umbraco.ContentAtRoot()...
instead?
https://our.umbraco.com/documentation/reference/routing/webapi/
According to https://our.umbraco.com/documentation/Reference/Querying/IPublishedContent/Collections/#oftypes you should then be able to do
Umbraco.ContentAtRoot().DescendantsOrSelf().OfTypes("Book")

Thanks to the comment by #Jannik Anker
I managed to get my collection by so:
var parent = Umbraco.ContentAtRoot().First().Children().FirstOrDefault(x => x.Name == "Booklist");
if (parent != null)
{
var books = parent.Children();
var test = "";
foreach (var book in books)
{
test += book.Id + ": " + book.Name + "\r\n";
}
return test;
}

Related

Getting error when multiple results passing to view in asp.net MVC5

I use asp.net mvc 5 and EF 6 .I have following code in my controller
var resultOne = stats.GetPlayerStatsByParam(year, modeOne); //first
ViewData["more"] = stats.GetTeamStatsID(year); //second
return View("StatsNew", resultOne.ToList());
I am able to display result in view using "resultOne". Now I want to pass another data to same view using ViewData[]. its a stored procedure and the result is paasing to "ViewData["more"]".
I have done following code for "ViewData["more"]" in View page
But I am getting an error saying that 'object reference not set'
#foreach (var item in ViewData["more"] as #List<demo.GetTeamStatsID_Result>)
{
#item.Pld;
}
Use a viewmodel and extend it by the data coming from stats.GetTeamStatsID(year);
Do not use ViewBag or ViewData if not necessary for some reason (which I canĀ“t imagine right now)
As the comments have already pointed out, build a ViewModel class, that wraps everthing you need:
//I am using fake types here, sinceI don't know your model classes
//substitute accordingly!
public class MyViewModel
{
public PlayerStatsType PlayerStats { get; set;}
public List<demo.GetTeamStatsID_Result> Teams { get; set;}
}
Then in your action method:
var vm = new MyViewModel();
vm.PlayerStats = stats.GetPlayerStatsByParam(year, modeOne); //first
vm.TeamId = stats.GetTeamStatsID(year); //second
return View("StatsNew", vm);
Amend the model declaration in your view:
#model Namespace.Models.MyViewModel //again use namespace for your vm class
Now you can access both properties from your model:
#foreach (var item in Model.Teams)
{
#item.Pld;
}

Creating pager for collection of anonymous type using PagedList.Mvc in ASP.NET MVC

I am developing an ASP.NET MVC project. In my project I need to implement pagination feature. So I use PagedList.Mvc for it. I installed it running this command,"install-package PagedList.Mvc" in package manager console. I know how to implement pagination using it. But I am having a problem with implementing for collection of anonymous type view.
This is my action method implementing pagination logic
public ActionResult IpList(int page = 1)
{
List<IpVisitInfo> ipVisitList = new List<IpVisitInfo>();
var rawVisits = trackTool.Visitors.GroupBy(x => x.IpAddress).Select(x => new { Visitors = x.Count(), IpAddress = x.Key, LastVisitedTime = x.OrderByDescending(c => c.Id).FirstOrDefault().VisitedAt }).ToPagedList(page,PageSize);
if(rawVisits!=null && rawVisits.Count()>0)
{
foreach(var visit in rawVisits)
{
IpVisitInfo info = new IpVisitInfo
{
IpAddress = visit.IpAddress,
LastVisitedAt = visit.LastVisitedTime,
TotalVisitors = visit.Visitors
};
ipVisitList.Add(info);
}
}
IpVisitListVM model = new IpVisitListVM
{
IpList = ipVisitList
};
return View(model);
}
As you can see I retrieved anonymous type collection and then loop through it and assign to strongly typed collection. I called to ToPagedList(PageNumber,PageSize) on anonymous type collection. But the problem is all the data required to implement pagination in view is in anonymous type collection. In view pagination has to be rendered something like this below.
#Html.PagedListPager(Model.Items, page => Url.Action("List", new { page }))
This is my view model
public class IpVisitListVM
{
public IEnumerable<IpVisitInfo> IpList { get; set; }
}
So according to what I am doing, IpList of my view model does not have required data to render pagination. If I retrieve all anonymous collection fist without using ToPagedList() on it, then loop through each record, performance will be very slow, because it is retrieving all records first from database. I will have so many records. How can I use PagedList on anonymous typed view and pass to strongly typed view to implement pagination?
In view model I am using this property
IEnumerable<IpVisitInfo> IpList
Instead of this
IPagedList<IpVisitInfo> IpList
because it does not have enough data to create pagination. Because I filtered in action.
But if I can pass following as list
trackTool.Visitors.GroupBy(x => x.IpAddress).Select(x => new { Visitors = x.Count(), IpAddress = x.Key, LastVisitedTime = x.OrderByDescending(c => c.Id).FirstOrDefault().VisitedAt }).ToPagedList(page,PageSize);
pagination is perfectly created and performance will be better. Is there a way to pass that list?

Weird razor behavior with List<dynamic>

This is my controller's code:
IQueryable<Foo> foos = dbContext.Foos.Where(...);
return View(foos);
And this razor code (cshtml) works well:
#model IQueryable<Foo>
#{
IQueryable<Foo> foos = Model;
var projected = foos.Select(e => new
{
fooId = e.FooId,
bar = new
{
barId = e.Foo.BarId
}
}).ToList();
}
#foreach (var x in projected)
{
<span>#x.fooId</span><br />
}
But this razor code (cshtml) doesn't work, being almost the same thing!:
#model IQueryable<Foo>
#{
IQueryable<Foo> foos = Model;
var projected = foos.Selected(Foo.Projection()).ToList()
}
#foreach (var x in projected)
{
<span>#x.fooId</span><br />
}
Foo.Projection() is a static method that I reuse a lot:
public static Expression<Func<Foo, dynamic>> Projection()
{
return e => new
{
fooId = e.FooId,
bar = new
{
barId = e.Foo.BarId
}
}
}
I'm getting that famous error: 'object' does not contain definition for 'fooId', which is discussed in here: MVC Razor dynamic model, 'object' does not contain definition for 'PropertyName' -but none of those answers helped me.
The accepted answer says: "now that MVC 3 has direct support for dynamic, the technique below is no longer necessary", so I also tried to return the projected List<dynamic> to the view ("ready to use, no projection needed") and it didn't work either (getting the same error). This is the code for that attempt:
Controller's code:
List<dynamic> foos = dbContext.Foos.Select(Foo.Projection()).ToList();
return View(foos);
View's code:
#model dynamic
etc.
Edit: With the debugger I'm able to check (just after the exception is thrown) that the item indeed has "a definition for..." (in the example code the item is x, but here is lot)
When you use dynamic you instruct the compiler to use reflection to call methods and access properties. In your case the objects that you access in this way are anonymous types and anonymous types are internal to the assembly they are created in.
The code generated for the Razor view is in a separate assembly and trying to reflect over an anonymous type created in the controller will fail. The debugger is not affected by this limitation so when the reflection fails and throws an exception you are still able to inspect the properties of the anonymous type in the debugger.
This also explains why your code works when you create the anonymous type in the Razor view. Then the code generated by your use of dynamic is able to reflect over the anonmyous type because it is declared in the same assembly.
Essentially, in MVC Razor you are not able to use anonymous types in a view when they are declared in the controller. Your use of dynamic was hiding this underlying problem by generating a run-time error that was hard to understand.
To fix your problem you can either create specific public types instead of using internal anonymous types or you can convert the anonymous type to an ExpandoObject in the controller.
I suppose that View() contructor just don't know what overload to use, since you have dynamic type. You can try to do this:
List<dynamic> foos = dbContext.Foos.Select(Foo.Projection()).ToList();
ViewData.Model = foos;
return View();
But why do you want to use strongly typed View if you don't use any of strongly typed ViewModel advantages, like type check and IntelliSense?
If you really want to pass dynamic type to your View since MVC 3 you can use ViewBag that already is dynamic.
In your Controller:
List<dynamic> foos = dbContext.Foos.Select(Foo.Projection()).ToList();
ViewBag = foos;
return View();
In your View:
#foreach (var x in ViewBag)
{
<span>#x.fooId</span><br />
}
inside the controller
List foos = dbContext.Foos.Select(Foo.Projection()).ToList();
return View(foos);
in side razor view
#model List<dynamic>

how to fill a GridPanel from a list Ext.net 3 razor engine?

I have returned a list of warning classes from the control class like this:
public ActionResult Ex3()
{
List<warning> warningsList = new List<warning>();
XElement xelem = XElement.Load(transformedFile);
var warnings = from elem in xelem.Descendants("warning")
select elem;
foreach (var v in warnings)
{
warning warn = new warning();
warn.id = v.Attribute("id").Value;
warningsList.Add(warn);
}
return View(warningsList);
}
i have created a view with the option "create a strongly typed view" and i have choose the warning class as a model (razor engine).
Now, in Ex3View.cshtml, i want to create and fill a gridpannel with the values in the warningsList. How can i do that ? how can i access these data in the returned list?
Please note that the view file is .cshtml and not .aspx.
You can put a Model to a Store's DataSource:
#Html.X().Store().DataSource(Model)

passing record to a view using entity framework

I am using .NET 4.0, MVC3 and entity framework 4.0
I need to pass a database record of type "statement" to a view. The code to fetch the record is as below:
public ActionResult pdfStatement(string InvoiceNumber)
{
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", InvoiceNumber);
return View(_db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters));
}
where "uspInvoiceStatement" is my stored procedure used to fetch "statement"
I have created a strongly typed view which receives the "statement"
< % # Page Language="C#" Inherits="System.Web.Mvc.ViewPage < InvoiceSearch.Models.Statement >" %>
When i run the application i get an exception saying
"The model item passed into the dictionary is of type 'System.Data.Objects.ObjectResult`1[InvoiceSearch.Models.Statement]',
but this dictionary requires a model item of type 'InvoiceSearch.Models.Statement'.
Help me to escape from this trouble.
"
The error is surprisingly helpful - it tells you, you are sending ObjectResult<Statement> to the view asi viewmodel, while you should send Statement.
Your _db.ExecuteFunction always returns ObjectResult<Statement>, so you need to take Statement from it, like this :
var statementResult = _db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters);
var statement = statementResult.SingleOrDefault();
if (statement != null)
{
return View(statement); // isnt that nicer when you can see what your viewmodel is?
}
else
{
return View("NotFound");
}

Resources