Using ViewModel to fetch data from multiple tables? - c#

I have 3 classes for rules,requests,executions, and approvers.
I want to combine these three tables into a viewModel so that I can fetch Requestid, description,approvers and execution status in one single view.
Here is my first crude try which seems to work but I feel this is very incorrect way of creating a ViewModel. Please suggest a better approach.
public class Rules
{
[Required]
public virtual int RulesId { get; set; }
[Required]
public virtual string RulesDescription { get; set; }
[Required]
public virtual int ApprovalLevels { get; set; } //if 0 then auto approved.
[Required]
public virtual string Requestor { get; set; }
}
public class Requests
{
[Required]
public virtual int RequestsId { get; set; }
[Required]
public virtual DateTime RequestTime { get; set; }
[Required]
public virtual bool isCompleted { get; set; }
[Required]
public virtual string UserName { get; set; }
[Required]
public virtual int RulesId { get; set; }
public virtual string Description { get; set; }
}
public class ExecutionStatus
{
[Required]
public virtual int ExecutionStatusId { get; set; }
[Required]
public virtual int RequestId { get; set; }
[Required]
public virtual int CurrentApproverLevel { get; set; }
[Required]
public virtual string ApprovalStatus { get; set; }
}
public class Approvals
{
[Required]
public virtual int ApprovalsId { get; set; }
[Required]
public virtual int RulesId { get; set; }
[Required]
public virtual int ApproverLevel { get; set; }
[Required]
public virtual string ApproverName { get; set; }
}
public class RequestExecutionViewModel
{
private RequestsContext db = new RequestsContext();
public RequestExecutionViewModel(string username)
{
this.Request = db.Requests.Where(a => a.UserName.Equals(username)).First();
//aa = db.Approvals.Where(a => a.RulesId.Equals(Request.RulesId));
this.Approvals = (List<Approvals>) db.Approvals.Where(a => a.RulesId.Equals(Request.RulesId)).ToList();
this.ExecutionStatus = (List<ExecutionStatus>)db.ExecutionStatus.Where(a => a.RequestId.Equals(Request.RequestsId)).ToList();
}
[Required]
public virtual int RequestExecutionViewModelId { get; set; }
public Requests Request {get;set;}
public List<Approvals> Approvals { get; set; }
public List<ExecutionStatus> ExecutionStatus { get; set; }
}
Edit: Doing the database query inside model seems wrong to me. There should be a better way of doing things.

the composition is fairly good, however, you shouldn't have private RequestsContext db = new RequestsContext(); and RequestExecutionViewModel() included in the viewmodel. also, you might want to use IList<> rather than List<>.
your db access should be performed in the service layer or the controller action and should probably be injected via some IOC container.
just my 2 cents

Typically you would do your model building in your Controller Action, not in the ViewModel code itself. Boilerplate mapping code though could be useful in the ViewModel, but the querying of the database I don't think should be in there.
Not all pages will have ViewModels necessarily, and it would lead to DB queries scattered around if some do queries in the controller while some do it in the ViewModel.

In my opinion this seems to be mostly fine. As far as I understand, a ViewModel should be used to provide just enough data from the Model(s) to your View, which yours does.
The only other thing I could suggest is maybe using the Repository pattern instead of directly using a RequestsContext so you could do unit testing better.

Related

ModelState.IsValid is false when model has a foreign key

I have a MachineSettings entity and this entity has a Machine entity as a foreign key.
public class Machine
{
[Key]
public int MachineId { get; set; }
[Required]
[Display(Name = "Machine Type")]
public string MachineName { get; set; }
}
public class MachineSettings
{
[Key]
public int MachineSettingId { get; set; }
[Required]
public string Password { get; set; }
[Required]
public int ReferenceKeyLength { get; set; }
[Required]
public virtual Machine MachineId { get; set; }
}
In my controller class, when I created an Edit method as POST:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(MachineSettings machineSettings)
{
if (ModelState.IsValid)
{
db.Entry(objapp).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(machineSettings);
}
On the page I have shown the list of machines in drop down list, when do the changes and select another machine in dropdown and click on save. The Edit method is called with a MachineSetting object. Here my ModelState.IsValid always false.
On checking the object in debug mode, I am getting the MachineId from the drop down, but MachineName is not returned, so how to avoid MachineName checking in this so that ModelState.IsValid property will be true?
try to fix the classes
public class MachineSettings
{
[Key]
public int MachineSettingId { get; set; }
[Required]
public string Password { get; set; }
[Required]
public int ReferenceKeyLength { get; set; }
[Required]
public int? MachineId { get; set; }
public virtual Machine Machine { get; set; }
}
public class Machine
{
[Key]
public int MachineId { get; set; }
[Required]
[Display(Name = "Machine Type")]
public string MachineName { get; set; }
public virtual ICollection<MachineSettings> MachineSettings { get; set; }
}
and create view model for the view
public class MachineViewModel
{
public MachineSettings MachineSettings {get; set;}
public IEnumerable<SelectListItem> Machines { get; set; }
}
and action
public ActionResult Edit(MachineViewModel viewModel)
Maybe you should use a specific class for your ViewModel. ViewModels are for Views(UI) <--> Controller intercommunication, and usually are diferrent classes than the persistance (EF) layer. So your ViewModel doesn't even needs a full Machine property. Could be something like that:
public class MachineSettingsViewModel
{
public int MachineSettingId { get; set; } // Only if you are using the ViewModel for edit
[Required]
public string Password { get; set; }
[Compare("Password")]
public string ConfirmPassword { get; set; }
[Required]
public int ReferenceKeyLength { get; set; }
[Required]
public int MachineId { get; set; }
public IEnumerable<SelectListItem> Machines { get; set; } // Collection with the options for the machine selector. Must be filled in the controller from query to service or DB
}
The ViewModel may include properties only used in the view, or needed for form validation but not needed in the DB.
In the controller "Get" action, you create and fill the viewmodel, then you pass it yo the view, and in the "Post" method you validate the viewmodel and convert it to a entity to be saved in the DB.
Take a look at What is ViewModel in MVC?. It's a more detailed explanation of ViewModels vs Models.

InCorrect Map In AutoMapper When Map Model To ViewModel

I Have an Model"TClientsAdmins" and ViewModel is TClientsAdminsViewModel.I want read all records from DB where filtered by id.
I Read they from domain model and when I want Map to viewmodel I get zero Count in viewmodel.
In addition I have a model that is called THoldingAdmins That is associated with TClientsAdmins.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[StringLength(50)]
public string CompanyName { get; set; }
public int? THoldingAdminsId { get; set; }
public virtual THoldingAdmins THoldingAdmins { get; set; }
and TClientAdminViewModel :
public int Id { get; set; }
[StringLength(50)]
public string CompanyName { get; set; }
public int? THoldingAdminsId { get; set; }
public virtual THoldingAdminsViewModel THoldingAdmins { get; set; }
and THoldingAdmins:
public byte[] Logo { get; set; }
public bool IsDeleted { get; set; }
public virtual List< TClientsAdmins> TClientsAdmins { get; set; }
and AutoMapper Configuration :
Mapper.CreateMap<TClientsAdminsViewModel, TClientsAdmins>();
Mapper.CreateMap<List<TClientsAdminsViewModel>,List<TClientsAdmins>>();
You don't need an explicit mapping for IList, but it does look like you have your mapping the wrong way round, if you're mapping from Model to ViewModel. Try
Mapper.CreateMap<TClientAdmins, TClientAdminsViewModel>();
Mapper.CreateMap<THoldingAdmins, THoldingAdminsViewModel>();

asp.net: How do I combine two models together to be able to create a Parent and all of its child entities?

I am new to use ASP.net and MVC and in need of some help.I am currently working on a project which is an online recipe book, and I have various tables but the two tables I want to combine into one view is Recipe and RecipeIngredients, as shown below:
Recipe.cs
namespace TheOnlineFoodBook.Models
{
public class Recipe
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int RecipeID { get; set; }
[Display(Name = "Recipe")]
public string RecipeName { get; set; }
[Display(Name= "Cuisine")]
public int CuisineID { get; set; }
public string Directions { get; set; }
[Display(Name = "Preperation Time:")]
public double PreperationTime { get; set; }
[Display(Name = "Cooking Time:")]
public double CookingTime { get; set; }
public virtual List<RecipeIngredient> RecipeIngredients { get; set; }
[ForeignKey("CuisineID")]
public virtual Cuisine Cuisine { get; set; }
}
}
RecipeIngredient.cs
public class RecipeIngredient
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int RecipeID { get; set; }
public int IngredientID { get; set; }
public double Quantity { get; set; }
public int MeasurementID { get; set; }
public string Notes { get; set; }
public virtual Recipe Recipe { get; set; }
public virtual Measurement Measurement { get; set; }
public virtual Ingredient Ingredient { get; set; }
}
}
I've read a little about combining models to create a viewModel but i'm not sure how I would go about doing this, and I also want to be able to add multiple RecipeIngredients entries at the same time when creating a Recipe. Currently I have the basic CRUD views working which entity framework created for me.
You already have a "Recipe" object in "RecipeIngredient" so using "RecipeIngredient" as model in your view would give you access to "Recipe" properties, another approach you could take is using #modelTuple<Recipe,RecipeIngredient> in your view after passing new Tuple<Person, Order>(new Recipe(),new RecipeIngredient()); as you would pass a model, but I wouldn't see the use in following the later one since "RecipeIngredient" already has "Recipe"
Will this do what you need?
public class RecipeViewModel
{
public Recipe Recipe { get; set; }
public List<RecipeIngredients> RecipeIngredientsList { get; set; }
}
You won't be able to use data annotations as effectively, but if that isn't a concern you will now be able to attach multiple RecipeIngredients to the same Recipe for your page.

Simple way to populate a collection with ViewModel objects with domain object properties to pass to index

I'm new to MVC.NET (and development generally really) and I'm really starting to see the light of the benefit of ViewModels.
So, I'm currently refactoring one of my index methods to send a collection of ViewModel objects to the view. However, I'm not quite sure of the best way to achieve this.
Basically, my domain model is a UseCase and my ViewModel is UserGoalsStepViewModel. I know that in my UserGoalIndex action I'll need a collection to contain my ViewModel objects, each of these having had it's properties set by each UseCase that exists, but I'm not sure how to achieve it.
Help much appreciated.
Note: I know I could be using AutoMapper and probably a lot of other techniques to make life easier, but as I'm new to this (and time pressured!) I'm just looking for the simplest approach possible.
UseCase Model
public enum Level
{
Summary, UserGoal, SubGoal,
}
public class UseCase
{
public virtual int ID { get; set; }
//[Required]
public virtual int ProjectID { get; set; }
public virtual Project project { get; set; }
[Required]
public virtual int ActorID { get; set; }
[DisplayName("Actor")]
public virtual Actor actor { get; set; }
public virtual string Title { get; set; }
public virtual Level? Level { get; set; }
public virtual string Precondition { get; set; }
[DisplayName("Minimal Guarantee")]
public virtual string MinimalGuarantee { get; set; }
[DisplayName("Success Guarantee")]
public virtual string SuccessGuarantee { get; set; }
public virtual ICollection<Step> Steps { get; set; }
public virtual ICollection<Extension> Extensions { get; set; }
public virtual ICollection<Query> Queries { get; set; }
}
UserGoalViewModel (subset of UseCase properties)
public class UserGoalsStepViewModel
{
public enum GoalLevel
{
Summary, UserGoal, SubGoal,
}
public int ProjectId { get; set; }
//ActorId removed as SelectedActorId is what binds to model
//public int ActorId { get; set; }
[DisplayName("Actor Title")]
public SelectList ActorList { get; set; }
[DisplayName("Actor Title")]
public string ActorTitle { get; set; }
public int SelectedActorId { get; set; }
[DisplayName("Goal Title")]
public string GoalTitle { get; set; }
public GoalLevel Level { get; set; }
public UserGoalsStepViewModel ()
{
}
}

Navigation Property Null after query in CTP4 Code First EF4 Feature

I just started playing around with the CTP4 and Code-First. I have the following setup for a possible dating site:
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string LoginName { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
public string Street { get; set; }
[Required]
public string Zip { get; set; }
[Required]
public string City { get; set; }
[Required]
public bool Gender { get; set; }
[Required]
public int SoughtGender { get; set; }
[Required]
public string Password { get; set; }
[Required]
public double Latitude { get; set; }
[Required]
public double Longitude { get; set; }
}
public class Vote
{
[Key]
public int ID { get; set; }
[Required]
public User Voter { get; set; }
[Required]
public User TargetUser { get; set; }
[Required]
public int Decision { get; set; }
[Required]
public DateTime Timestamp { get; set; }
}
public class MySQLContext : DbContext
{
public MySQLContext (string constring)
: base(constring)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Vote> Votes { get; set; }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
modelBuilder.Entity<Vote>().HasRequired(b => b.Voter).WithMany();
modelBuilder.Entity<Vote>().HasRequired(b => b.TargetUser).WithMany();
base.OnModelCreating(modelBuilder);
}
}
Now the framework does a nice job of creating the DB with all the proper keys. Now I inserted some dummy data and fired up the following query:
public override IEnumerable<Domain.Vote> FindVotes(Domain.User user)
{
var query = from v in context.Votes where v.Voter.Id == user.Id select v;
return from v in query.AsEnumerable() select v;
}
The query does return the proper Vote entities but the two User properties of the Vote object are Null. Shouldnt the framework populate those properties with the foreign keys of the users referenced in the Vote table?
Let me give you some background on EF so you can understand how this works. EF from day 1 only supported explicit load like one below
Customer.Orders.Load();
Hmm, the feedback was not welcomed by the community and developers wanted lazy loading. To support Lazy Loading EF team said you must mark your navigation property as virtual. So at runtime, Ef creates a proxy object that derives from your entity and overrides the virtual property. Below is an example of such code.
public class Customer
{
public string Name{get;set;}
public virtual ICollection<Order> Orders{get;set;}
}
At runtime there is a proxy that implements IEntityWithChangeTracker and concrete type of the collection is an entitycollection which has been around since version 1.
public class CustomerProxy:Customer,IEntityWithChangeTracker
{
private ICollection<Order> orders;
public override ICollection<Order> Orders
{
if(orders == null)
{
orders = new EntityCollection<Order>();
orders.Load();
}
return orders;
}
}
change your class to the follow
public class Vote {
[Key]
public int ID { get; set; }
[Required]
public virtual User Voter { get; set; }
[Required]
public virtual User TargetUser { get; set; }
[Required]
public int Decision { get; set; }
[Required]
public DateTime Timestamp { get; set; }
}
Notice I've added virtual to the Voter && TargetUser properties and you should be good to go.

Categories

Resources