I have a create method that works fine. Within this create, an image is saved to a separate table. Within my Person class I have a collection of files
public virtual ICollection<File> Files { get; set; }
When I use the create method, I have this line that works fine.
db.Persons.Add(person);
This will create the person record and add the file as well. I can then view the file later.
If however, I want to change the file and I use edit, I can update all other information except for the image file. This is what I'm using to update within the edit.
db.Entry(person).State = EntityState.Modified;
Why is this? I can recreate the record but I have other relations hanging off the person table and it seems more difficult to add these back. Am I missing something fundamental, have I provided enough info?
Thanks all
Related
I have 2 classes, like the below.
They can have very large collections - a Website may have 2,000+ WebsitePages and vice-versa.
class WebsitePage
{
public int ID {get;set;}
public string Title {get;set;}
public List<Website> Websites {get;set;}
}
class Website
{
public int ID {get;set;}
public string Title {get;set;}
public List<WebsitePage> WebsitePages {get;set;}
}
I am having trouble removing a WebsitePage from a Website. Particularly when removing a WebsitePage from mutliple Websites.
For example, I might have code like this:
var pageToRemove = db.WebsitePages.FirstOrDefault();
var websites = db.Websites.Include(i => i.WebsitePages).ToList();
foreach(var website in websites)
{
website.WebsitePages.Remove(pageToRemove)
}
If each website Include() 2k pages, you can imagine it takes ages to load that second line.
But if I don't Include() the WebsitePages when fetching the Websites, there is no child collection loaded for me to delete from.
I have tried to just Include() the pages that I need to delete, but of course when saving that gives me an empty collection.
Is there a recommended or better way to approach this?
I am working with an existing MVC site and I would rather not have to create an entity class for the join table unless absolutely necessary.
No, you can't... normally.
A many-to-many relationship (with a hidden junction table) can only be affected by adding/removing items in the nested collections. And for this the collections must be loaded.
But there are some options.
Option 1.
Delete data from the junction table by raw SQL. Basically this looks like
context.Database.ExecuteSqlCommand(
"DELETE FROM WebsiteWebsitePage WHERE WebsiteID = x AND WebsitePageID = y"));
(not using parameters).
Option 2.
Include the junction into the class model, i.e. map the junction table to a class WebsiteWebsitePage. Both Website and WebsitePage will now have
public ICollection<WebsiteWebsitePage> WebsiteWebsitePages { get; set; }
and WebsiteWebsitePage will have reference properties to both Website and WebsitePage. Now you can manipulate the junctions directly through the class model.
I consider this the best option, because everything happens the standard way of working with entities with validations and tracking and all. Also, chances are that sooner or later you will need an explicit junction class because you're going to want to add more data to it.
Option 3.
The box of tricks.
I tried to do this by removing a stub entity from the collection. In your case: create a WebsitePage object with a valid primary key value and remove it from Website.WebsitePages without loading the collection. But EF doesn't notice the change because it isn't tracking Website.WebsitePages, and the item is not in the collection to begin with.
But this made me realize I had to make EF track a Website.WebsitePages collection with 1 item in it and then remove that item. I got this working by first building the Website item and then attaching it to a new context. I'll show the code I used (a standard Product - Category model) to prevent typos.
Product prd;
// Step 1: build an object with 1 item in its collection
Category cat = new Category { Id = 3 }; // Stub entity
using(var db = new ProdCatContext())
{
db.Configuration.LazyLoadingEnabled = false;
prd = db.Products.First();
prd.Categories.Add(cat);
}
// Step 2: attach to a new context and remove the category.
using(var db = new ProdCatContext())
{
db.Configuration.LazyLoadingEnabled = false;
db.Products.Attach(prd);
prd.Categories.Remove(cat);
db.SaveChanges(); // Deletes the junction record.
}
Lazy loading is disabled, otherwise the Categories would still be loaded when prd.Categories is addressed.
My interpretation of what happens here is: In the second step, EF not only starts tracking the product when you attach it, but also its associations, because it 'knows' you can't load these associations yourself in a many to many relationship. It doesn't do this, however, when you add the category in the first step.
In my ASP MVC project, whenever I add a new table to my project two models are created. One under the Text Templating Transformation Toolkit (file w/.tt extension), and another outside this in the general Models folder (this one seems to want to default with a "1" at the end of the file).
Whenever a change is made to the database, it seems all the models under the .tt file are refreshed according to the fields currently existing in the database on that table. The files outside this is the general Models folder, however are left as they were. Because of this, we have put all of our model validation methods in these .cs files (with the "1" at the end of the file name).
However, in one instance I needed to remove a field from the model under the .tt file in order to perform a validation in the other .cs file. This is the validation I perform:
private string agentId;
//here, AgentId overrrides the field in the BankListAgentId.cs file,
//which MUST be commented out.
[DisplayName("Agent ID")]
public string AgentId
{
get { return agentId; }
set { agentId = this.scrubAgentId(value); }
}
Is there a way to specify in this .cs file that the AgentId field here (the file with the "1" at the end of the name) overrides the AgentId field in the.csfile found under the.tt` file?
As far as I know it can't be done. But there are few tricks that can you do.
Change name of your property to eg. InternalAgentId and also change in EDMX that field to be protected or even private. Then in your code reference to InternalAgentId:
public string AgentId
{
get { return this.InternalAgentId; }
set { this.InternalAgentId = this.ScrubAgentId(value); }
}
If you need that property in linq you can look at Microsoft Linq Translation
Another way is to change your T4 to generate partial methods and invoking it inside generated properties. I think that one of Entity Framework template already do that.
I was playing around with Entity based on this article in Visula Studio Express 2012: http://msdn.microsoft.com/en-us/data/gg685494.aspx
Had to completely re do this after some findings:
According to the article you can create database, this is correct but classes are not created unless you right click on an empty part in the diagram choose properties and select something for "code generating strategies" (I only had default). After that my project didn't build anymore because of errors. Trying to do it again an a newly created project didn't crate the model classes anymore (not sure what I did to make it work the first time).
After creating products in the diagram, create a database and then use that database in a second project to create models the products.cs file is created but without annotations (I had product name as 200 char max but it's not in the products.cs file).
After scaffolding controllers and views in the second project (making models from database created from models in the first project) the product name is incorrectly validated to 200 char max. Even though the designer shows 200 max the products.cs file is missing annotation.
If I am doing something wrong here could anyone explain to me how to create product.cs file from an Entity diagram and still be left with a buildable project?
Is it normal for model classes created from database to have no annotation (for example max 200 char field)?
I like Entity and the idea of it. Lots of documentation out there with videos but for now I'll go out and buy a hat because will pull all my hair out trying to get used to way it works :-)
It's cool to create database with entity classes using the designer but creating the classes from the designer or from the database is not (yet) working properly.
No annotations so no checks for null or anything. I manually created the classes and added annotation for product name:
public int Id { get; set; }
[DisplayName("Product name"),
Required(ErrorMessage = "Product Name is required"),
StringLength(10)]
public string Name { get; set; }
Then right click controllers folder in solution explorer and choose => add => controller => using entity framework as a template to create crud views.
For some reason here I have to create a data context class that creates yet another connection string and database.mdf file but ... form validation works perfectly in JavaScript and on server.
I have a Note domain object which belongs to a Document object. Only an owner of a Document can add Notes so in the Document class there is a canUserAccess() method. In my service layer I can call canUserAccess() to ensure a user only adds Notes to Documents they own.
This works well for create but I have hit a problem with my Note edit action. On post, the viewmodel is mapped to a Note object, providing it with a DocumentID. Problem is, a malicious user could send in a DocumentID on which they do have permission and thus edit a Note belonging to a Document they don't. In my service layer I cannot reliably use the supplied DocumentID yet I need to get the related DocumentID in order to verify that the user can edit the Note. This is an example:
public void editNote(Note note)
{
note.Document = documentRepository.Find(note.DocumentID);
if(note.Document.canUserAccess())
}
How do I get around this? It seems I need to avoid passing the DocumentID with the edit viewmodel but how do I hydrate the related Document in the service layer? There is probably a really simple solution to this and I am just tying myself up in circles!
You do this with BindAtrribute for the model or for the action method by adding a white list with the properties you want to be bound :
for the model
[Bind(Include="NoteText,NoteTitle")]
public Model{}
for the action method
public ViewResult Edit([Bind(Include="NoteText,NoteTitle"]Note note)){}
or use a black list for the properties you don't want to bind :
[Bind(Exclude="DocumentID")]
public Model{}
I would personally use white list with the model class. You might find this article interesting. The last section for under-posting is your case.
Then you don't have the documentID passed, but in your action you can do this:
public ViewResult Edit(Note note)
{
Note updateNote = nodesRep.Get(note.noteID);
if(updateNote.Document.canUserAccess())
{
//replace the values of the updateNote
//you can place this line in your base Repository class
context.Entry<Note>(updateNote).CurrentValues.SetValues(note); //I assume EF 4.1
}
}
Hey, sorry for my bad english...
Using EF4 code-only, I have created some POCO classes and my database was generated from that. All worked fine, I inserted some data on the tables but now I need to create a new property on one of my classes. If i just create it, the application give me the exception:
{"The model backing the 'TestContext' 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 RecreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data."}
I tried to create manually the field on the table, but it is still complaining... does someone know if there is a way and if so how can I manually update the database schema (i don't want to recreate it because i already have data) to match the model?
It should work if you make sure that your new column match exactly your new property.
For example, if you add a property NewProp
public class MyEntity
{
[Key]
public int Id;
public string PropA;
public int PropB;
public int NewProp;
}
then in your database table MyEntities, you would add a column NewProp of type int not null.
What you can do to check if the column you add is correct, is to rename your database, then let Code-First recreate the database and see what's different between the original and new databases.
EF generates partial classes. So you can add new properties(fields) in another partial class.