Asp Net MVC with VM is this good practice? (Code Sample) [closed] - asp.net-mvc

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I'm wondering if what I have here is good practice. Using Entity Framework Code First with MVC and View Models.
Below i have code for List, Create, Edit and Delete. Model name Page and 2 ViewModels name ContentViewModel and ContentListViewModel.
private readonly IUserService _userService;
//private readonly MembershipProvider _members;
public ContentController()
{
// _members = Membership.Provider;
_userService = new AspNetMembershipProviderWrapper();
}
//
// GET: /Profile/Content/
public ActionResult Index()
{
using (var db = new BlogContext())
{
IEnumerable<Page> pages;
pages = db.ArticlePages.ToList();
List<ContentListViewModel> model = new List<ContentListViewModel>();
foreach (Page pg in pages)
{
MembershipUser user = _userService.Get(pg.authorId);
model.Add(new ContentListViewModel()
{
PageID = pg.pageID,
UserName = user.UserName,
UserID = (Guid)user.ProviderUserKey,
IsFrontPage = pg.frontpage,
isPublished = pg.published,
PageTitle = pg.titleHeading,
PageUrlName = pg.idName,
PublishedDate = pg.datentime
});
}
return View(model);
}
}
//
// GET: /Profile/Content/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Profile/Content/Create
[HttpPost]
public ActionResult Create(ContentViewModel page)
{
if (ModelState.IsValid)
{
using (var db = new BlogContext())
{
db.ArticlePages.Add(new Page()
{
authorId = (Guid)Membership.GetUser().ProviderUserKey,
datentime = DateTime.Now,
frontpage = page.FrontPage,
published = page.Published,
titleHeading = page.TitleHeading,
pageContent = page.Content,
idName = page.IdName
});
db.SaveChanges();
return RedirectToAction("Index").Success("Page added Successfully.");
}
}
return View(page);
}
//
// GET: /Profile/Content/Edit/5
public ActionResult Edit(int id = 0)
{
if (id == 0)
{
return RedirectToAction("Index").Error("Page Not found.");
}
Page pg = new Page();
using (var db = new BlogContext())
{
pg = (from m in db.ArticlePages where m.pageID == id select m).SingleOrDefault();
}
if (pg == null)
{
return RedirectToAction("Index").Error("Page Not found..");
}
return View(new ContentViewModel()
{
id = pg.pageID,
Content = pg.pageContent,
FrontPage = pg.frontpage,
Published = pg.published,
TitleHeading = pg.titleHeading,
IdName = pg.idName
});
}
// POST: /Profile/Content/Edit/5
[HttpPost]
public ActionResult Edit(ContentViewModel page)
{
if (ModelState.IsValid)
{
using (var db = new BlogContext())
{
var oPage = db.ArticlePages.Single(p => p.pageID == page.id);
oPage.frontpage = page.FrontPage;
oPage.idName = page.IdName;
oPage.pageContent = page.Content;
oPage.published = page.Published;
oPage.titleHeading = page.TitleHeading;
db.SaveChanges();
return RedirectToAction("Index").Success("Page updated");
}
}
return View(page);
}
//
// POST: /Profile/Content/Delete/5
[HttpPost]
public ActionResult Delete(int id)
{
using (var db = new BlogContext())
{
Page page = db.ArticlePages.Find(id);
db.ArticlePages.Remove(page);
db.SaveChanges();
return RedirectToAction("Index").Success("Page deleted");
}
}
I would like to know there are better ways of doing this?

While that method is certainly better than passing the entities directly to the view, I would suggest a number of improvements.
First, I would create a new business layer, and then create a facade class to retrieve your data. Then move all database access to this layer. Thus, you would end up with a call to the service layer rather than doing direct database access like you are doing above.
Second, you should look into using something like AutoMapper to map between your data entities and your view models.

Related

ASP.NET MVC - Check for duplicate in country name without using Model Annotation

I have a ViewModel and Repository that are being used by the Controller Action for Create
Repository
BackendEntities entity = new BackendEntities();
public void AddCountry(CountriesViewModel countryModel)
{
COUNTRIES2 newCountry = new COUNTRIES2()
{
COUNTRY_ID = countryModel.COUNTRY_ID,
COUNTRY_CODE = countryModel.COUNTRY_CODE,
COUNTRY_NAME = countryModel.COUNTRY_NAME,
ACTION_STATUS = countryModel.ACTION_STATUS,
CREATED_BY = countryModel.CREATED_BY,
CREATED_DATE = countryModel.CREATED_DATE
};
entity.COUNTRIES.Add(newCountry);
entity.SaveChanges();
}
Then, I call the Repository from the Controller Action for Create.
Controller
public ActionResult Create(FormCollection collection, CountriesViewModel countries)
{
CountriesRepository countryRepo = new CountriesRepository();
if (ModelState.IsValid)
{
try
{
// TODO: Add update logic here
countryRepo.AddCountry(countries);
//countryRepo.
var notif = new UINotificationViewModel()
{
notif_message = "Record saved successfully",
notif_type = NotificationType.SUCCESS,
};
TempData["notif"] = notif;
return RedirectToAction("Index");
}
catch (Exception e)
{
this.AddNotification("Country cannot be added.<br/> Kindly verify the data.", NotificationType.ERROR);
}
}
return View(countries);
}
Please how do I Validate for duplicate COUNTRY_NAME using the condition, where ACTION_STATUS is not equal to 2
I don't want to do it from Model or View, but in the Controller or Repository.
Probably putting it before countryRepo.AddCountry(countries) in the Controller.
Create a Method In your Country Repository
public bool IsNameExist(string name, int id)
{
var result =entity.COUNTRIES.Any(c => c.COUNTRY_NAME == name && c.ACTION_STATUS != 2 && && c.COUNTRY_ID != id);
return result;
}
Then In your controller
public ActionResult Create(FormCollection collection, CountriesViewModel countries)
{
.......
if (countryRepo.IsNameExist(countries.COUNTRY_NAME, countries.COUNTRY_ID))
{
ModelState.AddModelError("COUNTRY_NAME", "COUNTRY NAME already exist.");
}
........
}

How to redirect view in separate target window

I am using visual studio 2012 and mvc4.
I want to open CertDet view in separate window
In index view I have used submit with post method.
My Controller -
[HttpPost]
[AllowAnonymous]
public ActionResult Index(ModelCertificate cert)
{
if (ModelState.IsValid)
{
dbRTCEntities objCon = new dbRTCEntities();
Mst_Catref data = objCon.Mst_Catref.Where(x => x.Catref == cert.Catref).FirstOrDefault();
if (data != null)
{
return RedirectToAction("CertDet", new { catref = data.Catref, Excise = cert.ExciseNumber, customerNm = cert.CustomerName });
}
else
ModelState.AddModelError("", "Catref not found");
}
return View();
}
public ActionResult CertDet(string catref, string Excise, string customerNm)
{
dbRTCEntities objCon = new dbRTCEntities();
Mst_Catref data = objCon.Mst_Catref.Where(x => x.Catref == catref).FirstOrDefault();
ModelCertificate cert = new ModelCertificate();
return View(cert);
}
You should need something like this on your View:
#Html.ActionLink("CertDet", "CertDet", new { catref = data.Catref, Excise = cert.ExciseNumber, customerNm = cert.CustomerName }, new {target = "_blank"})
This will render something like this in a new window;
CertDet

The neat and simple way for multiple models in a view

What can be simplest way for having rendered in a view information from multiple models. I use ViewModel in some scenarios (in particular when models are not related directly), but now I want to made a kind of dashboard for the current user. So apart from AspNetUsers model I have for example several models (e.g. Orders, OperationJournal, Jobs etc.) that in terms of entity have each a foreign key on UserID.
I made a ViewModel such:
namespace JobShop.Models
{
class QuickProfileVM
{
public IEnumerable<Jobs> Jobs { get; set; }
public IEnumerable<AspNetUsers> AspNetUsers { get; set; }
public IEnumerable<CreditJournal> CreditJournal { get; set; }
public IEnumerable<CandidateReview> CandidateReview { get; set; }
}
}
(since the base models that I need, are done by EF they have all about relations between entities) but it seems to me that is not enough. I am not able to view both the current user profile (so one record) and it's details (more than one record and more than one model).
I have try with Partial View, both with own controller or with actions in Dashboard View controller.
As an example an ActionResult that now I play with:
public ActionResult QuickProfile()
{
var QuickProfile = new QuickProfileVM();
var AspNetUsers = new AspNetUsers();
if (User.Identity.IsAuthenticated)
{
var CurrentUser = User.Identity.GetUserId();//UserManager.FindById(User.Identity.GetUserId());
var TheUser = db.AspNetUsers.Where(u => u.Id == CurrentUser)
.Select(u => new
{
ID = u.Id,
Email = u.Email,
PhoneNumber = u.PhoneNumber,
Companyname = u.Companyname,
Address = u.Address,
ZIP = u.ZIP,
City = u.City,
Country = u.Country,
Website = u.Website,
Facebook = u.Facebook,
Twitter = u.Twitter,
GooglePlus = u.GooglePlus,
Dribble = u.Dribble,
BirthDate = u.BirthDate,
Username = u.UserName,
Surrname = u.Surname,
Name = u.Name,
Role = u.Role,
ThumbURL = u.ThumbURL,
CreditBalance = u.CreditBalance
}).Single();
var TheJournal = db.CreditJournal.Where(tj => tj.UseBy == CurrentUser)
.Select(tj => new
{
IdJournal = tj.IdJournal,
Operation = tj.Operation,
CvID = tj.CvID,
JobID = tj.JobID,
CreditConsumed = tj.CreditConsumed,
UseDate = tj.UseDate,
UseBy = tj.UseBy
}).ToList();
//similar for Jobs and CandidateReview
//
var UserId = TheUser.ID;
var username = TheUser.Username;
var role = TheUser.Role;
var InitialCredit = TheUser.CreditBalance;
AspNetUsers.UserName = TheUser.Username;
AspNetUsers.Companyname = TheUser.Companyname;
AspNetUsers.Surname = TheUser.Surrname;
AspNetUsers.Name = TheUser.Name;
AspNetUsers.ThumbURL = TheUser.ThumbURL;
AspNetUsers.CreditBalance = InitialCredit;
//I put this to ilustrates what I have accesible for example
//about CreditJournal: only methods, not properties
QuickProfile.CreditJournal.AsEnumerable();
var id = CurrentUser;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
AspNetUsers aspNetUsers = db.AspNetUsers.Find(id);
if (aspNetUsers == null)
{
return HttpNotFound();
}
}
return View(AspNetUsers);
//Disbled since at this stage is not usefull
//return View(QuickProfile);
//return View();
}
I suggest you consider using Html.RenderAction in your view. For example, say your main dashboard is this:
#{
ViewBag.Title = "title";
}
<h2>Multiple Models</h2>
#{ Html.RenderAction("GetData", "Foo"); }
You can use Html.RenderAction to call FooController.GetData()
public class FooController : Controller
{
public ActionResult GetData()
{
var viewModel = new FooViewModel();
viewModel.TimeStamp = DateTime.UtcNow;
return View(viewModel);
}
}
So rather than having one viewmodel with lots of other viewmodels attached as properties, you can split up the rendering of the dashboard view.
Overall this should makes things easier for you - I've used this approach in the past and have found it reduces complexity.

Passing viewmodel from one method to another (post)

I have a wizardtype form where i would like to keep the model from the first step to the last.
This is how im doing it now, but i cant seem to keep the model from step 3 to 4.
[HttpPost]
public ActionResult Register(MemberViewModel model)
{
var mobile = model.Mobile;
var xml = MemberclubHelper.CreateVerificationTokenXML(mobile, "47");
MemberclubHelper.SendVerificationToken(xml);
return RedirectToAction("RegisterStep1", new { mobile = mobile });
}
public ActionResult RegisterStep1(string mobile)
{
var model = new MemberViewModel();
model.Mobile = mobile;
return View("Register/Step1", model);
}
[HttpPost]
public ActionResult RegisterStep1(MemberViewModel model)
{
var interests = (from interest in model.Interests where interest.isChecked select interest.value).ToList();
var passing = (from pass in model.Passing where pass.isChecked select pass.value).ToList();
var xml = MemberclubHelper.CreateMemberProfileXML(model.Mobile, model.FirstName, model.FirstName,
model.Email1, model.Zipcode, model.SelectedKid1, model.SelectedKid2, model.SelectedKid3,
model.Gender.ToString(), interests, passing);
model.XmlToSend = xml;
if (xml.Contains("error"))
{
model.ErrorMessage = xml;
return View("Register/Step1", model);
}
return View("Register/Step2", model);
}
[HttpPost]
public ActionResult RegisterStep2(MemberViewModel model)
{
var result = MemberclubHelper.SendCreateUser(model.XmlToSend, model.Password);
if (result.Contains("error"))
{
model.ErrorMessage = result;
return View("Register/Step2", model);
}
else
{
return View("Register/Finished");
}
}
I think you may be better served by creating a separate view model for each step (i.e., MemberViewModelStep1). To me this seems like only some of your MemberViewModel properties will will be set in each step unless you carry a lot of that state between steps via hidden inputs or some other mechanism.
Alternatively, have you considered using JavaScript to build up that state between across your steps and then submitting a single post with your fully populated MemberViewModel?

two models in a view - not working for me

I have created an entity data model from my database. however in certain areas of the application i need to pass two models. thus i create a third model which has as properties the objects of each required model.
In the scenario, i want to use one model just to show some data to the user and the other is to be populated by the user using form elements. therefore, i create a constructor in my custom model to populate it. here's the code:
THE CUSTOM MODEL
public class ordersModel
{
public ordersModel(order or)
{
this.prods = new order_products();
this.new_order = new order();
this.new_order.customer_id = or.customer_id;
this.new_order.my_id = or.my_id;
this.new_order.my_order_id = or.my_order_id;
this.new_order.order_date = or.order_date;
this.new_order.order_status_id = or.order_status_id;
}
public order new_order { get; set; }
public order_products prods { get; set; }
}
IT IS USED IN THE CONTROLLER AS FOLLOWS:
public ActionResult Create()
{
order or = new order();
// Store logged-in user's company id in Session
//or.my_id = Session["my_id"].ToString();
//do something to allow user to select customer, maybe use ajax
or.customer_id = "123";
or.order_amount = 0;
or.my_id = "74973f59-1f6c-4f4c-b013-809fa607cad5";
// display date picker to select date
or.order_date = DateTime.Now.Date;
// fetch statuses from database and show in select list box
or.order_status_id = 1;
return View(or);
}
//
// POST: /Orders/Create
[HttpPost]
public ActionResult Create(order or)
{
using (invoicrEntities db = new invoicrEntities())
{
var temp = db.last_order_number.SingleOrDefault(p => p.my_id == or.my_id);
if (temp != null)
{
or.my_order_id = temp.my_order_id + 1;
if (ModelState.IsValid)
{
ordersModel ord = new ordersModel(or);
db.orders.AddObject(or);
temp.my_order_id = temp.my_order_id + 1;
//TempData["my_order_id"] = or.my_order_id;
db.SaveChanges();
return RedirectToAction("AddProducts", ord);
//return RedirectToAction("AddProducts", new { id = or.my_order_id });
}
return View(or);
}
return RedirectToAction("someErrorPageDueToCreateOrder");
}
}
public ActionResult AddProducts()
{
using (invoicrEntities db = new invoicrEntities())
{
//string my_id = TempData["my_id"].ToString();
//string my_order_id = TempData["my_order_id"].ToString();
string my_id = "74973f59-1f6c-4f4c-b013-809fa607cad5";
int my_order_id = 1;
//Int64 my_order_id = Convert.ToInt64(RouteData.Values["order_id"]);
// Display this list in the view
var prods = db.order_products.Where(p => p.my_id == my_id).Where(p => p.my_order_id == my_order_id).ToList();
var or = db.orders.Where(p => p.my_id == my_id).Where(p => p.my_order_id == my_order_id).ToList();
if (or.Count == 1)
{
//ViewData["name"] = "sameer";
ViewData["products_in_list"] = prods;
ViewData["order"] = or[0];
return View();
}
return RedirectToAction("someErrorPageDueToAddProducts");
}
}
[HttpPost]
public ActionResult AddProducts(order_products prod)
{
prod.my_id = "74973f59-1f6c-4f4c-b013-809fa607cad5";
// find a way to get the my_order_id
prod.my_order_id = 1;
return View();
}
THIS ALL WORKS OUT WELL, UNTIL IN THE "ADDPRODUCTS" VIEW:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<invoicr.Models.ordersModel>" %>
AddProducts
<h2>AddProducts</h2>
<%: Model.new_order.my_id %>
the above statement gives an error
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
what am i doing wrong here?
You seem to be passing the wrong model when redisplaying your Create view.
Try passing the ord instance which is of type ordersModel and which is what your view is strongly typed to:
public ActionResult Create(order or)
{
using (invoicrEntities db = new invoicrEntities())
{
var temp = db.last_order_number.SingleOrDefault(p => p.my_id == or.my_id);
if (temp != null)
{
or.my_order_id = temp.my_order_id + 1;
ordersModel ord = new ordersModel(or);
if (ModelState.IsValid)
{
db.orders.AddObject(or);
temp.my_order_id = temp.my_order_id + 1;
db.SaveChanges();
return RedirectToAction("AddProducts", ord);
}
return View(ord);
}
return RedirectToAction("someErrorPageDueToCreateOrder");
}
}
UPDATE:
Now that you have shown your AddProducts action you are not passing any model to the view although your view expects an ordersModel instance. So don't just return View();. You need to pass an instance of ordersModel:
if (or.Count == 1)
{
ViewData["products_in_list"] = prods;
ViewData["order"] = or[0];
ordersModel ord = new ordersModel(or[0]);
return View(ord);
}

Resources