I'm developing bulletin board system (as part of my training of asp.net mvc). I have a basic understanding of data modeling, but I have a doubt the way I've created my model. The core logic is to post ad with the following categories realty, auto and service. Initially I tried to use TPH approach, but then faced with problem of binding my models and automapper configuration. Now I think to use zero or one relationship.
I have a Ad model:
public class Ad
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual Realty Realty { get; set; }
public virtual Auto Auto { get; set; }
public virtual Service Service { get; set; }
}
Realty:
public class Realty
{
[Key]
[ForeignKey("Ad")]
public int AdID { get; set; }
public string Type { get; set; }
public string NumberOfRooms { get; set; }
public virtual Ad Ad { get; set; }
}
Auto and service models have the same foreign key as the Realty model.
My db context:
public DbSet<Ad> Ads { get; set; }
public DbSet<Realty> Realties { get; set; }
public DbSet<Auto> Autos { get; set; }
public DbSet<Service> Services { get; set; }
I need update Ad model with one related model only. I'm using scaffolded controller action, which includes all related models:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "Title,Descirpiton,Realty,Auto,Service")] Ad ad)
{
if (ModelState.IsValid)
{
db.Ads.Add(ad);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.ID = new SelectList(db.Autos, "AdID", "CarType", ad.ID);
ViewBag.ID = new SelectList(db.Realties, "AdID", "Type", ad.ID);
ViewBag.ID = new SelectList(db.Services, "AdID", "ServiceType", ad.ID);
return View(ad);
}
The problem, that it makes possible to post Ad with all related models together. Before diving deep I wanted to ensure that I'm on a right way of doing this.
Thanks.
You're close. Based on what it looks like you're trying to do you should be using a table-per-type model. You create the base (Ad) and then inherit from it to create the sub-types.
public class Ad
{
[Key]
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
[Table("Realty")]
public class Realty : Ad
{
public string Type { get; set; }
public string NumberOfRooms { get; set; }
}
Your context remains the same. You can now create the appropriate sub-type when you know what kind of ad is being created.
var ad = new Realty();
ad.Title = "...";
ad.Description = "...";
ad.Type = "...";
ad.NumberOfRooms = "...";
You can retrieve specific ad types by using the specific type on the context.
db.Realty.ToList();
Or you can retrieve all the ads and interrogate the types as you loop over them.
var ads = db.Ads.ToList();
foreach(var ad in ads)
{
if(ad is Realty)
// do Realty stuff
else if (ad is Auto)
// do Auto stuff
}
Related
I am trying to implement code-first approach of entity framework. I have four entities UserInfo, Client, Admin and Account. I want relationships as:
Each Client has a UserInfo
Each Admin has a `UserInfo
Each Account is linked with a User(UserInfo)
Assuming these things i wrote the POCO models. With the relationships i want, is it correct ?Am i missing something?
public class UserInfo
{
public int UserInfoID { get; set; }
public Name Name { get; set; }
public Address Address { get; set; }
public Contact Contact { get; set; }
}
public class Admin
{
public int AdminID { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
public class Client
{
public int ClientID { get; set; }
public CompanyDetails CompanyDetails { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
public class Account
{
public int AccountID { get; set; }
[Required, Column("Balance"), Display(Name = "Account Balance")]
public double Balance { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
What you have appears to be correct based on your requirements however I personally prefer the Entity Framework Model Builder when configuring your entities with Code First.
Using the model builder means that you don't have any attributes on your POCO entities which in turn means that you don't need an EF reference to use the entities.
Take a look at my article here for some more info on how to use the modelbuilder : http://blog.staticvoid.co.nz/2012/07/entity-framework-navigation-property.html
I have three related tables. Calendar 1...* CalendarUser *...1 User. When I have edited the CalendarUsers in the edit calendar view I then post the ViewModel back to the controller. Here is my controller code:
[HttpPost]
public ActionResult Edit(int id, CreateCalendarViewModel cvm)
{
long userId = long.Parse(User.Identity.Name);
db.Calendars.Attach(cvm.CurrentCalendar);
cvm.Users= DbExtensions.GetUserList(userId);
if (ModelState.IsValid)
{
////Remove the deselected users
cvm.CurrentCalendar.CalendarUsers.Clear();
//Get the names from the selected users
var selectedUsers = from u in cvm.Users
where cvm.SelectedUsers.Contains(u.Key)
select new KeyValuePair<long, string>(long.Parse(u.Key), u.Value);
foreach (var selectedUser in selectedUsers)
{
User user = db.Users.Find(selectedUser.Key);
//If usr does not exist create a new
if (user == null)
{
db.Users.Add(new User
{
UserId = selectedUser.Key,
Name = selectedUser.Value,
Expires = DateTime.Now,
AccessToken = string.Empty
});
}
//Add the binding to the calendar
cvm.CurrentCalendar.CalendarUsers.Add(new CalendarUser
{
CalendarId = cvm.CurrentCalendar.CalendarId,
UserId = selectedUser.Key
});
}
db.Entry(cvm.CurrentCalendar).State = EntityState.Modified;
db.SaveChanges();
}
return View(cvm);
}
Here are my classes:
public partial class Calendar
{
public Calendar()
{
this.CalendarUsers = new HashSet<CalendarUser>();
}
public int CalendarId { get; set; }
public string CalendarTitle { get; set; }
public string CalendarDescription { get; set; }
public long UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<CalendarUser> CalendarUsers { get; set; }
}
public partial class CalendarUser
{
public int CalendarUserId { get; set; }
public int CalendarId { get; set; }
public long UserId { get; set; }
public Nullable<bool> IsAdmin { get; set; }
public virtual Calendar Calendar { get; set; }
public virtual User User { get; set; }
}
public partial class User
{
public User()
{
this.Calendars = new HashSet<Calendar>();
this.CalendarUsers = new HashSet<CalendarUser>();
}
public long UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Calendar> Calendars { get; set; }
public virtual ICollection<CalendarUser> CalendarUsers { get; set; }
}
For some reason when i save the changes the calendar is being deleted as well? I've searched a bit but noone seem to have the same problem? Am I doing it wrong? Is there a better way to update/remove related entities?
It seems that I forgot to include a hidden field in in the view containing the id of the user and the result was that when I updated the calendar it saved with Id = 0 and thus hid the objects in the view for the specified user. Mental note: Always verify in the database what is really happening.
I also need to look into whats happening when I send objects back and forth between views and controller. Sometimes it seems to manage by itself and sometimes I need to specify all the fields myself.
I have built a simple survey-tool using MVC 3 with only 1 layer (MVC). I regret this now. All my database access and mapping is handled in the controllers, and some other mapping classes.
I would like to switch over to using three layers:
Presentation (MVC)
Business Logic
Data / Persistence (EF)
I am using the Entity Framework to handle everything with the database. The entity framework creates it's own domain classes. Where should the mapping between the Models that MVC uses and the models that EF creates go?
If the mapping is in the business layer, is there a need for the Models folder in the MVC project?
A survey-question consists of the Question itself, Rows and Columns. Theese are the models that i use:
public class Question {
public int Question_ID { get; set; }
public Boolean Condition_Fullfilled;
[Required(ErrorMessage = "Dette felt er påkrævet")]
public String Question_Wording { get; set; }
public String Question_Type { get; set; }
[Required(ErrorMessage = "Dette felt er påkrævet")]
public String Question_Order { get; set; }
public String Left_scale { get; set; }
public String Right_scale { get; set; }
public int Scale_Length { get; set; }
public String Left_Scale_HelpText { get; set; }
public String Right_Scale_HelpText { get; set; }
public Boolean Visible { get; set; }
public Boolean IsAnswered { get; set; }
public String Question_HelpText { get; set; }
public int Category_ID { get; set; }
}
public class MatrixColumns
{
public int Column_ID { get; set; }
public int Column_Number { get; set; }
public String Column_Description { get; set; }
public Boolean IsAnswer { get; set; }
public int? Procent { get; set; }
public bool Delete { get; set; }
public bool Visible { get; set; }
public int? Numbers { get; set; }
public String Help_Text { get; set; }
}
public class MatrixRows
{
public bool Delete { get; set; }
public bool Visible { get; set; }
public int Row_Id { get; set; }
public String Row_Number { get; set; }
public String Row_Description { get; set; }
public String Special_Row_CSS { get; set; }
public String Help_Text { get; set; }
// Dette er summen af procenterne af alle kolonner i rækken
public int RowSum { get; set; }
}
All the data for theese models is retrieved in the Controller, based upon a QuestionID, and mapped to a ViewModel that looks like this:
public class ShowMatrixQuestionViewModel : Question
{
public Dictionary<MatrixRows, List<MatrixColumns>> columnrow { get; set; }
public List<MatrixColumns> columns { get; set; }
public List<MatrixRows> rows { get; set; }
public ShowMatrixQuestionViewModel()
{
columns = new List<MatrixColumns>();
rows = new List<MatrixRows>();
columnrow = new Dictionary<MatrixRows, List<MatrixColumns>>();
}
}
So when i want to send a ShowMatrixQuestionViewModel to a View from my Controller, what is the route i should take?
This is my suggestion:
-> Controller calls a method in the business layer called
public ShowMatrixViewModel GetQuestion(int QuestionID) {}
-> GetQuestion calls the following methods in the data layer:
public Question GetQuestion(int QuestionId) {}
public MatrixRows GetRows(int QuestionId) {}
public MatrixColumns GetColumns(int id) {}
-> Entity framework returns "pure" objects, which i want to map over to the ones i posted above
-> GetQuestion calls methods to map the EF models to my own models
-> Last GetQuestion calls a method that maps the Questions, Rows and Columns:
ShowMatrixQuestionViewModel model = MapShowMatrixQuestionViewModel(Question, MatrixRows, MatrixColumns)
return model;
Is this correct?
Thanks in advance
To answer the first part of your question:
"Where should the mapping between the Models that MVC uses and the models that EF creates go?"
The answer is that the models MVC uses are the models created by the EF. Your EF tool in the ASP.NET MVC project is either Linq to SQL Classes or the ADO.NET Entity Framework Model. You should create these inside the Models folder in your project and they provide your data / persistence (EF).
I'm creating an MVC3 asp.net application using Entity Framework 4 and C#.
I've tried to read up on EF and model binding, lazy loading, etc. But I've hit an obstacle.
I have a User Model. The Store and UserType models can have an ICollection of Users. When I add a User with the Create Action, How do I specify multiple parents?
I think that I only know how to add if there is one parent.
My Models:
public class UserType
{
public virtual int ID { get; set; }
public virtual string UserTypeName { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class Store
{
public virtual int ID { get; set; }
public virtual string StoreName { get; set; }
public virtual Address StoreAddress { get; set; }
public virtual ICollection<Workroom> Workrooms { get; set;}
public virtual ICollection<User> Users { get; set; }
}
public class User
{
public virtual int ID { get; set; }
public virtual string Username { get; set; }
public virtual string Email { get; set; }
public virtual Store Store { get; set; }
public virtual UserType UserType { get; set; }
}
Here is my db context:
public DbSet<Workroom> Workrooms { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Store> Stores { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserType> UserTypes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Store>()
.HasMany(store => store.Workrooms)
.WithRequired(workroom => workroom.Store);
modelBuilder.Entity<Store>()
.HasMany(store => store.Users)
.WithRequired(user => user.Store);
modelBuilder.Entity<UserType>()
.HasMany(usertype => usertype.Users)
.WithRequired(user => user.UserType);
base.OnModelCreating(modelBuilder);
}
Here's my create action:
public ActionResult Create()
{
return View(new User());
}
//
// POST: /Users/Create
[HttpPost]
public ActionResult Create(User newUser)
{
try
{
int storeID = newUser.Store.ID;
var store = _db.Stores.Single(r => r.ID == storeID);
store.Users.Add(newUser);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.InnerException.Message);
return View();
}
}
Do I just add another "Add" call for UserType? for example:
int userTypeID = newUser.UserType.ID
var usertype = _db.UserTypes.Single(s => s.ID == userTypeID)
How would the Entity Framework know that Users has another Parent??? ...do I care?
My hunch is that I should be doing this a different way, more specific and more accurate.
In this case, you probably want to add the user to the Users table, rather than the Stores. Then you assign the StoreID and UserTypeID to the user before you commit.
It looks like you're already setting the StoreID in your UI, are you doing the same for UserType? If so, then just add the user to the users table and you should be good.
I'm having trouble passing view information from my Get/Create action to my view. Here are my three model classes;
public class Competition
{
public int Id { get; set; }
public int CompetitionId { get; set; }
public string Name { get; set; }
public string Prize { get; set; }
}
public class CompetitionEntry
{
public int Id { get; set; }
public int CompetitionEntryId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int CompetitionId { get; set; }
}
public class CompetitionEntryViewModel
{
public int Id { get; set; }
public Competition Competitions { get; set; }
public int CompetitionId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Here is my Get/Create action in CompetitionEntry Controller;
public ActionResult Create(int id)
{
CompetitionEntryViewModel competitionentryviewmodel = db.CompetitionEntriesView.Find(id);
return View(competitionentryviewmodel);
}
I know this doesn't work. The id parameter goes into the URL fine. How to I get access to my Competition class in th Get action? I need to be able to show the competion name on my Create Competition entry view.
Thanks in advance!
public ActionResult Create(int id)
{
var data = db.CompetitionEntriesView.Find(id);
CompetitionEntryViewModel competitionentryviewmodel = new CompetitionEntryViewModel();
competitionentryviewmodel.CompetitionName = data.Name;
return View(competitionentryviewmodel);
}
What you are trying to do is build an object graph and display it through a view model. In order to do this, you need to map your domain model(s) to your view model.
You can do the mapping yourself by writing a lot of code (re-inventing the wheel), or, you could consider using third party tools to do this for you. I recommend you use an AutoMapper as it is very simple to use imo.
The other problem is that your view model contains a domain model. This is likely to cause you a lot of headache in near future. If I were you, I would replace Competition with CompetitionViewModel.
I would also consider creating a view model for a list of competitions, i.e. CompetitionsViewModel. Look into partial views to see how you can display a list of competitions.
Good luck