I'm new to ASP.NET MVC and want to create a small order management tool. My database contains the tables Orders and Articles (and a few other ones), and I generated an EF Model from my database, so I can use the full power of the EF mappings (e.g. db.Orders.Articles)
My two main relations which I'm concerned about are Orders and Articles.
An order can have many articles
An article can only belong to one order.
I've created an OrdersController with an Create action to create an order:
//
// GET: /Orders/Create
public ActionResult Create()
{
Order order = new Order()
{
// filling some order columns, e.g. date
};
Article article = new Article()
{
// ... article columns
};
order.Articles.Add(article);
return View(order);
}
//
// POST: /Orders/Create
[HttpPost]
public ActionResult Create(Order order)
{
// i know i should care more about error handling, but now ommit it
db.Orders.AddObject(order);
db.SaveChanges();
return RedirectToAction("index");
}
So I'm directly binding an EF Object to a view (read somewhere not to do that and use a view model instead, but don't really know what that view model should look like)
My view contains the Order form as well as the article form (because i want to create a order and articles at the same time and not seperate). I used these greate EditorFor Methods to do that.
And now to my problem: If i hit the submit button, the app crashes as soon as it comes to the HttpPost Create Method (when mapping the order) with this error message:
Error Message: The EntityCollection
has already been initialized. The
InitializeRelatedCollection method
should only be called to initialize a
new EntityCollection during
deserialization of an object graph.
If i hit continue in VS2010, it will complete saving the order - so my question is how to solve this problem in a reliable way.
Thanks in advance and sorry for that long story :)
I solved my problem now by using a separate ViewModel like #Yakimych advised me. However I did not copy all the attributes from the EF models, instead I just refer to them. My ViewModel looks like this:
public class NewOrderViewModel {
public Order { get; set; }
public List<Article> { get; set; }
}
Related
Let me premise this by saying I am transitioning from Classic ASP to .net so my .net knowledge is all through books, not use. I am working on a shopping cart platform and have been tasked with creating 3 different display types (horizontal slider, vertical slider and grid) that will be able to be utilized by 2 different models.
I got the new views to work with my first model and thought I was in the clear and plugged in the second, but then I started getting an error about the models not being the same. I am trying to figure out what the best way to handle this. The solutions I have come up with are
Using A View Model - this seems like it is supposed to only be used when you need to combine 2 models into one and that is really the opposite of what I need, I need to make 1 view work for 2 models.
Make separate views for each type. This seems logical but is going to result in 5 new view files that are almost identical to those I have already created, it seemed redundant.
Use renderPartial and convert the models - not really sure how I would even accomplish this, but my though was to renderpartial out the creation of the actual displays (horizontal slider, vertical slider and grid view)
I am having a hard time determining what the best course of action is. I know that with MVC and .net you are supposed to never code the same thing twice and I think making the separate views is doing exactly that. Any thoughts on how to best approach this would be greatly appreciated.
This is the way I'm doing it right now on my project, I don't know if it's the best way or not but for me works fine.
Basically on my website's homepage I need to have to 2 different models to handle 2 forms posting to the same Method in the controller
namespace Website.Models.ViewModels
{
public class HomePageModels
{
public SearchFlyModel SearchFly { get; set; }
public CarRequestModel CarRequest { get; set; }
public int form { get; set; }
public HomePageModels()
{
SearchFly = new SearchFlyModel();
CarRequest = new CarRequestModel();
}
}
}
Then on the controller's method I need to know which form was posted to know which form I need to validate, because Asp.net automatically validate the model , it will ,by default validate both models in your ViewModel , and this affects the ModelState and when you check if your model is valid doing ModelState.isValid , this will return false because you only posted one form and not the other which is completely empty.
To manage that, I do the following
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(HomePageModels model)
{
ModelState.Clear();
if (model.form == 0)
{
try
{
ValidateModel(model.SearchFly);
return this.SearchFly(model.SearchFly);
}
catch (Exception e)
{
}
}
else
{
try
{
ValidateModel(model.CarRequest);
return this.SearchCar(model.CarRequest);
}
catch (Exception e)
{
}
}
var modelHomePage = new HomePageModels()
{
SearchFly = model.SearchFly,
CarRequest = model.CarRequest,
form=model.form
};
return View(modelHomePage);
}
Both forms are posting to the same action because I need to return the form's validations error so the user can fix it.
What I'm curious to get help with, is setting Model variables based on table data.
For example, In my database I may have a table of Facilities such as:
Toilet int, Sink int, Bath int, Rug string
Really bad example but yeah, these values may change in the database from form input or more facilities maybe added based on user input.
Now for me this isn't a problem, However what I would like to know is; in my model I get; and set; the values for [HttpPost] like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(NewBooking model)
{
if (!ModelState.IsValid)
{
return View("~/Views/Timetabler/Booking/Index.cshtml", model);
}
return View("~/Views/Timetabler/Booking/Index.cshtml", model);
}
So in my model I don't know what the variables would be, they could be Toilet, Sink etc but they could be somehting else therefore something like this wouldn't be suitable:
namespace build_01.Models.Timetabler
{
public class NewBooking
{
public string Toilet { get; set; }
}
}
How can I, if at all, create my model variables based on database table content?
Might I suggest using Entity Framework to generate database models?
It really is quite simple and if you make a change to the database table, you can update the model in your project.
If you already have a database, you would use a "Database First" approach. Make sure your database tables each have a Primary Key assigned to them.
https://www.asp.net/mvc/overview/getting-started/database-first-development/creating-the-web-application
In your application, right click your 'Models' folder > Add > New Item > ADO.NET Entity Data Model > EF Designer From Database ... Follow the steps on screen from here.
This will generate model classes that match the data types in your database. Oh, if your project doesnt already have Entity Framework, search for it in Nuget or check it on the Nuget repository.
https://www.nuget.org/packages/EntityFramework/
I have a web solution (in VS2010) with two sub-projects:
Domain which holds the Model classes (mapped to database tables via Entity Framework) and Services which (besides other stuff) are responsible for CRUD operations
WebUI which references the Domain project
For the first pages I've created I have used the Model classes from the Domain project directly as Model in my strongly typed Views because the classes were small and I wanted to display and modify all properties.
Now I have a page which should only work with a small part of all properties of the corresponding Domain Model. I retrieve those properties by using a projection of the query result in my Service class. But I need to project into a type - and here come my questions about the solutions I can think of:
I introduce ViewModels which live in the WebUI project and expose IQueryables and the EF data context from the service to the WebUI project. Then I could directly project into those ViewModels.
If I don't want to expose IQueryables and the EF data context I put the ViewModel classes in the Domain project, then I can return the ViewModels directly as result of the queries and projections from the Service classes.
In addition to the ViewModels in the WebUI project I introduce Data transfer objects which move the data from the queries in the Service classes to the ViewModels.
Solution 1 and 2 look like the same amount of work and I am inclined to prefer solution 2 to keep all the database concerns in a separate project. But somehow it sounds wrong to have View-Models in the Domain project.
Solution 3 sounds like a lot more work since I have more classes to create and to care about the Model-DTO-ViewModel mapping. I also don't understand what would be the difference between the DTOs and the ViewModels. Aren't the ViewModels exactly the collection of the selected properties of my Model class which I want to display? Wouldn't they contain the same members as the DTOs? Why would I want to differentiate between ViewModels and DTO?
Which of these three solutions is preferable and what are the benefits and downsides? Are there other options?
Thank you for feedback in advance!
Edit (because I had perhaps a too long wall of text and have been asked for code)
Example: I have a Customer Entity ...
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public City { get; set; }
// ... and many more properties
}
... and want to create a View which only shows (and perhaps allows to edit) the Name of customers in a list. In a Service class I extract the data I need for the View via a projection:
public class CustomerService
{
public List<SomeClass1> GetCustomerNameList()
{
using (var dbContext = new MyDbContext())
{
return dbContext.Customers
.Select(c => new SomeClass1
{
ID = c.ID,
Name = c.Name
})
.ToList();
}
}
}
Then there is a CustomerController with an action method. How should this look like?
Either this way (a) ...
public ActionResult Index()
{
List<SomeClass1> list = _service.GetCustomerNameList();
return View(list);
}
... or better this way (b):
public ActionResult Index()
{
List<SomeClass1> list = _service.GetCustomerNameList();
List<SomeClass2> newList = CreateNewList(list);
return View(newList);
}
With respect to option 3 above I'd say: SomeClass1 (lives in Domain project) is a DTO and SomeClass2 (lives in WebUI project) is a ViewModel.
I am wondering if it ever makes sense to distinguish the two classes. Why wouldn't I always choose option (a) for the controller action (because it's easier)? Are there reasons to introduce the ViewModel (SomeClass2) in addition to the DTO (SomeClass1)?
I would solve your problem by using an auto-mapping tool (like AutoMapper) to do the mapping for you. In cases where the mapping is easy (for example if all properties from one class should be mapped to properties with the same name on another class) AutoMapper will be able to do all the hook-up work for you, and you'll have to give a couple of lines of code to note that there should be a map between the two at all.
That way, you can have your entities in Domain, and a couple of view model classes in your WebUI, and somewhere (preferrably in WebUI or a sub namespace of the same) define maps between them. Your view models will in effect be DTOs, but you won't have to worry much about the conversion process between the domain and your DTO classes.
Note: I would strongly recommend against giving your Domain entities straight to the views of your MVC web UI. You don't want EF to "stick around" all the way to the front-end layer, in case you later want to use something other than EF.
introduce ViewModels which live in the
WebUI project and expose IQueryables
and the EF data context from the
service to the WebUI project. Then I
could directly project into those
ViewModels.
The trouble with this is you soon run into problems using EF trying to 'flatten' models. I encountered something similar when I had a CommentViewModel class that looked like this:
public class CommentViewModel
{
public string Content { get; set; }
public string DateCreated { get; set; }
}
The following EF4 query projection to the CommentViewModel wouldn't work as the couldn't translate the ToString() method into SQL:
var comments = from c in DbSet where c.PostId == postId
select new CommentViewModel()
{
Content = c.Content,
DateCreated = c.DateCreated.ToShortTimeString()
};
Using something like Automapper is a good choice, especially if you have a lot of conversions to make. However, you can also create your own converters that basically convert your domain model to your view model. In my case I created my own extension methods to convert my Comment domain model to my CommentViewModel like this:
public static class ViewModelConverters
{
public static CommentViewModel ToCommentViewModel(this Comment comment)
{
return new CommentViewModel()
{
Content = comment.Content,
DateCreated = comment.DateCreated.ToShortDateString()
};
}
public static IEnumerable<CommentViewModel> ToCommentViewModelList(this IEnumerable<Comment> comments)
{
List<CommentViewModel> commentModels = new List<CommentViewModel>(comments.Count());
foreach (var c in comments)
{
commentModels.Add(c.ToCommentViewModel());
}
return commentModels;
}
}
Basically what I do is perform a standard EF query to bring back a domain model and then use the extension methods to convert the results to a view model. For example, the following methods illustrate the usage:
public Comment GetComment(int commentId)
{
return CommentRepository.GetById(commentId);
}
public CommentViewModel GetCommentViewModel(int commentId)
{
return CommentRepository.GetById(commentId).ToCommentViewModel();
}
public IEnumerable<Comment> GetCommentsForPost(int postId)
{
return CommentRepository.GetCommentsForPost(postId);
}
public IEnumerable<CommentViewModel> GetCommentViewModelsForPost(int postId)
{
return CommentRepository.GetCommentsForPost(postId).ToCommentViewModelList();
}
Talking about Models, ViewModels and DTOs is confusing, personally I don't like to use these terms. I prefer to talk about Domain Entities, Domain Services, Operation Input/Result (aka DTOs). All of these types live in the Domain layer. Operations is the behavior of Entities and Services. Unless you are building a pure CRUD application the presentation layer only deals with Input/Result types, not Entities. You don't need additional ViewModel types, these are the ViewModels (in other words, the Model of the View). The View is there to translate the Operation Results to HTML, but the same Result could be serialized as XML or JSON. What you use as ViewModel is part of the domain, not the presentation layer.
I've been getting several errors:
cannot add an entity with a key that is already in use
An attempt has been made to attach or add an entity that is not new, perhaps having been loaded from another datacontext
In case 1, this stems from trying to set the key for an entity versus the entity. In case 2, I'm not attaching an entity but I am doing this:
MyParent.Child = EntityFromOtherDataContext;
I've been using using the pattern of wrap everything with a using datacontext. In my case, I am using this in a web forms scenario, and obviously moving the datacontext object to a class wide member variables solves this.
My questions are thus 2 fold:
How can I get rid of these errors and not have to structure my program in an odd way or pass the datacontext around while keeping the local-wrap pattern? I assume I could make another hit to the database but that seems very inefficient.
Would most people recommend that moving the datacontext to the class wide scope is desirable for web pages?
Linq to SQL is not adapted to disconnected scenarios. You can copy your entity to a DTO having a similar structure as the entity and then pass it around. Then copy the properties back to an entity when it's time to attach it to a new data context. You can also deserialize/reserialize the entity before attaching to a new data context to have a clean state. The first workaround clearly violates the DRY principle whereas the second is just ugly. If you don't want to use any of these solution the only option left is to retrieve the entity you're about to modify by its PK by hitting the DB. That means an extra query before every update. Or use another ORM if that's an option for you. Entity Framework 4 (included with .NET 4) with self-tracking entities is what I'm using currently on a web forms project and everything is great so far.
DataContext is not thread-safe and should only be used with using at the method level, as you already do. You can consider adding a lock to a static data context but that means no concurrent access to the database. Plus you'll get entities accumulated in memory inside the context that will turn into potential problems.
For those that came after me, I'll provide my own take:
The error "an attempt has been made to add or attach an entity that is not new" stems from this operation:
Child.Parent = ParentEntityFromOtherDataContext
We can reload the object using the current datacontext to avoid the problem in this way:
Child.Parent = dc.Entries.Select(t => t).Where(t => t.ID == parentEntry.ID).SingleOrDefault();
Or one could do this
MySubroutine(DataContext previousDataContext)
{
work...
}
Or in a web forms scenario, I am leaning to making the DataContext a class member such as this:
DataContext _dc = new DataContext();
Yes, the datacontext is suppose to represent a unit of work. But, it is a light-weight object and in a web forms scenario where a page is fairly transient, the pattern can be changed from the (using dc = new dc()) to simply using the member variable _dc. I am leaning to this last solution because it will hit the database less and require less code.
But, are there gotchas to even this solution? I'm thinking along the lines of some stale data being cached.
What I usually do is this
public abstract class BaseRepository : IDisposable
{
public BaseRepository():
this(new MyDataContext( ConfigurationManager.ConnectionStrings["myConnection"].ConnectionString))
{
}
public BaseRepository(MyDataContext dataContext)
{
this.DataContext = dataContext;
}
public MyDataContext DataContext {get; set;}
public void Dispose()
{
this.DataContext.Dispose();
}
}
Then imagine I have the following repository
public class EmployeeRepository : BaseRepository
{
public EmployeeRepository():base()
{
}
public EmployeeRepository(MyDataContext dataContext):base(dataContext)
{
}
public Employee SelectById(Guid id)
{
return this.DataContext.Employees.FirstOrDefault(e=>e.Id==id);
}
public void Update(Employee employee)
{
Employee original = this.Select(employee.Id);
if(original!=null)
{
original.Name = employee.Name;
//others
this.DataContext.SubmitChanges();
}
}
}
And in my controllers (I am using asp.net mvc)
public ActionResult Update(Employee employee)
{
using(EmployeeRepository employeeRepository = new EmployeeRepository())
{
if(ModelState.IsValid)
{
employeeRepository.Update(employee);
}
}
//other treatment
}
So the datacontext is properly disposed and I can use it across the same instance of my employee repository
Now imagine that for a specific action I want the employee's company to be loaded (in order to be displyed in my view later), I can do this:
public ActionResult Select(Guid id)
{
using(EmployeeRepository employeeRepository = new EmployeeRepository())
{
//Specifying special load options for this specific action:
DataLoadOptions options = new DataLaodOptions();
options.LoadWith<Employee>(e=>e.Company);
employeeRepository.DataContext.LoadOptions = options;
return View(employeeRepository.SelectById(id));
}
}
I finished NerdDinner tutorial and now I'm playing a bit with project.
Index page shows all upcoming dinners:
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
return View(dinners);
}
In DinnerRepository class I have method FindAllDinners and I would like to add to above Index method number of all dinners, something like this:
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
var numberOfAllDinners = dinnerRepository.FindAllDinners().Count();
return View(dinners, numberOfAllDinners);
}
Of course, this doesn't work. As I'm pretty new to OOP I would need help with this one.
Thanks,
Ile
Create view model:
public class DinnerViewModel
{
public List<Dinner> Dinners { get; set; }
public int NumberOfAllDinners { get; set; }
}
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
var numberOfAllDinners = dinnerRepository.FindAllDinners().Count();
return View(new DinnerViewModel { Dinners = dinners, NumberOfAllDinners = numberOfAllDinners } );
}
You need to create a "wrapper" object that contains the objects you wish to pass as public properties of it. For instance, create an object called DinnerViewModel and give it two properties and set these with two properties, one a List called Dinners and one an int called DinnerCount. Then pass the DinnerViewModel to the view and you can then access Model.Dinners and Model.DinnerCount
In your case I would prefer the solution mentioned by LukLed.
In general you could of course also transfer multiple values from your controller to your view using ViewData:
ViewData["dinners"] = dinners;
ViewData["numberOfAllDinners"] = 150;
...
For more information also take a look at this link.
Just simply use dinners.Count property instead.
Remember, you start off using the ViewData inherts in you .aspx filesand returning the same in you return statements. Because of that, I figure that it was an issue with the Inherits attribute on the top of the ASP.NET files. But, if you are getting the error when trying to create or edit a new Dinner when you are on the 'Upcoming Dinners' page (generated from the Details.aspx and the LINQ file that gets all Dinners that are after todays date), go into your 'Controllers' directory, specifically the DinnerController.cs. Then look at the Edit and or Create methods. the answer lies right here. If you put breakpoints on these methods, you should be able to figure it out. If not, continue reading:
Look where it fails, the 'return...' line. Maybe I am the only person who forgot to change this, but my error is the same as people are getting in this page and this os how I fixed it.....the 'return(dinner)' line, in Create and Edit (and any others that you are having issues with), they are using the NerDinner.Model.Dinner / ViewData method. However, if you change it to the ViewModel return method instead, it should fix it, For example: 'return(new DinnerFormViewModel(dinner));', it should work for you. I hope this helps, as it was what my issue was. Just a simple overlook.