Retrieving records from another SQL Server database - Umbraco - c#

I'm working on a website, where I need to retrieve pricelists, from another database on the same SQL Server as my Umbraco database.
It's a requirement that it has to be in a separate database.
I have made a new connection string Pricelist and used EF database-first.
PriceList repository:
namespace UmbracoCMS.Repository{
using System;
using System.Collections.Generic;
public partial class Prisliste
{
public string Kode { get; set; }
public string Speciale { get; set; }
public string Ydelsesgruppe { get; set; }
public string Gruppe { get; set; }
public string Ydelse { get; set; }
public string Ydelsestekst { get; set; }
public string Anaestesi { get; set; }
public string Indlæggelse { get; set; }
public Nullable<double> Listepris { get; set; }
public Nullable<int> WebSort { get; set; }
public string YdelsesTekstDK { get; set; }
public string Frapris { get; set; }
public Nullable<int> Sortering { get; set; }
}
}
PriceListController class:
using System;
using System.Linq;
using System.Web.Mvc;
using UmbracoCMS.Repository;
namespace UmbracoCMS.Controllers{
public class PriceListController : Umbraco.Web.Mvc.SurfaceController {
[HttpGet]
public PartialViewResult GetPriceList(string contentTitle){
var db = new PricelistContext();
var query = from b in db.Prislistes orderby b.Speciale select b;
Console.WriteLine("records in the database:");
foreach (var item in query)
{
Console.WriteLine(item.Speciale);
}
return PartialView("~/views/partials/PriceList.cshtml");
}
}
}
What I want is to load the prices for a treatment, based on a property on the document type. I'm just not sure how do this in umbraco since I'm fairly new a umbraco.
So when a treatment page is requested, I need to take the property ContentTitle value. Use it to retrieve all records with the same Speciale and display them in a list/table.
With a query
.where(b.Speciale = contentTitle)
It would be great if someone could help a little, or lead me in the right direction.
Also is it possible to do it in the same http request? Or should I use partial view or macros to both get the properties of the document type, from the umbraco database, and the records from the pricelist database at the same time when a user go to the treatment page?
Or is there a better way to do this?
Update:
Thanks a lot, for the great answer Ryios.
I got a question more.
using System;
using System.Linq;
using System.Web.Mvc;
namespace UmbracoCMS.Controllers
{
public class PriceListSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public ActionResult GetPriceList(string contentTitle)
{
PricelistContext.RunInContext(db =>
{
var result = db.Prislistes.OrderBy(p => p.Speciale);
});
return View(result);
}
}
}
I got it working, so it call the method and the data from the Pricelist Database is shown in:
var result = db.Prislistes.OrderBy(p => p.Speciale);
Now I just need to get the list of prices out to the view again, so I can show a list or table of the prices.
Do you have a suggestion on how I can this in Umbraco. Normally I would return a ViewModel in MVC like:
return View(new ListViewModel(result));
and use it in the view like:
#model Project.ViewModels.ListViewModel
So I can loop through it.
But I want to still have the properties from the the "Home"/"TreatmentPage" Document type.
Should I do it with a partialView or is there a better way?
Solved
I thought I wanted to share it, if anyone else is in a similar situaction.
Controller:
namespace UmbracoCMS.Controllers
{
public class PriceListSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public PartialViewResult PriceList(string contentTitle)
{
List<Prisliste> result = null;
PricelistContext.RunInContext(db =>
{
result = db.Prislistes.Where(p => p.Speciale == contentTitle)
.OrderBy(p => p.Speciale).ToList();
});
var model = result.Select( pl => new PrislistVm()
{
Speciale = pl.Speciale,
Listepris= pl.Listepris
});
return PartialView(model);
}
}
}
ViewModel:
namespace UmbracoCMS.ViewModels
{
public class PrislistVm
{
public PrislistVm()
{
Results = new List<Prisliste>();
}
public List<Prisliste> Results { get; set; }
public string Speciale { get; set; }
public double listepris { get; set; }
}
}
View/PriceListSurface:
#model IEnumerable<UmbracoCMS.ViewModels.PrislistVm>
#{
ViewBag.Title = "PriceList";
}
<h2>PriceList</h2>
#foreach (var item in Model)
{
#item.Speciale
#item.Listepris
}

Your going to have a memory leak if you load your EF context like that. I recommend creating a method to wrap it for you with a llambda callback. Put it in your context class.
public static void RunInContext(Action<PricelistContext> contextCallBack)
{
PricelistContext dbContext = null;
try
{
dbContext = new PricelistContext();
contextCallBack(dbContext);
}
finally
{
dbContext.Dispose();
dbContext = null;
}
}
//Example Call
PricelistContext.RunInContext(db => {
var result = db.PrisListes.OrderBy(p => p.Speciale);
//loop through your items
});
To get the Value of the DocumentType, it depends on the calling context. Assuming you are using a Razor Template that is attached to the document type, that is associated with a Content Page.
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
Layout = "ContentPageLayout.cshtml";
}
#* Call GetPriceList on PriceListController with Parameter contentTitle *#
#Html.Action("GetPriceList", "PriceListSurface", new { contentTitle = Model.Content.GetPropertyValue<string>("ContentTitle") });
In the above example, I have created a document type with a property called ContentTitle that is associated with a view called ContentPage. Then I created content in the backoffice Content section called "Home" that uses the document type. Giving me a url like
http://localhost/home
Also, your SurfaceController will not work. Umbraco's logic for mapping the routes for surface controllers has some requirements for your surface controller's naming conventions. You have to end the name of the class with "SurfaceController" and then it get's called PriceListSurfaceController, then it maps the controller with a name of "PriceListSurface".
Here's the documentation for the SurfaceController features.
http://our.umbraco.org/documentation/Reference/Mvc/surface-controllers
Using a surface controller is the right logic. It's not good practice to have your Data Layer code calls in the UmbracoTemplatePage. 1, because RazorTemplates are interpreted/compiled and SurfaceController's are JIT compiled int the dll, so SurfaceController code is WAY faster. 2 Because you can make asynchronous Controller calls in MVC Razor. If it was all in the view it would make it really difficult to convert everything to be asynchronous. It's best to keep server side logic in a controller.
Optionally, you can Hijack an Umbraco route and replace it with a custom controller that doesn't have to inherit from SurfaceController, which makes it possibly to surface content to the browser that is or isn't part of umbraco.
http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers
You can also create a new section in the backoffice to manage your Price List "the ui framework for building one is written against AngularJS"
http://www.enkelmedia.se/blogg/2013/11/22/creating-custom-sections-in-umbraco-7-part-1.aspx

Related

How to write code for Server side pagination in ASP.NET Core

I want to implement Server side pagination for loading of some data I want to be loaded into browser. It's working fine Client side with PageList in MVC but I don't know how to do in Asp.net Core Server side.
This is my Class There I want to show all proporties , even photo (image)
public class HouseDTO
{
[Key]
public int HouseId { get; set; }
public Nullable<decimal> Price { get; set; }
public string LiveArea { get; set; }
public string RoomAmount { get; set; }
public string HouseType { get; set; }
public string ImageName { get; set; }
}
And then my Repisitory
public interface IHouseRepository
{
public IEnumerable<HouseDTO> GetAllHouses()
}
public class HouseRepository : IHouseRepository
{
private ApplicationDbContext db;
public HouseRepository(ApplicationDbContext db)
{
this.db = db;
}
public IEnumerable<HouseDTO> GetAllHouses()
{
return db.Houses;
}
}
And this is my Controller
public class AdvController : Controller
{
private IHouseRepository db;
private IHostingEnvironment hostingEnvirnment;
public AdvController(IHouseRepository db, IHostingEnvironment hostingEnvirnment)
{
this.db = db;
this.hostingEnvirnment = hostingEnvirnment;
}
public IActionResult Index()
{
var model = db.GetAllHouses(); // How can I do this to Server side pagination?
return View(model);
}
}
So How can create Server side Pagination for this action?
public IActionResult Index()
{
var model = db.GetAllHouses();
return View(model);
}
I would greatly appreciate it if you help me.
You can use Skip() and Take(). Make a repository method that will take current position (to skip) and give parameter to Take. Something like:
public House GetPaged(currentPosition)
{
return db.Houses.Skip(currentPosition).Take(20);
}
Take() and Skip() over results of db.Houses is the way to go.
Like this:
// Skip (n) pages and take (x) elements from required page.
return db.Houses.Skip(page*countPerPage).Take(countPerPage);
// As suggested in comments for the answer above, specified code should belong to
// repository method. Initially implemented as a template to be copypasted
// and reused according to your needs.
make sure that page numbering in query is 0-based:
page = 0 if page not specified; page = 0 if you require page #1; page = 1 if you need page #2 etc. And countPerPage meaning is obvious :)
I might be a bit late for the party but I wrote a lightweight package to address this issue by giving you the toolkit to build your DB queries using Skip() and Take() as the other answers suggested.
This might be helpful for someone googling around: https://github.com/YannikG/dotnet-pageable-data

MVC - how to send to the partial view ViewModel containing a list

I am the beginner in MVC and I have a web application, where in my controller I declare a list of objects (feedback from visitors) and then send it to the view, which displays it. It looks like this. Declaration:
public class TrekFeedbackItem
{
public string trekid { get; set; }
public string comment { get; set; }
public string author { get; set; }
public TrekFeedbackItem(string trekid, string comment, string author)
{ this.trekid = trekid;
this.comment = comment;
this.author = author;
}
}
And usage:
List<TrekFeedbackItem> feedbackList = new List<TrekFeedbackItem>
{
//constructor called, data entered into the list
}
return View(trekname, feedbackList);
However, now I need to pass also another list, lets call it relatedblogsList. As a first step, I decided to encapsulate my feedbackList into the ViewModel (and once it works, add another list of different objects.)
public class TrekViewModel
{
public List<TrekFeedbackItem> feedback { get; set; }
}
and fill the data like this:
TrekViewModel trek = new TrekViewModel();
trek.feedback = new List<TrekFeedbackItem>
{
//insert data here
};
return View(view, trek);
The problem is - how to send this model to the partial view and how to access it?
Thank a lot
You can pass data into the partial view like below
from the controller return this view:
return PartialView("_partial_viewname", trek);
then in the beginning of the partial view:
#model Models.TrekViewModel
after that you can use Model.feedback inside the partial view.
Set return type of your action controller to "PartialView" rather than "View".
return PartialView("_yourPartialViewName", yourObject);
In case, if application does not work as expected, build it and re-run it.

View expecting IEnumerable

Well im kinda new in Asp.net Mvc and im learning alone from scratch, i have a aplicattion that controls expends and earnings and what i am trying to do now is, basing on a list of earnings and expends give me the balance from a user, im having a lot of problems trying to control this and i dont know if i am doing it the right way
Here is my model:
public class Balance
{
public int BalanceId { get; set; }
public List<Expense> Despesas { get; set; }
public List<Earning> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
Soo what i did was, first trying to control when the user inserts a Earning or a row like, verifying if the User already exists on the database in the control method Create on the expenses and in the earning, if it doesnt exist he add the aplicationUserId and the expensive or the earning.
I want that the balance appears in every page, soo i added this to my Layout.cshtml
<li>#Html.Action("GetBalance", "Home")</li>
it calls the controller GetBalance:
public PartialViewResult GetBalance()
{
var userId = User.Identity.GetUserId();
var balance = db.Balance.Where(d => d.ApplicationUserId == userId);
return PartialView("_GetBalance",balance);
}
Send to the view _GetBalance the balance model:
#model <MSDiary.Models.Balance>
<p>Saldo: #GetBalance()</p>
#functions
{
HtmlString GetBalance()
{
decimal saldo = 0;
if (Model.Expense.Count != 0 || Model.Earning.Count != 0)
{
foreach (var item in Model.Despesas)
{
balance += item.EarningValue;
}
foreach (var item in Model.Rendimentos)
{
balance -= item.ExpenseValor;
}
}
return new HtmlString(balance.ToString());
}
}
What i want to know is, if there is a easyer way to do this, or what i can do to do what i want, i cant get it why my view expects something different can someone explain me what i am doing wrong?
Ps: Sorry for the long post and English, but i want to learn more :)
Firstly, the model #model <MSDiary.Models.Balance> needs to be changed to:
#model IEnumerable<MSDiary.Models.Balance>
Also, the method GetBalance should ideally be placed in a class not in GetBalance partial view. You could achieve this two ways, either through extension methods or have a Balance View Model that has the calculated balance as a property which is then passed down to your view.
As an example via an extension method:
public static class BalanceExtensions
{
public static string GetBalance(this Balance balance)
{
string displayBalance = "0:00";
// Your logic here
return displayBalance;
}
}
And then in your Partial View you can use the new HTML Helper:
#Html.GetBalance();
As an additional note I would change List to IEnumerable for expenses and earnings as it appears you are only exposing the data and not manipulating the data.
Your model would then look like:
public class Balance
{
public int BalanceId { get; set; }
public IEnumerable<Expense> Despesas { get; set; }
public IEnumerable<Earning> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
#Filipe Costa A few things here.
You should probably name your view the same thing as your method. The underscore preceding the name is fairly standard so I would suggest using that same name for the method. If the name of the method and view are the same you can simply pass in the model and not have to do the name + model signature of PartialView method. It's simpler.
Aside from that your code is fine but your .cshtml partial view should have this for the first line. That will accept the list you're passing.
#model IEnumerable<MSDiary.Models.Balance>
<h1>#Model.BalanceId</h1>
#*Do other stuff!*#

The operation cannot be completed because the DbContext has been disposed using MVC 4

I know that this Question is asked so many times. I have read and implemented all solution but didn't get success. I am getting this error when I retrieve data from database using EF and binds with model after that use this model on View.
My controller code is
using System.Linq;
using System.Web.Mvc;
using JsonRenderingMvcApplication.Models;
namespace JsonRenderingMvcApplication.Controllers
{
public class PublisherController : Controller
{
public ActionResult Index()
{
PublisherModel model = new PublisherModel();
using (DAL.DevelopmentEntities context = new DAL.DevelopmentEntities())
{
model.PublisherList = context.Publishers.Select(x =>
new SelectListItem()
{
Text = x.Name,
Value = x.Id.ToString()
}); ;
}
return View(model);
}
}
}
My View code is
#model JsonRenderingMvcApplication.Models.PublisherModel
#{
ViewBag.Title = "Index";
}
<div>
#Html.DisplayFor(model=>model.Id)
#Html.DropDownListFor(model => model.Id, Model.PublisherList);
</div>
<div id="booksDiv">
</div>
My model code is
using System.Collections.Generic;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
namespace JsonRenderingMvcApplication.Models
{
public class PublisherModel
{
public PublisherModel()
{
PublisherList = new List<SelectListItem>();
}
[Display(Name="Publisher")]
public int Id { get; set; }
public IEnumerable<SelectListItem> PublisherList { get; set; }
}
}
My entity code is
namespace JsonRenderingMvcApplication.DAL
{
using System;
using System.Collections.Generic;
public partial class Publisher
{
public Publisher()
{
this.BOOKs = new HashSet<BOOK>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Year { get; set; }
public virtual ICollection<BOOK> BOOKs { get; set; }
}
}
Yes this entity has a navigation property but I don't want to that entity data so I don't want to include that.
Thanks
The problem you're experiencing is due to LINQ's deferred execution. It's quite the gotcha for developers who haven't yet realized how LINQ works under the hood. I have a great blog post about it, but the core concept is that you must force an enumeration on the collection to cause the LINQ code to run immediately instead of later. This means changing this:
model.PublisherList = context.Publishers.Select(x =>
new SelectListItem()
{
Text = x.Name,
Value = x.Id.ToString()
});
to this:
model.PublisherList = context.Publishers.Select(x =>
new SelectListItem()
{
Text = x.Name,
Value = x.Id.ToString()
}).ToList();
Note the .ToList() there which forces the enumeration.
Your LINQ query is deferred meaning that it is not being run at your controller but instead afterwards, probably in your view where you loop over the collection (which forces the enumeration and thus runs the LINQ). Because you're using the using statement to dispose of your DB context (which is of course good practice), the context is disposed of before you reach the view, which executes the code against the disposed context. Forcing the enumeration within the using statement will run the code at that time, instead of later when the context is disposed, and prevent this issue.

Manually setting path to a cshtml file and ignoring the MVC Logic

Let's say i have 2 files located in the same folder.
/Test/View.cshtml
<h1>File that needs to be loaded in to a string</h1>
/Test/Content.cs
public class Content {
public string GetView()
{
Return View("/Test/View.cshtml",someModel)
}
}
It should not cair about the RouteData from Web.Config
The point of doing this is, so that i am able to retrieve the GetView and use it elsewhere.
I know this question and approch is wierd but i am in a uniq situation developeing a CMS system, so i really need something like this.
How could i achieve this :)?
Update: Explanation
_Layout.cshtml
This file has no RenderBody as it normally has. Instead it has different Areas like this one.
#{
Render r = new Render("Content");
}
#r.Print()
Each area are printing out different modules, fx: a newsletter or a gallery. And for that to be possible this is done:
public interface IModule
{
string Name { get; set; }
int Id { get; set; }
string View();
}
public class ModuleList
{
public List<IModule> Modules = new List<IModule>();
public ModuleList()
{
Modules.Add(new ContentView() { Name = "Content" });
Modules.Add(new GalleryView() { Name = "Gallery" });
Modules.Add(new NewsletterView() { Name = "Newsletter" });
}
}
And here is is the ContentView Class (One of many Modules)
public class ContentView: IModule
{
public string Name { get; set; }
public int Id { get; set; }
DbModulesDataContext db = new DbModulesDataContext();
public string View()
{
var q = (from c in db.mContents
where c.Id == Id
select c).FirstOrDefault();
return ("<h1>"+q.Html+"</h1>");
}
}
As you can see right now the html is inline with the c# but i want it the other way around. (i want the View() to work together with a cshtml file)
Does it make a little more sence now?
I finaly found my solution!
It's right there!
http://razorengine.codeplex.com/
Thank you for trying though :)

Categories