model and table change error EF 4.1 - asp.net

I have following model in my MVC3 application and I am using entity frame 4.1 code first.
public class Movie
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Director { get; set; }
[CustomValidation(typeof(MovieValidator), "ValidateReleaseDate")]
public DateTime ReleaseDate { get; set; }
}
Noe, If I remove the Required attribute from Title and Director and make them nullable in database (which is automatically created by EF code first from the above model), I get following error:
The model backing the 'MoviesDB'
context has changed since the database
was created. Either manually
delete/update the database, or call
Database.SetInitializer with an
IDatabaseInitializer instance. For
example, the
DropCreateDatabaseIfModelChanges
strategy will automatically delete and
recreate the database, and optionally
seed it with new data.
I have read that to solve this I have to do one of the following things:
1) remove db so that EF can recreate it
2) add following lines in context class: ( this doesnt work in context with EF 4.1, I added in application_start and it works. Why doesnt it work in context calss now ?)
Database.SetInitializer<MoviesDB>(
new DropCreateDatabaseIfModelChanges<MoviesDB>());
3) match table with model ( my model is exactly same as table why I still get this error ?)
Please suggest answers of questions mentioned in point 2 and 3.
Thanks.

Setting initializer should work for example in context's constructor but it is not needed to call every time you initialized context - you need to do it only once so calling it during application startup is a good approach.
Manully updating database doesn't work if you are using automatic change detection (default behavior). When context creates a model it computes a hash from this model and compares it with value stored in EdmMetadata table (table is created first time the EF creates the database). If the value loaded from database is different EF either starts database recreation (if initializer is configured) or throws the exception you see. Manually changing the database will not modify hash stored in EdmMetadata so the model will still think that the database is unchanged.
This behavior can be removed by removing IncludeMetadataConvention:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
model.Conventions.Remove<IncludeMetadataConvention>();
}
After removing this convention EF will not use EdmMetadata table and will not check changes in the model. Updating the database will be completely up to you.

Related

Identity MySQL EF Pomelo Core 2.2 - Two user tables to one UserId FK relationship not displaying user results

I have three MySQL tables that all appear to have been generated correctly with .Net Core 2.2. code first with Pomelo/EF framework:
AspnetUsers with PK=UserId (default Identity Users table with Id
changed to UserId)
UserSetting with PK=UserId
I originally had these as one User table on my last project and wanted to leave Identity alone as much as possible and so I created a second table to hold other user settings.
The problem exists on my third table called Incident with this FK relationship currently:
public class Incident
{
[Key]
public int IncidentId { get; set; }
...
public string UserId { get; set; }
...
[ForeignKey("UserId")]
public virtual IdentityUser User { get; set; }
[ForeignKey("UserId")]
public virtual UserSetting UserSetting { get; set; }
}
So, ideally I'll be able to pull values from both the AspnetUsers table and the UserSetting table to form one complete user. However, when trying to do this:
IQueryable<Incident> incident = _context.Incident.OrderByDescending(d => d.IncidentDate);
And then get at either:
incident.User (e.g. incident.User.Username)
incident.UserSetting (e.g. incident.UserSetting.IPAddress)
I get a NullReferenceException. I think EF is confused or maybe I am... but when I remove the annotations and let EF try to sort it out, I get this error message when running add-migration:
Both relationships between 'Incident.User' and 'IdentityUser' and
between 'Incident.UserSetting' and 'UserSetting' could use {'UserId'}
as the foreign key. To resolve this configure the foreign key
properties explicitly on at least one of the relationships
I tried several things and none the wiser. I've definitely checked that the UserId value matches in all three tables (so it's not null because it's actually null). When I look in the debugger output I don't see any joins being done on the select statement.
I thought about creating a FK relationship between UserSetting and the AspnetUsers table (e.g. incident.User.Usersetting.IPAddress), but again, trying to get away from the tweaking of Identity which appears to be mostly invisible without scaffolding it.
How do I link both my user tables to my Incident table using one UserId property in that Incident table? Is this maybe a bad idea (i.e. should I just go back to using a single user table)? Thanks.
Rookie mistake... I was converting a four year old project from .NET Framework to .NET Core. .NET Framework doesn't require the .Include() statements. So, I simply changed:
IQueryable<Incident> incident = _context.Incident.OrderByDescending(d => d.IncidentDate);
To:
var incident = _context.Incident
.Include(u => u.User)
.Include(u => u.UserSetting)
.OrderByDescending(d => d.IncidentDate);

How to create model variables based on database content

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/

Why isn't server side validation working in my ASP.Net MVC3 application with Entity Framework?

I have an ASP.NET MVC3 application that uses entities generated from a database. Each entity has also has a separate partial class that uses the MetadataType attribute to associate each entity with a class that is decorated with a number of validation attributes (see below).
[MetadataType(typeof(Drawing.Metadata))]
public partial class Drawing
{
private sealed class Metadata
{
[Required]
[StringLength(50, MinimumLength = 3, ErrorMessage = "Drawing numbers must be between {2} and {1} characters in length.")]
[DisplayName("Drawing number")]
public string Number { get; set; }
[Required]
[StringLength(255, MinimumLength = 3, ErrorMessage = "Drawing titles must be between {2} and {1} characters in length.")]
public string Title { get; set; }
}
}
My controller code looks like this:
[HttpPost]
public ActionResult Create(Drawing drawing)
{
if (ModelState.IsValid)
{
// Save to database here...
return RedirectToAction("Index");
}
else
{
return View(drawing);
}
}
I have used the Visual Studio templates to create the views to add, edit and delete the entities (The designer code has not been altered).
The problem I am having is that when I create an entity, validation only works if I have client side validation enabled. If I turn off the client side validation then ModelState.IsValid always seems to return true and returns me to the index page.
Can anyone provide any suggestions on how to get server side validation working with Entity Framework entities?
UPDATE:
It seems this question is similar to mine. The author of this post seems to have solved the problem but rather unhelpfully omitted to mention how they fixed the problem...
I found another solution to this problem. Because I didn't really want to set my properties to nullable I added the following:
[DisplayFormat(ConvertEmptyStringToNull = false)]
Adding the following annotation to your model property fixes the error as well.
After further investigation it seems that my problem is occuring due to a ConstraintException being thrown by my entity class (which inherits from ObjectContext) when the default model binder tries to bind the user input values (Null in this case) to the entity properties.
I can see 2 possible solutions to this:
Relax the constraints on my database tables (I don't want to do this).
Make the entity fields nullable (use the entity designer set the nullable property to yes)
I have used and tested the second option and can confirm that server side validation now works as expected.
Whilst researching solutions to this problem I have come to the conclusion that the problem is due to my entities inheriting from ObjectContext which is quite a heavy class. I found a lot of tutorials which used a code-first approach. In this case, the entity class will inherit from DbContext which is much more lightweight so I guess this could be considered a third solution to the problem.

Entity Framework error - nested model problem

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; }
}

Unable to delete child entities from a POCO using Unit Of Work pattern

I am using POCO classes on an EF4 CTP5 project and I am having trouble deleting child properties. Here's my example (hopefully not too long).
Relevant Portions of the Tour Class
public partial class Tour
{
public Guid TourId { get; private set; }
protected virtual List<Agent> _agents { get; set; }
public void AddAgent(Agent agent)
{
_agents.Add(agent);
}
public void RemoveAgent(Guid agentId)
{
var a = Agents.Single(x => x.AgentId == agentId);
_agents.Remove(Agents.Single(x => x.AgentId == agentId));
}
}
Command Handler
public class DeleteAgentCommandHandler : ICommandHandler<DeleteAgentCommand>
{
private readonly IRepository<Core.Domain.Tour> _repository;
private readonly IUnitOfWork _unitOfWork;
public DeleteAgentCommandHandler(
IRepository<Core.Domain.Tour> repository,
IUnitOfWork unitOfWork
)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
public void Receive(DeleteAgentCommand command)
{
var tour = _repository.GetById(command.TourId);
tour.RemoveAgent(command.AgentId);
// The following line just ends up calling
// DbContext.SaveChanges(); on the current context.
_unitOfWork.Commit();
}
}
Here's the error that I get when my UnitOfWork calls DbContext.SaveChanges()
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
This is happening because EF wont just automatically delete the an Agent entity from the database just because it has been removed from the Agents collection in my Tour class.
I need to explicitly call dbContext.Agents.DeleteObject(a);, but my problem is, I don't have access to the dbContext from within my POCO.
Is there any way to handle this scenario?
With your current architecture I am afraid you need to feed your DeleteAgentCommandHandler with a second repository (IRepository<Core.Domain.Agent>, I guess) and then call something like Delete(command.AgentId) on that second repository.
Or you could extend your IUnitOfWork to be a factory of repositories, so the interface would get an additional method like T CreateRepository<T>() which allows you to pull any instance of your generic repository from the unit of work. (Then you only need to inject IUnitOfWork into the DeleteAgentCommandHandler, and not the repositories anymore.)
Or stay away from generic repositories in your business/UI layer. If Agent is completely dependent on Tour it doesn't need to have a repository at all. A non-generic ITourRepository could have methods to handle the case of removing an agent from a tour in the database layer appropriately.
This does seem like something that should work. I've found this post which suggests this feature is being investigated for future versions:
http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/58a31f34-9d2c-498d-aff3-fc96988a3ddc/
I've also found another post (somewhere - unfortunately I lost it) which suggested adding the parent entity's key to the child entity in your DbContext OnModelCreating method like this:
modelBuilder.Entity<Agent>()
.HasKey(AgentId)
.HasKey(TourId);
Currently this throws an exception at runtime using code-first, although I have got this working when using an EDMX file by hacking the XAML to include the parent key in the store data model as well as the conceptual data model. I think this difference in behaviour is because in the case of the EDMX file, EF trusts that the store metadata it holds is accurate, whereas code-first checks the database to see whether it's model matches.
Another way which may work although I haven't yet tried it yet, is to include the parent key as a compound key in the child table so that code-first is happy. Obviously changing the database or hacking the XAML are both less than ideal and workarounds at best.

Resources