viewmodel and scaffolding migration - c#

I am trying to create a multiple options select for my dropdown menu. I am able to get the listbox working fine, however, I am trying to store the selected items so I tried using a viewmodel to store it as a collection, however, my .cshtml file was already calling the db model, so I couldn't include the viewmodel in the .cshtml file since a view can only have one model.... also even when I tried just calling the viewmodel in my .cshtml using other means, it kept saying the collection was null exception error...
So following this wonderful youtube video tutorial I decided to bring the ICollection to my existing model like this:
public virtual ICollection<Country> Countries{ get; set; }
Everything was smooth until it said, i should do a database migration because the model has changed.
Due to security reasons, I need to send any update queries to the sysadmin to run any table changes against the database, so my question is, what is really happening when I use
public virtual ICollection<Country> Countries{ get; set; }
What is changing in my existing table? What am I altering? And if nothing is changing, then how come I need to do a db migration for this to take effect? Each time I run update-database in the package manager console, I get an error which says I don't have permissions (an error I am familiar with, which requires me to send table changes to the sysadmin)
Any ideas where to store my collection, in the viewmodel or the model. The youtube example shows in the model?

When you add a property like:
public virtual ICollection<Country> Countries{ get; set; }
Then a foreign key needs to be added on the table backing Country to point to the model that has this property.
I'm not sure what problem exactly you're having using view models, but it's pretty straight-forward. Just add whatever properties you need to edit from your model to the view model and then make the view model the model for the view.
If you got an error saying a collection is null, then you need to initialize the collection. Again, this is all pretty basic stuff here. You can either do it in the constructor of your view model, manually in your action, etc. Whatever makes the most sense for your application.
Then, for a select list, you must remember that the value that will be posted is going to be a scalar type: string, int, etc. You can't directly post to something that expects a class like Country because the modelbinder will have no idea how to instantiate Country based on the posted value. Usually, what you would do is use the Id as the value of the option, and then in your post action, use the id values to look up the actual objects from the database, before finally attaching those to your model.
View Model
public class FooViewModel
{
public FooViewModel()
{
SelectedCountryIds = new List<int>();
}
...
public List<int> SelectedCountryIds { get; set; }
public IEnumerable<SelectListItem> CountryChoices { get; set; }
}
Controller
private void PopulateCountryChoices(FooViewModel model)
{
model.CountryChoices = db.Countries.Select(m => new SelectListItem
{
Value = m.Id.ToString(),
Text = m.Name
});
}
public ActionResult Foo()
{
var model = new FooViewModel();
PopulateCountryChoices(model);
return View(model);
}
[HttpPost]
public ActionResult Foo(FooViewModel model)
{
if (ModelState.IsValid)
{
var foo = new Foo
{
// map properties from view model
Countries = db.Countries.Where(m => model.SelectedCountryIds.Contains(m.Id))
}
db.Foos.Add(foo);
db.SaveChanges();
return RedirecToAction("Index");
}
// Posted form has errors
PopulateCountryChoices(model);
return View(model);
}
View
#model Namespace.To.FooViewModel
...
#Html.ListBoxFor(m => m.SelectedCountryIds, Model.CountryChoices)

Related

Add variable to Entity Framework created model class without editing the actual database columns?

I just recently started creating a .net web app in mvc5. I've tried to search for the answer as I usually do rather than asking a question but Im not sure how to word this but here it goes:
I have a class:
public partial class employee
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
In which the class was created by using the Database First wizard with Entity Framework. (reasoning for the tag)
What I did was get the FistName, MiddleName, and LastName from the database. In which I successfully did with this ActionResult from my controller.
[ChildActionOnly]
public ActionResult Nav()
{
employee user;
using (DatabaseEntities context = new DatabaseEntities())
{
user = context.employees.FirstOrDefault(e => e.FirstName ="Bob");
if (!System.IO.File.Exists(Server.MapPath(user.imagefile)))
{
user.imagefile = $"/Content/Images/user/{user.FirstName[0]}.png";
}
}
return PartialView("_Nav", user);
}
Now I'm wondering if its possible, how to create another item to the class without adding a column to the database. I want to add a FormattedName variable to the class that is got after the database call to get the model (user) to include the new variable (FormattedName) that gets its value from the existing variables in the class.
I understand the wording and terminology could be off but any help would be great and thx in advance.
Edit:
In the comment section, I was informed about attributes ex.[NotMapped] which was information I definitely needed to know. But for this particular question, it doesn't work. Everything was ok until I updated the model using entity framework, the attributes were gone and have to keep adding them every model update which is not ideal for me. I went ahead and edited my database to include a (FormattedName) column so the problem is fixed. But I still would love to know if this is doable.

retrieving database-computed properties of classes within entity framework

I have a good understanding of EF, and generated my database successfully. Now, I am struggling with adding dynamic properties to one of the entity classes. For example, I have a Post class, and other users can make comments to the posts. When I list the existing posts, I want to display the number of comments made to corresponding post.
One solution might be having a property called CommentCount, and updating the Post by increasing the (int) value of the CommentCount property by 1 when a new comment is made.
The other solution, and I think it is a better solution, is that when retrieving the post from the DB, the number of comments associated with the post can be computed and retrieved at the same time and assigned to CommentCount property of the post instance. However, I do not know how to achieve this with EF.
Which approach is highly recommended? Or, is there any other ways of doing this? If it is the second one, how can I achieve this with EF?
1) You should simply consider not putting the property called CommentCount into your model. When you develop for example a WPF Windows application, you should consider using MVVM pattern and the CommentCount would belong to your ViewModel class and not to your Model class. There you implement INotifyPropertyChanged and you can use it from your frontend Views. Analogically there is MVC pattern for ASP.NET etc.
There are other design patterns like Repository pattern. Using this pattern you can create the CommentCount in your repository class and not in your
model class. This would be similar to your second solution.
2) I assume from your question that you are using code-first approach:
generated my database successfully
If you do so and you wish to include CommentCount directly in your Model class, you can do it this by adding partial class file to your project like this:
namespace DBModel.Models
{
public partial class Post
{
public int CommentsCount
{
get { return this.Comments.Count; }
}
...
But I cannot see why to create extra property in your model just for that.
On the other hand adding this field as a computed field into your SQL database could make sense and then it would be part of your EF model.
If you calculation is very complex you should try creating a View in your DB and then add it to your Model?
But if your Model have something simple like
class Post {
public int postid { get; set; }
public virtual ICollection<comment> comment { get; set; }
}
In your controller you can do
db.post(x => x.postid == yourid).comments.count()
to get total of comment
or in your view
#foreach (var item in Model)
{
<li>item.postid;</li>
<li>item.comment.Count();</li>
}
Or update your class
class Post {
public int postid { get; set; }
public virtual ICollection<comment> comment { get; set; }
public int CommentCount
{
get
{
return comment.Count();
}
}
}
Just remember bring related data in your query.
In my case POI have properties parish_id, sector_id, city_id and parish have municipality, and municipality have state.
Using this query I can get Poi with all the related data.
filter = db.poi
.Include("parish")
.Include("sector")
.Include("city")
.Include("parish.municipality")
.Include("parish.municipality.state")
.Where(x => x.sector_id == SectorID);

How to retrieve data from multiple tables and display in a view using viewmodel

I'm trying to develop a messeging system to my mvc application using mvc 5. I have tables called Event, EventUser, EventObject. Each of those tables have following;
Event
ID
CreatedBy
StartTime
IsShared
Budget
EventUser
EventID
UserID
IsAccepted
EventObject
EventID
ObjectID
in my messageController i have the index method which receive the parameter of the user id.i need to display every event that user has invited using this method..
namespace MvcApp.Controllers
{
public class MessageController : Controller
{
private EPlannerDatabaseEntities db = new EPlannerDatabaseEntities();
// GET: /Message/
public ActionResult Index(int UId)
{
/* linq expressions */
return View();
}
}
}
when the parameter has passed in, i want to;
*Select from EventUser table where UID=UserID and join the result with Event and EventObject tables by using EventID attribute.
*Finally by using the final result i need to display every event's infomation that user has invited; like CreatedBy , StartTime, Budget,other users,objects etc..
i'm new to mvc and viewmodel concept.I heard that viewmodel concept can help with these situations.can i overcome this problem by using viewmodel concept.if yes what are the things i need to add in view model?? otherwise what are the other ways to do this?
one way i can see of doing this is creating a custom return object and using EF to join all the tables together. Example
public class MyObject{
public DateTime DateCreated{get;set}
// add remaining properties here
// properties to get back
}
then in code you would use Entity Framework to create a joined data set into a nice list of objects. Example:
var results = (from b in bla join bla2 in (Some Second Query Here)
from SomeSecondQueryHere
where cond1 and cond2 Select new MyObject{
// add properties in here})
where you would replace the bla and bla2,etc with respective table names needed. Then all you need to do is
return View(results);
And the changes will be accessible in the View
If you question is regarding querying with an ORM like Entity Framework, you need to post your entities, not your table schemas. The whole purpose of an ORM is to abstract away the underlying database structure, so while the schema will often be similar to the entity class, it can also be quite different. As a result, I'll have to make assumptions about your entity classes.
To query everything, you just need something like the following:
var events = db.Events.Where(m =>
m.EventUsers.Any(u => u.UserID == UId && u.IsAccepted)
).Include(m => m.EventObjects);
That assumes entity classes along the lines of:
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public int UserID { get; set; }
public bool IsAccepted { get; set; }
}
You end up with an enumerable of Event. If you need to access the EventObjects for an individual event, you have to use the appropriate collection property. For example:
foreach (var item in events)
{
foreach (var obj in item.EventObjects)
{
// do something with `obj` (an invidual `EventObject` instance)
}
}
If you need the actual User object, you're better object querying that first and including related Events and EventObjects:
var user = db.Users.Include("EventUsers.Event.EventObjects").SingleOrDefault(m => m.UserID == UId);
That assumes entities like:
public class User
{
...
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public virtual Event Event { get; set; }
}
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
}
With that method, however, there's no way to filter the included Events by whether they're accepted or not. There's a potential way around that, but it requires disabling lazy-loading of EventUsers entirely and complicates querying the information you need. If you need to go that route, see: https://msdn.microsoft.com/en-us/data/jj574232.aspx#explicitFilter.
Otherwise, you can just exclude non-accepted events before iterating over the collection:
var events = user.EventUsers.Where(m => m.IsAccepted).Select(m => m.Event);
Really you don't need a view model, per se, for any of this. As you can either pass the lists of events (which will include any related EventObjects) or the the single user instance (which includes related events and related EventObjects) directly to your view.
A very high level description of how to solve your scenario using Entity Framework would be something like this:
First you've got to create a series of entity data objects that will represent your tables in the EF data model using EF Code first techniques.
Then you create DbContext objects with DbSets for your previously created entities.
Then you create at least one Service class that will have a property representing DbContext and a set of methods encapsulating Linq queries to your entities.
In the MVC controller you call an instance of Service that you previously create and assign it to a property ant Controller's construction time. Finally, in the Action method you should call the correct Service method and pass any result to the view.
( I am assuming this is a small Ad-Hoc system with a handful of tables , an elaborate System with production quality would require using IoC techniques).

What's the best way to pass data from multiple tables/one database to a view?

I'm running into a weird issue. I'm using MVC 4, and I'm trying to pull data from two separate tables to pass into my view. Normally I'd just build a model to handle this, but these two tables are already used in other models, so when I try to create a new model, there's issues with ambiguity.
I guess my question is this: what's the best way to pull data from two separate tables on a database and get it into a view? For some reason, the whole concept of Entity Framework is a little confusing to me, so maybe I'm missing something simple here.
Also, if anyone has any place where I can read up on some pretty comprehensive database interactions using EF, I'd really appreciate it (though I guess I could just google that)
The best way will be to create a ViewModel and add the 2 models inside of it.
So you will need to create something like
public class MyViewModel
{
public MyFirstEntity FirstEntity{ get; set; }
public MySecondEntity SecondEntity{ get; set; }
}
And in your controller do something like this:
public ActionResult Index(int someparameter)
{
MyFirstEntity firstEntity=
BusinessLogic.GetMyFirstEntity(someparameter);
MySecondEntity secondEntity=
BusinessLogic.GetMySecondEntity(someparameter);
MyViewModel myViewModel = new MyViewModel
{
FirstEntity = firstEntity,
SecondEntity= secondEntity
};
return View(myViewModel);
}
Also you can check this blog post for more information on the matter.
Typically, your view model would only pass the view the information it requires. If implemented correctly, the view model should not know or care where the information has comes from.
In your case, you would create the view model based upon the data from the two tables, rather than using the two tables themselves.
The following is an example of what you could do:
public ViewResult MyActionMethod()
{
var tableOne = MyDataRepository.GetDataFromTableOne();
var tableTwo = MyDataRepository.GetDataFromTableTwo();
var model = new MyActionMethodModel()
{
Property1 = tableOne.Property1,
Property2 = tableTwo.Property2,
};
return this.View(model);
}

Does every property of an entity need to be included in an update

I have an entity "Rep"...
public partial class rep
{
public string repid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Title { get; set; }
public string Department { get; set; }
many, many more properties...
}
I then have a form that updates only the rep's first and last name. When the form is submitted, the first and last name are updated, but every other property is set to null.
I want the other properties to be left alone so they retain there existing values.
I could include all of the properties of the entity with hidden form fields to pass their values along to the Edit method, but is there a better/easier way?
I'm familiar with setting the IsModified property...
entry.Property(e => e.Title).IsModified = true;
But that seems unnecessary as well. Is this the way to go, or is there a better way?
The code you use to render the view and update the entity are quite relevant but missing, but I can guess what they look like:
#model ...rep
#using (Html.BeginForm()) {
#Html.HiddenFor(m => m.repid )
#Html.TextBoxFor(m => m.FirstName )
#Html.TextBoxFor(m => m.LastName)
<input type="submit" />
}
And in your controller:
[HttpPost]
public ActionResult Update(rep model)
{
using (var context = new MyContext())
{
context.reps.Attach(model);
context.SaveChanges();
}
return RTA(...);
}
This is attaching the entity that was posted, which is an entirely new object that is unknown to the context and its change tracker. By attaching an entity like that, you tell the context this is the new representation you want saved, i.e. with most properties set to null.
You can easily fix this by first retrieving the entity and then updating only the required properties:
[HttpPost]
public ActionResult Update(rep model)
{
using (var context = new MyContext())
{
var entity = context.reps.Find(rep.repid);
entity.FirstName = model.FirstName;
entity.LastName = model.LastName;
context.SaveChanges();
}
return RTA(...);
}
But don't do this. See What is ViewModel in MVC? or ViewModel Best Practices or search the web or this site on "mvc viewmodel".
Mainly because your database entity contains columns you don't want displayed or updated (mass assignment) and because sooner or later you will want to use extra properties in your view (dropdown data, script variables, related entities) that you can't stick in your database entities.
Genarally you can say: don't use entity models for view models, especially not when they are posted back. So the viewmodel is the way to go:
public class RepresentativeViewModel
{
public string RepID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
You then only have to alter your view's model declaration:
#model ...RepresentativeViewModel
And your controller just looks like the easiest solution:
[HttpPost]
public ActionResult Update(RepresentativeViewModel model)
{
using (var context = new MyContext())
{
var entity = context.reps.Find(rep.RepID);
entity.FirstName = model.FirstName;
entity.LastName = model.LastName;
context.SaveChanges();
}
return RTA(...);
}
You can of course replace the entity.FirstName = model.FirstName; mapping code into some automated solution, for example using AutoMapper.
You either use the IsModified property like you mentioned or you can create a view model that has the properties you want to change and use a mapper, like AutoMapper, to copy the values from the input to the entity. Typically I have a base class that has my modifiable data and a subclass that has the relationships and data I don't want to change like CreatedBy and stuff. Hidden fields is defnitely NOT the way to go.
You have two methods you could try
Create a view model with only the properties you want to update.
Store the values I the other properties in hidden fields they will not be returned null

Categories

Resources