I am trying to create foreign key relationship using Entity Framework Code first approach.
I am creating 1-M relation b/w Vehicle and FileUpload (user can upload multiple images against one vehicle).
Problem: I always get FK value 0 and Vehicle navigation property null when saving file information.
Please help to understand what i am doing wrong?
Following are model classes with FK relation defined.
Vehicle model
public class Vehicle
{
public Vehicle()
{
FileUploads = new List<FileUpload>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<FileUpload> FileUploads { get; set; }
}
File Upload Model
public class FileUpload
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public int Vehicle Id { get; set; }
[ForeignKey("Vehicle Id")]
public virtual Vehicle Vehicle { get; set; }
[Required]
[MaxLength(250)]
[Display(Name = "File name")]
public string FileName { get; set; }
[Required]
public string FileName{ get; set; }
}
My post method inside controller
public HttpResponseMessage Post()
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = HttpContext.Current.Server.MapPath("~/Images/" + postedFile.FileName);
postedFile.SaveAs(filePath);
_saveToDB(postedFile.FileName);
}
return Request.CreateResponse(HttpStatusCode.Created);
}
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
private void _saveToDB(string fileName)
{
FileUpload data = new FileUpload();
data.FileName = fileName;
db.FileUpload.Add(data); // here data.VehicleId is always 0 and navigation property null. At this stage Vehicle record has been already created in database.
db.SaveChanges();
}
data.VehicleId is 0 because data is a local variable you created.
FileUpload data = new FileUpload();
This is just C# code, and Entity Framework Code First has no knowledge about what VehicleId, these files need to be saved against.
Essentially, you need a VehicleId in your POST input or from the snippet that creates the Vehicle record. (to know the Vehicle againts which these images need to be uploaded)
Once you have the VehicleId, set it on the data object.
FileUpload data = new FileUpload();
data.FileName = fileName;
data.VehicleId = vehicleId; // get this from POST input or the snippet that created the Vehicle record.. so vehicle.Id
db.FileUpload.Add(data);
db.SaveChanges();
Related
I am new to C# and .NET, I am trying to implement CRUD functionality with local database. So I created a Product class which contains attributes and and foreign keys. But I get an error when I launch the application. The methods Update() and Delete() do not work, but Add() method works fine.
Here are my generic methods -
public class GenericRepository<T> where T : class
{
GPContext context;
IDbSet<T> dbSet;
public GenericRepository()
{
context = new GPContext();
dbSet = context.Set<T>();
}
public IEnumerable<T> getAll()
{
return dbSet.AsEnumerable();
}
public T GetById(int id)
{
return dbSet.Find(id);
}
public void Add(T t)
{
dbSet.Add(t);
context.SaveChanges();
}
public void Update(T t)
{
dbSet.Attach(t);
//on modifie la instance avant la inserer dans la bd
context.Entry(t).State = EntityState.Modified;
context.SaveChanges();
}
public void Delete(T t)
{
dbSet.Remove(t);
context.SaveChanges();
}
}
and here is how I call them in Program.cs -
GenericRepository<Product> productRepository = new GenericRepository<Product>();
Address adresse = new Address() { City = "Paris", StreetAdress = "Escalier 9" };
Category cat1 = new Category() { Name="cat1" };
Chemical p1 = new Chemical() { ProductId=26, DateProd = DateTime.Now , myAdress = adresse, Description = "CHemichal Product",
Price = 55,Name="PHARMA CHEMICH 1", Quantity = 100 , MyCategory = cat1};
Chemical p2 = new Chemical() {ProductId = 12 , DateProd = DateTime.Now , myAdress = adresse, Description = "CHemichal Update Product",
Price = 55,Name="PHARMA CHEMICH 1 Update", Quantity = 100 , MyCategory = cat1};
Product p3 = new Product() { ProductId = 11 };
productRepository.Add(p1);
productRepository.Update(p2);
productRepository.Delete(p3);
Following error occurs when I launch the application -
System.InvalidOperationException : 'A referential integrity constraint
violation occurred: The property value(s) of 'Category.CategoryId' on
one end of a relationship do not match the property value(s) of
'Product.categoryId' on the other end.'
Also I have this error in Delete method -
System.InvalidOperationException : 'The object cannot be deleted
because it was not found in the ObjectStateManager.'
The entity classes are below -
Product:
public class Product : Concept
{
[Display(Name ="Date de prod")]
[DataType(DataType.Date)]
public DateTime DateProd { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Required(ErrorMessage ="Champ obligatoire")]
[StringLength(25)]
[MaxLength(50 , ErrorMessage ="taille max dans la base 50")]
public string Name { get; set; }
[DataType(DataType.Currency)]
public double Price { get; set; }
public int ProductId { get; set; }
[Range(0,int.MaxValue)]
public int Quantity { get; set; }
[DataType(DataType.ImageUrl) ,Display(Name ="Image")]
public string image { get; set; }
[ForeignKey("categoryId")]
public Category MyCategory { get; set; }
public int? categoryId { get; set; }
public List<Provider> Providers { get; set; }
public override void GetDetails()
{
Console.WriteLine("Product Name:, Price "+ Name + " " + Price);
}
public virtual void GetMyType()
{
Console.WriteLine("Uknown");
}
}
Chemical:
public class Chemical : Product
{
// public string City { get; set; }
public string LastName { get; set; }
// public string StreetAddress { get; set; }
public Address myAdress { get; set; }
public override void GetMyType()
{
System.Console.WriteLine("CHEMICAL");
}
}
Biological:
public class Biological : Product
{
public string Herbs { get; set; }
public override void GetMyType()
{
Console.WriteLine("Biological");
}
}
I am really a beginner and I have no idea about what I should do or change. I have read other answers on the same topic and I did not understand what to do.
First, you are creating 3 new entities and trying to Add, Update and Delete them. A new entity can be Added, but cannot be Updated or Deleted. Think about it, it is an object that you have just created, and it is not in the database yet. How do you expect to Update/Delete it? To Update or Delete an entity it must be an existing one.
Second, you are using newly created (without any Id) Address and Category objects to update myAdress and MyCategory properties. Those two are not primitive properties representing column values like the rest of them, they are related entities with foreign key constraints. If you really want to change/update them, you must set one that is already existing in database with Id. Otherwise, it will violate the foreign key constraint (or, referential integrity constraint), that's exactly is what the first error message is saying.
Try the following, for Update -
// retrieve an existing address from database
Address newAddress = new GenericRepository<Address>().GetById(3);
// retrieve an existing category from database
Category newCategory = new GenericRepository<Category>().GetById(5);
// retrieve an existing product from database
Chemical p2 = new GenericRepository<Product>().GetById(12);
// update/change properties, but don't change the ProductId
p2.DateProd = DateTime.Now;
p2.myAdress = newAddress;
p2.Description = "CHemichal Update Product";
p2.Price = 55;
p2.Name="PHARMA CHEMICH 1 Update";
p2.Quantity = 100;
p2.MyCategory = newCategory;
// now call for update
productRepository.Update(p2);
and for Delete -
// retrieve an existing product from database
Product p3 = productRepository.GetById(11);
// now call for delete
productRepository.Delete(p3);
I'm using ASP.NET C# with entity framework and I'm trying to upload image for a profile and display it.
Here is the relevant part of the View file (Manage.cshtml)
<input type="file" name="form-register-photo" id="form-register-photo" disabled>
Here is the relevant part of the Controller file (Manage.cs)
[HttpPost]
public ActionResult Manage(ManageViewModel manageviewmodel,HttpPostedFileBase upload)
{
TheFoodyContext db = new TheFoodyContext();
User user_to_update = db.Users.SingleOrDefault(s => s.email == manageviewmodel.Email);
if (ModelState.IsValid)
{
if (user_to_update != null && (upload != null && upload.ContentLength > 0))
{
var fileName = Path.GetFileName(upload.FileName);
var path = Path.Combine(Server.MapPath("~/FOODY"), fileName);
user_to_update.email = manageviewmodel.Email;
user_to_update.fname = manageviewmodel.FirstName;
user_to_update.lname = manageviewmodel.LastName;
user_to_update.phone = manageviewmodel.Phone;
user_to_update.photo = path;
user_to_update.address = manageviewmodel.Address;
user_to_update.city = manageviewmodel.City;
user_to_update.postcode = manageviewmodel.PostCode;
user_to_update.district = manageviewmodel.District;
user_to_update.user_type = manageviewmodel.UserType;
user_to_update.status = manageviewmodel.Status;
db.SaveChanges();
return RedirectToAction("Manage");
}
}
return View(manageviewmodel);
}
Within the above controller i have coded for other fields also. So I want to upload the picture among with them. That means from a single button click.
Here is my Model class (ManageViewModel.cs)
public class ManageViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Photo { get; set; }
public string Address { get; set; }
public string City { get; set; }
public int PostCode { get; set; }
public string District { get; set; }
public string UserType { get; set; }
public string Status { get; set; }
}
But for this one photo uploading part did not work properly. So I really don't know how to manage this.
Entity Framework doesn't help you to literally upload an image.
From your code, you only just edit 1 record in Users in database, without actually upload the image to hosting drive.
For simple, you will need to have something like below to store the file in physically:
var path = Path.Combine(Server.MapPath("~/FOODY"), fileName);
upload.SaveAs(newSPath);
You didn't show exactly what is the result after this db.SaveChanges(); show I'm not sure whether your photo path getting any error. My suggestion is add in a try catch block and run the code in Debug, see what will you have in user_to_update.photo after db.SaveChanges();
I'm a newbie to ASP.NET using Entity Framework. I have different models for People, FileType and FilePath. I want to display the image by retrieving the file path from FilPath together with data like name, age, etc. in index view. I made it happen in Detail view, but in index view page I received error as "Value can not be null", which caused by the FilePath in PeopleDB is null.
Below is my code, please help. Thanks.
/Model/PeopleDB.cs
namespace MvcDemo.Models {
public class PeopleDB
{
public PeopleDB()
{
this.FilePaths = new HashSet<FilePath>();
}
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Interests { get; set; }
public ICollection<FilePath> FilePaths { get; set; }
}
public class PeopleDBContext : DbContext
{
public DbSet<FilePath> FilePaths { get; set; }
public DbSet<PeopleDB> People { get; set; }
}
}
/Model/FilePath.cs
namespace Assessment_HC.Models
{
public class FilePath
{
public int FilePathId {get;set;}
[StringLength(255)]
public string FileName {get;set;}
public FileType FileType {get;set;}
public int PersonID {get;set;}
public virtual PeopleDB Person {get;set;}
}
}
Moedel/FileType.cs
namespace Assessment_HC.Models
{
public enum FileType
{
Avatar = 1, Photo
}
}
Here is the controller for index view
//Get: /People/Index
public ActionResult Index()
{
return View(db.People.ToList());
}
In db.People.ToList(), People.FilePath view is null.
In the controller, the detail view is like this, from where I can get the image showing on detail page:
// GET: /People/Details
public ActionResult Details(int id = 0)
{
PeopleDB peopledb = db.People.Find(id);
PeopleDB people = db.People.Include(i => i.FilePaths).SingleOrDefault(i => i.ID == id);
if (peopledb == null)
{
return HttpNotFound();
}
return View(peopledb);
}
Thanks for your help. Let me know if you need more code.
Based on comments, It seems the only thing you should do is changing FilePaths property of your PeopleDB to be virtual to work with Lazy Loading (which is enabled by default):
public virtual ICollection<FilePath> FilePaths { get; set; }
Lazy Loading is enabled by default, and as stated in comments you didn't change it and there is nothing about Lazy Loading in your context constructor, So it seems the problem is in your FilePaths navigation property that is not virtual.
For index action:
return View(db.People.ToList());
For details action its better to do like:
var people = db.People.Where(x => x.ID == id).FirstOrDefault();
if (people == null)
{
return HttpNotFound();
}
return View(people );
But any way, If disable lazy Loading, you should use Include to include your navigation property in result. In this situation you can load data in your index action use:
db.People.Include(x => x.FilePaths).ToList()
or
//Remember to add using System.Data.Entity;
db.People.Include("FilePaths").ToList()
And to disable Lazy Loading you can
db.Configuration.LazyLoadingEnabled = true;
Or in the constructor of your context:
this.Configuration.LazyLoadingEnabled = false;
More information:
Loading Related Entities
Lazy loading is the process whereby an entity or collection of
entities is automatically loaded from the database the first time that
a property referring to the entity/entities is accessed. When using
POCO entity types, lazy loading is achieved by creating instances of
derived proxy types and then overriding virtual properties to add the
loading hook.
I've tested the code, the only one thing that you need is enabling Eager loading using Include method:
public ActionResult Index()
{
var _db = new ApplicationDbContext();
var model = _db.People.Include("FilePaths").ToList();
return View(model);
}
In this case all related file paths will be loaded.
You can also make FilePaths as virtual:
public virtual ICollection<FilePath> FilePaths { get; set; }
And change your query this way:
var model = _db.People.ToList();
In both cases, all related file paths will be loaded.
I am creating a simple blogging application to get .NET MVC 4 down and I am having a problem. Everything works except for when I try to tag a blog using an array of strings for each blog like so:
public class BlogEntry
{
public List<Comment> BlogComments { get; set; }
public virtual List<String> RawTags { get; set; }
public virtual List<Tag> BlogTags { get; set; }
public virtual User Author { get; set; }
public int AuthorId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime DatePosted { get; set; }
[Key]
public int Id { get; set; }
public bool IsAcceptingComments { get; set; }
public bool IsVisible { get; set; }
public DateTime LastEdited { get; set; }
}
public class Tag
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RefCount { get; set; }
}
Upon creating a blog and tagging it, I save tags into the BlogEntry model using this:
[HttpPost]
public int Create(string data)
{
if (data != null)
{
BlogEntry newBlog = JsonConvert.DeserializeObject<BlogEntry>(data);
newBlog.Author = Session["user"] as User;
newBlog.AuthorId = newBlog.Author.Id;
newBlog.IsVisible = true;
newBlog.IsAcceptingComments = true;
newBlog.LastEdited = DateTime.Now;
newBlog.DatePosted = DateTime.Now;
newBlog.BlogTags = new List<Tag>();
foreach (String s in newBlog.RawTags)
{
// First check to see if the tag already exists
Tag check = Db.Tags.Where(m => m.Name == s).FirstOrDefault();
if (check != null)
{
check.RefCount++;
newBlog.BlogTags.Add(check);
Db.Tags.Attach(check);
Db.Entry(check).State = System.Data.Entity.EntityState.Modified;
Db.SaveChanges();
}
else
{
// Create a new tag
Tag newTag = new Tag();
newTag.Name = s;
newTag.RefCount = 1;
newBlog.BlogTags.Add(newTag);
Db.Tags.Add(newTag);
}
}
Db.BlogEntries.Add(newBlog);
Db.SaveChanges();
return newBlog.Id;
}
return -1;
}
First I do a check to see if a tag already exists.. If it does, I try to add the same tag, check to the newBlog object. I would have thought that this would just save a reference to this Tag object in the DbSet, however, if I create multiple blogs posts with the tag "html" and then run a query to see what blogs have the html tag, only the most recently tagged blog retains this value.... What can I do so that I can have multiple BlogEntry objects with the same Tag object in the database?
I don't have my dev machine in front of me right now, so this is just a guess, but I figure it's better than making you wait until tomorrow...
I don't think you need the last 3 lines in your if(check!=null) and in fact, I wonder if they aren't messing you up:
Db.Tags.Attach(check);
Db.Entry(check).State = System.Data.Entity.EntityState.Modified;
Db.SaveChanges();
You shouldn't need to attach because you got it from the Db object already, so it should already be being tracked. This means you don't need to change the state and as for the SaveChanges, you are going to do that below.
And now another disclaimer: I've done some work with Entity Framework (version 6, if you want to know), but not with MVC, so it may be different, but my understanding is that it is better to create a new DbContext for each set of instructions, rather than having a class variable that just tracks running changes. I'm not sure if that is what you are doing or not, but it sort of looks that way from this code sample. Assuming that is relevant in MVC, you may consider creating a new DbContext (Db) at the top of your create method.
Let me know how it goes--if this doesn't help, I'll delete this answer.
First you would have to update the Tag class so that it can track its registered blog entries itself. Here the BlogEntry and Tag classes have a many-to-many relationship. So the Tag class would look like below:
public class Tag
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RefCount { get; set; }
public virtual List<BlogEntry> BlogEntries { get; set; } // MODIFICATION
}
Now you have to add the blog entry to all of its tags for back referencing to meet your query in an easy way. Look for the modifications I have made in the for-loop below:
foreach (String s in newBlog.RawTags)
{
// First check to see if the tag already exists
Tag check = Db.Tags.Where(m => m.Name == s).FirstOrDefault();
if (check != null)
{
check.RefCount++;
check.BlogEntries.Add(newBlog); // MODIFICATION
newBlog.BlogTags.Add(check);
Db.Tags.Attach(check);
Db.Entry(check).State = System.Data.Entity.EntityState.Modified;
Db.SaveChanges();
}
else
{
// Create a new tag
Tag newTag = new Tag();
newTag.Name = s;
newTag.RefCount = 1;
newTag.BlogEntries = new List<BlogEntry>(); // MODIFICATION
newTag.BlogEntries.Add(newBlog); // MODIFICATION
newBlog.BlogTags.Add(newTag);
Db.Tags.Add(newTag);
}
}
To see what blogs have the html tag, you just have to query on the Tag class and search through the BlogEntries to get the desired blogs. Good luck!
I'm wondering what the best practice for modelling by using references would be given situation under. I'm using MongoRepository library.
public class User : Entity
{
publis string Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
public class Post : Entity
{
public string Id { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public DateTime Added { get; set; }
public User Owner { get; set; }
}
When storing the Post I want only reference to Owner (User) object instead of whole object underlying.
Currently I'm doing it like this, not knowing of better way...
var post = new Post
{
Title = "Example title",
Summary = "asd asd",
Added = DateTime.Now,
Owner = new User { Id = "someExistingUserId" }
};
postRepository.Update(post); //Save
..
//Then to get the post
var post = postRepository.GetById("previouslySavedPostId");
post.Owner = userRepository.GetById(post.Owner.Id);
return post;
userRepository and postRepository are of MongoRepository type.
Is this the correct approach to solving my problem using MongoDB with C#/MVC(4)?
You can use MongoDBRef object instead of User object.
public class Post : Entity
{
public string Id { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public DateTime Added { get; set; }
public MongoDBRef Owner { get; set; }
}
Then you can:
var mongo = new Mongo(config.BuildConfiguration());
mongo.Connect();
var DB = mongo.GetDatabase(_dataBaseName)
var post = new Post();
post.Owner = new MongoDBRef("User", userId); // First parameter is a mongoDB collection name and second is object id
// To fetch object referenced by DBRef you should do following
var owner = DB.FollowReference<User>(post.Owner);
Mongo is a document database and if you are used to using sql server it requires a slightly different way of thinking.
As you don't want the user password details in every single post, the way i would probably do it is to create a new class to contain any user info that might be required to display a post.
public class PostOwnerInfo
{
public string UserId { get; set; }
public string Name { get; set; }
}
Update your post entity, replacing the Owner property with an OwnerInfo property, of type PostOwnerInfo.
Then when you create a new post, do the following.
var user = userRepository.GetById(someExistingUserId);
var post = new Post
{
Title = "Example title",
Summary = "Example summary",
Added = DateTime.Now,
OwnerInfo = new PostOwnerInfo
{
UserId = user.Id,
Name = user.Name
}
};
postRepository.Update(post);
This way when you query for a post it will have all the user info that you require to display the post in it's OwnerInfo property with no further queries required.
var post = postRepository.GetById(previouslySavedPostId);
// post.OwnerInfo will contain user info
There is a certain amount of data redundancy, but in an document database this is how i would do it.
If you need the full user info for any reason just do a seperate query for it as you were doing before.
The idea is that you store all the user info you need for a post in a child document of the post, so you shouldn't need to do a seperate query for the user.
If the user data chages, just update the UserInfo field on all posts made by your user.
Your user data will rarely change, but you will query for posts very often.