Load Navigation Property by its Key - asp.net-mvc

I am using the EF CF approach and have:
the following data model:
public class Category {
[Key]
public int CategoryID { get; set; }
[Required]
public string CategoryName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
public class Product {
[Key]
public int ProductID { get; set; }
[Required]
public string ProductName { get; set; }
public virtual Category Category { get; set; }
}
the following DataContect definition:
public class EFCFContext : DbContext {
public EFCFContext() : base("EFCFConnection") { }
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
The purpose of this code is as follows:
I have an ASP.NET MVC 4 application. There is a form for editing the Product entity, and a "lookup" selector/editor for the Category entity bound with the Category.CategoryID property.
When submitting the form to the corresponding HttpPost Action, the Product.Category.CategoryName is still empty. I do not have a direct instance of the Category object in this scope:
//Category cat = new Category();
//cat.CategoryName = "Fake Name";
//context.Categories.Add(cat);
//context.SaveChanges();
EFCFContext context = new EFCFContext();
Product prod = new Product();
prod.ProductName = "Fake Name";
prod.Category = new Category();
prod.Category.CategoryID = cat.CategoryID;
//prod.Category.CategoryName is null. ModelState Error is added.
context.SaveChanges();
As a result, some Model Validation errors occur.
I need to assign/reload the Product's Category property explicitly, since I already know the CategoryID.
So my question is:
How do I force the navigation property to load by its Key/ID without the extra steps (and thus avoid the ModelState Errors)?
I may be going in the wrong direction. Any guidance will be helpful.

Related

Viewmodel set up Aspt.net MVC 6

I'm having trouble understanding how to implement a ViewModel in Asp.net MVC, I have the following tables:
Form
ID, Data
Report
ID, FormID, Owner, Category, Status, SubmissionDate
ReportValues
ID, ReportID, Title, Value
I'm looking for a way to display and edit Report and ReportValues in the one ViewModel where ReportValues.ReportID = Report.ID
ReportValues will have multiple entries that relate to a Report.
I have had a look at similiar questions on here and tried following a tutorial ( http://techfunda.com/howto/262/list-data-using-viewmodel ) and coming up empty handed.
If you need any more information let me know and thanks in advance for any replies!
Your View Model is nothing more than a class. You can solve this many ways, but here's an example.
Create your 3 classes like you normally would.
public class Form
{
public int Id { get; set; }
public string Data { get; set; }
}
public class ReportValues
{
public int Id { get; set; }
public int ReportId { get; set; }
public string Title { get; set; }
public string Value { get; set; }
}
public class Report
{
public int Id { get; set; }
public int FormId { get; set; }
public string Owner { get; set; }
public string Category { get; set; }
public string Status { get; set; }
public DateTime SubmissionDate { get; set; }
}
Then, create your ViewModel class to include the three above classes like this.
public class ReportViewModel
{
public Form Form { get; set; }
public ReportValues ReportValues { get; set; }
public Report Report { get; set; }
}
In your view you can access your three classes and their properties as you would in your controller. Model.Form.Id
Depending on your data types, ReportValues will likely be a property of Report, but that's entirely up to your data structure. You will need to populate the classes using whatever method you want (Entity Framework, ADO, etc.) before you can pass them to your view and use them.

Unable to add data into Many-To-Many relationship database table with code first

I have 3 tables that are connected together as follows:
Survey
public enum Language
{
Danish,
English
}
public class Survey
{
public Survey()
{
Id = Guid.NewGuid();
DateCreated = DateTime.Now;
Sort = 0;
}
[Key]
public Guid Id { get; set; }
public bool Active { get; set; }
public string Title { get; set; }
public bool Status { get; set; }
[Display(Name = "Kommentar")]
public string Comment { get; set; }
public string MAilReciever { get; set; }
public string CCMailReciever { get; set; }
public string BBCMailReciever { get; set; }
public int Sort { get; set; }
public DateTime DateCreated { get; set; }
public string StartText { get; set; }
public string EndText { get; set; }
public string RegardText { get; set; }
public Language Language { get; set; }
public virtual ICollection<UserSurvey> UserSurveys { get; set; }
public virtual ICollection<Chapters> Chapters { get; set; }
public virtual ICollection<Questions> Questions { get; set; }
}
ApplicationUser
The default ApplicationUser table from Mvc with identity.
UserSurvey (My relationShipTable between Survey and ApplicationUser)
public class UserSurvey
{
[Key, Column(Order = 0)]
public Guid SurveyId { get; set; }
[Key, Column(Order = 1)]
public string ApplicationUserId { get; set; }
public virtual Survey Survey { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
public bool Active { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string RegardText { get; set; }
}
I'm trying to add a new relation between a user and a servey by the following code:
UserSurvey Item = new UserSurvey();
Item.Survey = s;
Item.ApplicationUser = userinfo;
Item.Active = true;
Item.StartDate = Convert.ToDateTime(dateFrom);
Item.EndDate = Convert.ToDateTime(dateTo);
db.UserSurvey.Add(Item);
db.SaveChanges();
The error message I get says that I cant "Duplicate the SurveyId primary key because it already exist in the database (table survey).
I know it exist but I just want to make a relation between users and surveys, not create a new survey or user.
Thanks for your help
Your problem is that you are providing a completed UserSurvey object with nested objects (Survey/ApplicationUser) populated. When you do that and set the changeset as "Add" (caused by db.UserSurvey.Add(Item);). EF will attempt to issue INSERT INTO for the parent (UserSurvey) and all child/related(Survey/ApplicationUser) tables since it thinks that the "Add" changeset applies to all of these tables. To resolve your issue, you need to just provide the Ids. That will just insert a new relationship:
UserSurvey Item = new UserSurvey();
//Item.Survey = s; //This object filling is making EF believe that you are attempting to add a new record in Survey. Comment it out
//Item.ApplicationUser = userinfo; //This object filling is making EF believe that you are attempting to add a new record in ApplicationUser. Comment it out
Item.ApplicationUserId = userinfo.ApplicationUserId;//or whatever is the id column name in ApplicationUser table/class
Item.SurveyId = s.SurveyId;//I see that you are missing SurveyId property in UserSurvey. You will need to add this. This will be the Foreign Key to Survey table just like you have ApplicationUserId FK to ApplicationUser table.
Item.Active = true;
Item.StartDate = Convert.ToDateTime(dateFrom);
Item.EndDate = Convert.ToDateTime(dateTo);
db.UserSurvey.Add(Item);
db.SaveChanges();
So, the underlying idea is to fill the Ids if the nested records (Survey/Application in this case) already exist. You only populated nested object when you want EF to attempt to INSERT them too for you. Dont populate them if you dont want this. Just the Ids help EF to create just the relationship and not go after creating these related records again for you.
Hope this helps.

Adding dynamic attributes to a model

I can't wrap my mind around this issue and haven't found the correct search keys yet:
I would like to have several categories of items in which all items have specific attributes. Those attributes (text fields, dropdowns, or checkboxes) should be added to a category and I want to edit and save those attributes for each item.
I'm working with MVC 4 and code-first EF5. How can I implement this?
My first approach were several classes like Text, Dropdown that were inherited from an abstract Attribute class and a Category class like this:
public class Category
{
[Key]
public int CategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual ICollection<Attribute> Attributes { get; set; }
}
But then I had no idea to proceed. Am I on the right way or completely wrong? Can someone give me a few hints I can search for?
Edit
Ultimately I'm trying to build a list of hifi devices. Speakers have different attributes than amplifier and those have different attributes to tape recorders. I would like to give a unified look for the details of each device and pre-define specific attributes to that category with an additional free-for-all text area. Speaker XYZ is my item, Speaker my category and dB an attribute.
Ok so this question is basically about the data design.
First, I assume that the rule is:
One item has one category
One category has many attributes
One item has many attributes associated with the category
For rule no.1, it is good enough in your design. (simplified example)
public class Category{
public IEnumerable<Item> Items{get;set;}
}
public class Item{
public Category Category{get;set;}
}
Its clear enough.
For rule no.2, I think you should make a CategoryAttribute class. It holds the relation between one to many Category and Attribute. Basically, CategoryAttribute is a master, whereas the children will be ItemAttribute.
public class Category{
public IEnumerable<CategoryAttribute> CategoryAttributes{get;set;}
}
public class CategoryAttribute{
public Category Category{get;set;}
public string CategoryName{get;set;}
public string DefaultValue{get;set;} // maybe a default value for specific
// attribute, but it's up to you
public IEnumerable<ItemAttribute> ItemAttributes{get;set;}
}
The IEnumerable<ItemAttribute> is the one to many relation between category attribute and item attribute.
For rule no.3, the the ItemAttribute described in rule no.2 will be represented attribute owned by each item.
public class Item{
public IEnumerable<ItemAttribute> ItemAttributes{get;set;}
}
public class ItemAttribute{
public Item Item {get;set;} // it owned by one attribute
public CategoryAttribute{get;set;} // it owned by one category attribute
}
I don't quite sure about how to represent relation or primary and foreign key in code first. Hopefully I can enhance my answer if needed (and if I able). But hopefully my illustration about relations and the class designs for each objects.
I think something like this may work for you...
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual ICollection<Attribute> Attributes { get; set; }
}
public class Item
{
public int ItemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
public virtual ICollection<ItemAttribute> ItemAttributes { get; set; }
}
public class Attribute
{
public int AttributeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<ItemAttribute> ItemAttributes { get; set; }
}
public class ItemAttribute
{
public int ItemId { get; set; }
public int AttributeId { get; set; }
public Item Item { get; set; }
public Attribute Attribute { get; set; }
public string Value { get; set; }
public int ValueInt{ get; set; }
// etc.
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ItemAttribute>()
.HasKey(x => new { x.ItemId, x.AttributeId });
modelBuilder.Entity<ItemAttribute>()
.HasRequired(x => x.Item)
.WithMany(x => x.ItemAttributes)
.HasForeignKey(x => x.ItemId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<ItemAttribute>()
.HasRequired(x => x.Attribute)
.WithMany(x => x.ItemAttributes)
.HasForeignKey(x => x.AttributeId)
.WillCascadeOnDelete(false);
// AttributeCategories is created for you - but you can do the same as ^ above to customize
// just change 'ICollection<Category> Categories' to collection of 'ItemAttribute'
}
// use it like e.g.
var item = new Item { Name = "ItemTest", };
var attribute = new Attribute { Name = "attributeTest", };
item.ItemAttributes = new List<ItemAttribute>
{
new ItemAttribute { Item = item, Attribute = attribute, Value = "test", },
};
var category = new Category
{
Name = "cat1",
Items = new[]
{
item,
new Item{ Name = "Item1", },
new Item{ Name = "Item2", },
new Item{ Name = "Item3", },
new Item{ Name = "Item4", },
new Item{ Name = "Item5", },
},
Attributes = new[]
{
attribute,
new Attribute{ Name = "att1", },
new Attribute{ Name = "att2", },
}
};
db.Categories.Add(category);
db.SaveChanges();
var categories = db.Categories.ToList();
ItemAttribute is used to connect and store values.
And you're going to need to further adjust as per your requirements.
I actually never worked with code first approach, but I can give you some idea about how this scenario can be handled...To me, it looks that Item is the major one instead of Category. So you can have this structure...
public class Category
{
[Key]
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string CategoryDescription { get; set; }
// use attributes here if you want them for Category
//public Dictionary<string, string> ItemnAttributes { get; set; }
}
public class MyItem
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
public string ItemDescription { get; set; }
public Category ItemnCatagory { get; set; }
public Dictionary<string, string> ItemnAttributes { get; set; }
}
Hope this helps..

Validation of Viewmodel containing nested domain objects

I have the following 2 entities:
public class Product
{
[Key]
public int ID { get; set; }
[Required]
public string Name { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
[Key]
public int ID { get; set; }
[Required]
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
and a view model
public class ProductCreateOrEditViewModel
{
public Product Product { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
The create view for Product uses this ViewModel. The category ID is set as follows in the view:
<div class="editor-field">
#Html.DropDownListFor(model => model.Product.Category.ID,new SelectList
(Model.Categories,"ID","Name"))
#Html.ValidationMessageFor(model => model.Product.Category.ID)
</div>
When the form posts I get an instance of the view model with a product and the selected category object set but since the "Name" property of Category has a [Required] attribute the ModelState is not valid.
As far as creating a Product goes I don't need or care for the "Name" property. How can I get model binding to work such that this is not reported as a ModelState error?
You should create a correct ViewModel for your View.
The best approach imo is not to expose your domain entities to the view.
You should do a simple DTO flattening from your entities to your viewmodel.
A class like that
public class ProductViewModel
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
public int CategoryId? { get; set; }
public SelectList Categories { get; set; }
}
From your controller you map the product to your viewmodel
public ViewResult MyAction(int id)
{
Product model = repository.Get(id);
//check if not null etc. etc.
var viewModel = new ProductViewModel();
viewModel.Name = model.Name;
viewModel.CategoryId = model.Category.Id;
viewModel.Categories = new SelectList(categoriesRepo.GetAll(), "Id", "Name", viewModel.CategoryId)
return View(viewModel);
}
Then in the action that respond to the post, you map back your viewModel to the product
[HttpPost]
public ViewResult MyAction(ProductViewModel viewModel)
{
//do the inverse mapping and save the product
}
I hope you get the idea

Entity Framework 4 CTP 5 POCO - Many-to-many or Lookup table?

I'm building a personal blog app with Entity Framework 4 CTP 5 POCO only, where a Post can have many Tags and a Tag can be in many Posts. My question is whether to build a many-to-many model, or to have a lookup table.
At first I was trying to use many-to-many, but I don't know how to do insertion, each time a new post is posted (with many tags selected), I'm not sure what to do so that the tags should be associated with the post (and wouldn't insert a new tag if the tag name already exists.)
I then try to build a Lookup table like so:
Post
public class Post
{
public int Id { get; set; }
[Required]
[StringLength(512, ErrorMessage = "Title can't exceed 512 characters")]
public string Title { get; set; }
[Required]
[AllowHtml]
public string Content { get; set; }
public string FriendlyUrl { get; set; }
public DateTime PostedDate { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<PostTagLookup> PostTagLookups { get; set; }
}
Tag
public class Tag
{
public int Id { get; set; }
[Required]
[StringLength(25, ErrorMessage = "Tag name can't exceed 25 characters.")]
public string Name { get; set; }
public string FriendlyName { get; set; }
public virtual ICollection<PostTagLookup> PostTagLookups { get; set; }
}
PostTagsLookup
public class PostTagLookup
{
public int PostId { get; set; }
public int TagId { get; set; }
}
The problem is I'm not sure how EF are going to handle lookup table (how will I get the Tags of a Post, or a collection of the Posts when I select a Tag). And with the code below, I got an error saying PostTagLookup doesn't have an Id key:
var getPosts = _post.GetLatestPosts(3).ToList();
var posts = from post in getPosts
select new PostModel
{
Id = post.Id,
Title = post.Title,
Content = post.Content,
FriendlyUrl = post.FriendlyUrl,
PostedDate = post.PostedDate,
IsActive = post.IsActive,
NumberOfComments = post.Comments.Count(),
PostTags = post.PostTagLookups.Where(p => p.PostId == post.Id).ToList()
};
Any suggestion on how to accomplish this task? Thank you very much!
I think your model should work as-is with a slight tweak: add an ID column/field to the PostTagLookup entity.
public class PostTagLookup
{
[Key]
[DatabaseGenerated(DatabaseGenerationOption.Identity)]
public int PostTagLookupId { get; set; }
//etc.
}
However, I'm not sure why you wouldn't want EF to handle the underlying many-to-many on it's own. When you have a new Post object, for example, all you have to do is add any associated Tags to the instantiated Post's Tags collection before calling SaveChanges() on your context. Did this not work for you?
I've struggled with it for the past couple days and decided to go with the Lookup table as intended, and in the Lookup table, also have references to Post and Tag model as such:
public class PostTagLookup
{
public int Id { get; set; }
public int PostId { get; set; }
public int TagId { get; set; }
public virtual Post Post { get; set; }
public virtual Tag Tag { get; set; }
}
Might not be the best way but it's working :) Thanks all for looking.

Resources