I have a list of comments as a parameter to each lesson on my website. I have tried a number of different ways (shown below) to retrieve the number of comments. The structure of my Model looks like this:
public class EducateLesson
{
[Key]
public int EducateLessonID { get; set; }
public EducateTopics Topic { get; set; }
public string Title { get; set; }
public string Introduction { get; set; }
public string Body { get; set; }
public string VideoURL { get; set; }
public int Likes { get; set; }
public virtual List<Comment> Comments { get; set; }
public void AddComment(Comment c)
{
Comments.Add(c);
}
}
public class Comment
{
[Key]
public int CommentID { get; set; }
public DateTime Date { get; set; }
public string IdentityUserName { get; set; }
public string Text { get; set; }
}
I have noted that the Comment class has no FK to the lesson object here.
These are the methods I've tried to retrieve the count for an individual lesson in the view:
count = (from l in lesson.Comments select l).Count();
#lesson.Comments.Count()
#lesson.Comments.Count
I solved this answer without changing the model. Comments made on this post were not actually beneficial. Firstly I added this to my repository class:
public int CommentCount(int id)
{
EducateLesson lesson = context.EducateLessons.Find(id);
return lesson.Comments.Count;
}
Next I took the count by creating a repository class and using this method for each lesson object:
int count = 0;
EducateRepository EducateRepository = new EducateRepository();
count = EducateRepository.CommentCount(lesson.EducateLessonID);
Related
I have an object that has an embedded list of objects, which itself has an embedded list of object, like so
namespace AppName.Models
{
[BindProperties]
public class WorkOrder
{
public string OrderNumber { get; set; }
public int FinishedGoodsToMake { get; set; }
public int Quantity { get; set; }
public List<FinishedGood> FinishedGoods { get; set; } = new List<FinishedGood>();
}
[BindProperties]
public class FinishedGood
{
public int ID { get; set; }
public string ProductCode { get; set; }
public List<RawMaterial> RawMaterials{ get; set; } = new List<RawMaterial>();
}
[BindProperties]
public class RawMaterial
{
public string ProductCode { get; set; }
public int Status { get; set; }
public string Description { get; set; }
}
}
In the RawMaterial object, the Status can have a value of 0, 1, 2 or 3. I need to process them grouped by this status. I think it would be easiest if I could just loop through statuses and select the object I need. So, let's say the work order has three FinishedGoods and each FinishedGood has four RawMaterials. I need to get a full WorkOrder object, with FinishedGoods and RawMaterials, only where RawMaterial.Status == 0. Is there any way to do this easily?
I could optionally map the object to iterate through each status also. Just looking for the easiest/ most elegant solution here. Thanks!
I have two tables in my database that have similar data but not same.
I'd like to show these tables in the same view. Looking in internet the most suggested practice is to use a ViewModel class.
So... These are my class tables:
iCareIndoorAlert.cs
[Table("iCareIndoorAlerts")]
public class iCareIndoorAlert
{
[Key]
[DisplayName("ID Allarme iCare indoor")]
public long AlertID { get; set; }
[DisplayName("ID Messaggio")]
public long MessageID { get; set; }
public string UUID { get; set; }
public int MAG { get; set; }
public int MIN { get; set; }
[DisplayName("Distanza")]
public float Dist { get; set; }
[DisplayName("Data ora")]
public DateTime DateTime { get; set; }
public virtual HttpPop3 HttpPop3 { get; set; }
}
iCareOutdoorAlert.cs
[Table("iCareOutdoorAlerts")]
public class iCareOutdoorAlert
{
[Key]
[DisplayName("ID Allarme iCare outdoor")]
public long AlertID { get; set; }
[DisplayName("ID Messaggio")]
public long MessageID { get; set; }
[DisplayName("Latitudine")]
public float Lat { get; set; }
[DisplayName("Longitudine")]
public float Lon { get; set; }
[DisplayName("Accuracy")]
public float Acc { get; set; }
[DisplayName("Data ora")]
public DateTime DateTime { get; set; }
}
After I created a ViewModel folder with a iCareAlertViewModel.cs class:
public List<iCareIndoorAlert> iCareIndoorAlert { get; set; }
public List<iCareOutdoorAlert> iCareOutdoorAlert { get; set; }
And added there rows in the iCareEntities.cs:
public DbSet<iCareIndoorAlert> iCareIndoorAlerts { get; set; }
public DbSet<iCareOutdoorAlert> iCareOutdoorAlerts { get; set; }
public DbSet<iCareAlertViewModel> iCareAlertsViewModels { get; set; }
After I generated the iCareAlertController.cs and run the view Index.cshtml but I get the error "Key not found". So in the ViewModel I added a dummy variable with [Key] attribute and now a get an error that says that can't find the iCareAlertViewModel table...
What am I doing wrong?
Thank you
First, don't create a DbSet for your view model, that's not necessary. A view model is a POCO that sits between your data models (i.e. your entities) and your user interface.
What you probably need to do is define an interface that has the properties that are common to your two entities, so something like this:
public interface IAlert
{
long AlertID { get; set; }
long MessageID { get; set; }
//etc...
}
Now each entity can implement that interface:
public class iCareIndoorAlert : IAlert
{
//snip
}
Now your viewmodel:
public class AlertViewModel : IAlert
{
public long AlertID { get; set; }
public long MessageID { get; set; }
//snip
}
So your view will now be something like this:
#model IEnumerable<My.Assembly.AlertViewModel>
#foreach(var alert in Model)
{
#Html.DisplayFor(m => m.AlertID)
//etc
}
Finally you need something to map your entities to your view model, you can use a library like Automapper but manually, it's something like this (including the action method and return):
public ActionResult Index()
{
List<IAlert> alerts = GetOutdoorAlerts(); //for example
List<AlertViewModel> alertViewModels = alert
.Select(a => new AlertViewModel
{
AlertID = a.AlertID,
MessageID = a.MessageID,
//etc...
});
return View(alertViewModels);
}
Man I am having a heck of a time today trying to get this to work. I think I'm missing something in terms of a navigational property.
My controller. When I put a breakpoint at foo = 5, and I look at the local watch window, "listOfComments" has zero elements even though my database has the information listed(see below)
public ActionResult CommentsList()
{
var post = _db.GetPost(5);
List<Comment> listOfComments = post.Comments.ToList();
var foo = 5;
return View(post);
}
GetPost method
public Post GetPost(int? postId)
{
var context = DataContext;
var post = context.Posts.FirstOrDefault(x => x.Id == postId);
if (post == null)
{
return new Post();
}
return post;
}
Comment class
public class Comment
{
public int Id { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Body { get; set; }
//Navigational
public Post Post { get; set; }
}
Post class
public class Post
{
public Post()
{
Comments = new HashSet<Comment>();
Tags = new HashSet<Tag>();
}
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
public string Body { get; set; }
//Navigational
public ICollection<Comment> Comments { get; set; }
public ICollection<Tag> Tags { get; set; }
}
My DbContext class
public class HundredBlog: DbContext
{
public HundredBlog() : base("HundredBlog") { }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Administrator> Administrators { get; set; }
public DbSet<Tag> Tags { get; set; }
}
The Database table, "Comments" has the following columns:
-Id
-Date
-Name
-Email
-Body
-Post_Id
The Database table, "Posts" has the following columns:
-Id
-Title
-Date
-Body
Just as an example, my Database populates the Comments columns just fine, it adds the right Post_Id referencing the primary key and all. I have the same issue with the Tag table, but that even has it's own reference table:
The Database table, "TagPosts" has the following columns:
-TagId
-PostId
Lost please help!
The Comments collection in the Post class should be virtual if you want to enable lazy loading or you should use Include(p => p.Comments) to load the data with the original query. In general the second option is better.
I have a Model like this
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<string> SolvedBy { get; set; }
}
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
}
and then Controller like this. But I cannot update the List "SolvedBy", the next time I step through with the debugger, the list is still empty.
[HttpPost]
public string Index(string flag = "", int id=0)
{
Challenge challenge = db.Challenges.Find(id);
if (flag == challenge.Flag)
{
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<string>();
}
chall.SolvedBy.Add(User.Identity.Name);
db.Entry(chall).State = EntityState.Modified;
db.SaveChanges();
//congrats, you solved the puzzle
return "got it";
}
else
{
return "fail";
}
}
is there any way around it to make a list of strings kept in the database?
EF don't know how to store an array in database table so it just ignore it. You can create another table/entity or use XML/JSON to store the list. You can serialize the list before saving and deserialize it after loading from database
A List<T> in a model would normally map to a second table, but in your DbContext you only have a single table. Try adding a second table.
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
public DbSet<Solution> Solutions {get; set;}
}
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<Solution> SolvedBy { get; set; }
}
public class Solution
{
public int ID { get; set; }
public string Name { get; set; }
}
Then your controller can use code along the lines of...
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<Solution>();
}
chall.SolvedBy.Add(new Solution {Name=User.Identity.Name});
None of the above has been tested and I may have made some mistakes there, but the general principle I want to illustrate is the fact that you need another table. The List<T> represents a JOIN in SQL.
I have two models, a code model and a tag model which are linked by a many to many relationship. I am trying to add a code entry that includes a possible selection of many tags using a view model (using check boxes for the tags in my view). I am getting the error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[StoRed.Models.Code]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[StoRed.Models.CodeTagViewModel]'.
It feels like I need to somehow convert my data to the acceptable format before trying to save it into the table but I'm new to MVC and I am having trouble finding any useful information on the internet about my specific problem. Any help would be greatly appreciated.
The code model
public class Code
{
[Key]
public int CodeID { get; set; }
[Required]
[StringLength(30)]
public string Title { get; set; }
[Required]
[StringLength(150)]
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public DateTime LastUpdated { get; set; }
[Required]
[StringLength(30)]
public string Project { get; set; }
[Required]
[StringLength(30)]
public string CMS { get; set; }
public int DotNetVersion { get; set; }
[Required]
[StringLength(150)]
public string Dependencies { get; set; }
[StringLength(30)]
public string Author { get; set; }
public string CodeFile { get; set; }
[Required]
[StringLength(100)]
public string TFSLocation { get; set; }
////Creates a relationship in the DB with Tag
//[ForeignKey("TagID")]
public virtual ICollection<Tag> Tags { get; set; }
////Purely for API
//[Required]
public int TagID { get; set; }
}
The Tag model
public class Tag
{
[Key]
public int TagID { get; set; }
[Required]
[StringLength(30)]
public string TagName { get; set; }
public virtual ICollection<Code> Code { get; set; }
}
The context
public class Context : DbContext
{
public DbSet<Code> Code { get; set; }
public DbSet<Tag> Tags { get; set; }
}
The view model
public class CodeTagViewModel
{
public Tag Tag { get; set; }
public Tag TagID { get; set; }
public List<Tag> Tags { get; set; }
public int CodeID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public DateTime LastUpdated { get; set; }
public string Project { get; set; }
public string CMS { get; set; }
public int DotNetVersion { get; set; }
public string Dependencies { get; set; }
public string Author { get; set; }
public string CodeFile { get; set; }
public string TFSLocation { get; set; }
}
Relevant part of the code controller
[HttpPost]
public ActionResult Create(CodeTagViewModel codeTagViewModel)
{
if (ModelState.IsValid)
{
Code code = new Code();
Tag tag = new Tag();
var codeTag = new CodeTagViewModel();
code.Title = codeTagViewModel.Title;
code.Description = codeTagViewModel.Description;
code.DateAdded = codeTagViewModel.DateAdded;
code.LastUpdated = codeTagViewModel.LastUpdated;
code.Project = codeTagViewModel.Project;
code.CMS = codeTagViewModel.CMS;
code.DotNetVersion = codeTagViewModel.DotNetVersion;
code.Dependencies = codeTagViewModel.Dependencies;
code.Author = codeTagViewModel.Author;
code.CodeFile = codeTagViewModel.CodeFile;
code.TFSLocation = codeTagViewModel.TFSLocation;
code.Tags = codeTagViewModel.Tags;
db.Code.Add(code);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(codeTagViewModel);
}
Your best bet is to create some kind of provider/manager/service/factory/handler - choose a name that makes most sense in terms of the job it is doing within the flow of data through your system - that is responsible for taking the ViewModel and mapping the properties of the ViewModel into an instance of the domain model before persisting the domain model to the data store, either itself or by passing the hydrated domain model to a repository layer. You can either do this manually or by using something like AutoMapper. Here's a quick manual example:
Create a CommandHandlers folder in your web project with the interface and dependant handler:
public interface ICodeCommandHandler
{
int Save(CodeTagViewModel input);
}
public class CodeCommandHandler : ICodeCommandHandler
{
private IRepository<Code> repository;
public CodeCommandHandler(IRepository<Code> repository)
{
this.repository = repository;
}
public int Save(CodeTagViewModel input)
{
Code code = new Code();
Tag tag = new Tag();
code.Title = input.Title;
code.Description = input.Description;
code.DateAdded = input.DateAdded;
code.LastUpdated = input.LastUpdated;
code.Project = input.Project;
code.CMS = input.CMS;
code.DotNetVersion = input.DotNetVersion;
code.Dependencies = input.Dependencies;
code.Author = input.Author;
code.CodeFile = input.CodeFile;
code.TFSLocation = input.TFSLocation;
code.Tags.Add(tag);
return repository.Save(code);
}
}
Then in your controller, inject the ICodeCommandHandler in via constructor injection, the same as you do with the repository in the CodeCommandHandler:
private readonly ICodeCommandHandler commandHandler;
public CodeController(ICodeCommandHandler commandHandler)
{
this.commandHandler = commandHandler;
}
[HttpPost]
public ActionResult Create(CodeTagViewModel codeTagViewModel)
{
if (!ModelState.IsValid)
{
return View(codeTagViewModel);
}
var id = codeCommandHandler.Save(codeTagViewModel);
// maybe do something useful with the document id after save
return RedirectToAction("Index");
}
To keep the Repository nice and simple, here's how that could look:
public interface IRepository<T>
{
int Save(T entity);
}
public class CodeRepository : IRepository<Code>
{
public int Save(Code entity)
{
using (var context = new Context())
{
context.Code.Add(entity);
context.SaveChanges();
}
}
}
I've not gone into detail about the dependency injection side of things as that wasn't part of the question but this should give you an idea of where to start