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);
}
Related
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);
I have one model class called Client.
namespace Logistic.Models
{
public class Client
{
public int ClientId { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public ICollection<Dispatch> dispatches { get; set; }
}
}
I have another class that has two properties with relationship to Client:
namespace Logistic.Models
{
public class Dispatch
{
public int DispatchId { get; set; }
public int CustomerId { get; set; }
public int RecipientId { get; set; }
public Client Customer { get; set; }
public Client Recipient { get; set; }
}
}
In order to have a relationship in my Dispatch class I have to have clientId. Right? But in this case I will have two clientId. I am just started with ASP.NET MVC, and I can't understand it.
Because in my controller I have:
public ActionResult Dispatch()
{
db.Dispatches.Include("Customer").ToList();
db.Dispatches.Include("Recipient").ToList();
var dispatch = db.Dispatches;
return View(dispatch);
}
And in my View I am trying to display:
#model IEnumerable<Logistic.Models.Dispatch>
#{
ViewBag.Title = "Dispatch";
}
<h2>Dispatch</h2>
#foreach(var item in Model)
{
<h2>Tracking : #item.TrackingId</h2> <br />
<h2>Customer : #item.Customer.Name</h2> <br />
<h2>Recipient : #item.Recipient.Name</h2> <br />
}
If I understand correctly, you are trying to use RecipientId and CustomerId as Foreign Keys for Recipient and customer respectively? In this case you can add the foreign key attribute to the properties like this:
namespace Logistic.Models
{
public class Dispatch
{
public int DispatchId { get; set; }
public int CustomerId { get; set; }
public int RecipientId { get; set; }
[ForeignKey("CustomerId")]
public Client Customer { get; set; }
[ForeignKey("RecipientId")]
public Client Recipient { get; set; }
}
}
This will explicitly specify the foreign keys for the relationship. Hope this helps!
I'm trying to sort out this issue but as I'm learning a lot of this stuff as I go along I'd really appreciate it if someone could explain where I'm going wrong and/or some good resources where I can read up.
So, I have a model based on my Entity Framework model of my database and a viewmodel representing properties in that model. I've built a Kendo grid to display the data (defined in a js file) and the method in the contoller returns a Json result set. Trouble is, when I try to display a value in a joined db table, if there hasn't been a key value set, I get a nullreferenceexception error. Obviously I'm missing part of the puzzle here as there must be a way of coding this to stop it happening. Any help would be gratefully received!
My model is like this:
namespace TrainingKendoUI.Models
{
using System;
using System.Collections.Generic;
public partial class TRAINING_EMPLOYEE_COURSES
{
public int EMP_COURSE_ID { get; set; }
public int EMPLOYEE_ID { get; set; }
public int COURSE_ID { get; set; }
public Nullable<System.DateTime> DATE_ATTENDED { get; set; }
public Nullable<decimal> COURSE_COST { get; set; }
public string COURSE_RESITS { get; set; }
public Nullable<int> PROVIDER_ID { get; set; }
public Nullable<int> EMP_COURSE_STATUS_ID { get; set; }
public Nullable<int> VENUE_ID { get; set; }
public virtual TRAINING_COURSES TRAINING_COURSES { get; set; }
public virtual TRAINING_EMPLOYEE_COURSE_STATUS TRAINING_EMPLOYEE_COURSE_STATUS { get; set; }
public virtual TRAINING_EMPLOYEES TRAINING_EMPLOYEES { get; set; }
public virtual TRAINING_PROVIDERS TRAINING_PROVIDERS { get; set; }
public virtual TRAINING_VENUES TRAINING_VENUES { get; set; }
}
}
My controller method looks like this:
public JsonResult EmployeeCourses_Read()
{
var model = db.TRAINING_EMPLOYEE_COURSES;
var ViewModel = new List<EmployeeCoursesIntersectionViewModel>();
foreach (var employee in model)
{
ViewModel.Add(new EmployeeCoursesIntersectionViewModel(employee));
}
return Json(ViewModel, JsonRequestBehavior.AllowGet);
}
and my view model lilke this:
namespace TrainingKendoUI.ViewModels
{
public class EmployeeCoursesIntersectionViewModel
{
#region Constructors
public EmployeeCoursesIntersectionViewModel()
{
}
public EmployeeCoursesIntersectionViewModel(TRAINING_EMPLOYEE_COURSES model)
{
this.empCourseId = model.EMP_COURSE_ID;
this.employee = model.TRAINING_EMPLOYEES.FIRST_NAME;
this.course = model.TRAINING_COURSES.COURSE_NAME;
this.dateAttended = model.DATE_ATTENDED;
this.cost = model.COURSE_COST;
this.resits = model.COURSE_RESITS;
//These lines will produce a NullReference error if not set through the front end...
this.provider = model.TRAINING_PROVIDERS.PROVIDER_NAME;
this.status = model.TRAINING_EMPLOYEE_COURSE_STATUS.EMP_COURSE_STATUS;
this.venue = model.TRAINING_VENUES.VENUE_NAME;
}
#endregion
#region Properties
public int empCourseId { get; set; }
public string employee { get; set; }
public string course { get; set; }
public Nullable<System.DateTime> dateAttended { get; set; }
public Nullable<decimal> cost { get; set; }
public string resits { get; set; }
public string provider { get; set; }
public string status { get; set; }
public string venue { get; set; }
#endregion
}
}
Do a null check on the object before setting it, i.e.
this.provider = model.TRAINING_PROVIDERS == null ? ""
: model.TRAINING_PROVIDERS.PROVIDER_NAME;
and you'll have to do similar for status and venue
this.status = model.TRAINING_EMPLOYEE_COURSE_STATUS== null ? ""
model.TRAINING_EMPLOYEE_COURSE_STATUS.EMP_COURSE_STATUS;
this.venue = model.TRAINING_VENUES== null ? ""
model.TRAINING_VENUES.VENUE_NAME;
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
I'm encountering an issue using Silverlight4, Ria Services and Entity Framework.
From my sl client I try to get some data through ria services, in my domainService class this method gets called:
public IQueryable<LastMinuteWachtLijstPromotie> GetLastMinuteWachtLijstPromoties(){
IQueryable<LastMinuteWachtLijstPromotie> list = (IQueryable<LastMinuteWachtLijstPromotie>)this.ObjectContext.LastMinuteWachtLijstPromoties.Include("Promotie");
return (from LastMinuteWachtLijstPromotie lwmp in list where lwmp.Actief select lwmp);
}
when I check the contents of the list, in debug mode, it's filled with objects of type LastMinuteWachtLijstPromotie.
these objects have a navigation property to an Object named Promotie.
And i can access the properties of these Promotie objects.
On the silveright client however a method gets invoked when loading is complete:
public void OnLoadEntitiesCompleted(ServiceLoadResult<T> result) {
}
In this method I get all the requested LastMinuteWachtLijstPromotie objects as expected, the property
Promotie however is null.
I have set the [Include] tag on the property Promotie in the auto generated metadata class
and I use the .Include("Promotie")
These same methods are used for different objects from my Domain Model, this works perfectly.
Also, I cannot seem to find differences in the .edmx file with the database mappings and navigation properties.
Has anyone encountered the same issue or know a solution for it?
the metadata classes:
[MetadataTypeAttribute(typeof(LastMinuteWachtLijstPromotie.LastMinuteWachtLijstPromotieMetadata))]
public partial class LastMinuteWachtLijstPromotie
{
// This class allows you to attach custom attributes to properties
// of the LastMinuteWachtLijstPromotie class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class LastMinuteWachtLijstPromotieMetadata
{
// Metadata classes are not meant to be instantiated.
private LastMinuteWachtLijstPromotieMetadata()
{
}
public int AlertId { get; set; }
public string ArtikelNummer { get; set; }
public Nullable<int> ArtikelVariant { get; set; }
public int LastMinuteWachtLijstPromotieId { get; set; }
[Include]
public Promotie Promotie { get; set; }
public int PromotieArtikelId { get; set; }
public int PromotieId { get; set; }
public bool Actief { get; set; }
public DateTime Aanmaakdatum { get; set; }
}
}
[MetadataTypeAttribute(typeof(Promotie.PromotieMetadata))]
public partial class Promotie
{
// This class allows you to attach custom attributes to properties
// of the Promotie class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class PromotieMetadata
{
// Metadata classes are not meant to be instantiated.
private PromotieMetadata()
{
}
public string ActieType { get; set; }
public string AssortimentsManagerNaam { get; set; }
public string AssortimentsManagerTeamIds { get; set; }
[Display(Name = "Commerciele tekst")]
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Nokavision.ReclameFolder.UI.Web.Resources.ValidationResources))]
public string CommercieleTekst { get; set; }
[Display(Name = " ")]
public string CommercieleTekstDetails { get; set; }
[Include]
public Frame Frame { get; set; }
public Nullable<int> FrameId { get; set; }
public Nullable<DateTime> LastMinuteWijzigingsDatum { get; set; }
public string Opmerkingen { get; set; }
[Display(Name = "Op wachtlijst")]
public Nullable<bool> OpWachtLijst { get; set; }
//public Nullable<int> PromotieCopyId { get; set; }
public int PromotieId { get; set; }
[Include]
public EntityCollection<PromotieLeverancier> PromotieLeveranciers { get; set; }
[Include]
public EntityCollection<PromotieMutatie> PromotieMutaties{ get; set; }
//public Nullable<int> PromotieOrigineleId { get; set; }
[Include]
public EntityCollection<PromotieSymbool> PromotieSymbolen { get; set; }
public string Status { get; set; }
[Display(Name = "Promotie inhoud")]
public string PromotieInhoud { get; set; }
[Display(Name = "Promotie eenheid")]
public string PromotieEenheid { get; set; }
[Display(Name = "Promotie prijs")]
public decimal PromotiePrijs { get; set; }
}
}
Add the Composition attribute to the property Promotie property of the LastMinuteWachtLijstPromotieMetadata class. Then it should work.
public partial class LastMinuteWachtLijstPromotie {
internal sealed class LastMinuteWachtLijstPromotieMetadata{
[Include]
[Composition]
public Promotie Promotie { get; set; }
}
}
I know this is an older thread and it may well have been answered elsewhere but I just stumbled upon it and since nobody has provided a link or a better answer.
I'm currently using Silverlight 5 and this is what worked for me (I think the process is the same in SL4 IIRC).
When propegating navigation properties to the client you need to tell RIA services that there is a relationship somewhere using the [Key] and [Association] attributes, this, not unlike the entity framework just describes how to map the relationship to the proper object.
First the metadata classes:
[MetadataTypeAttribute(typeof(Category.CategoryMetadata))]
public partial class Category
{
internal sealed class CategoryMetadata
{
private CategoryMetadata() {
}
[Key]
public int Id { get; set; }
public string NAME { get; set; }
[Association("CategoryToProducts", "Id", "CAT")]
[Include]
public EntityCollection<Product> Products { get; set; }
}
}
[MetadataTypeAttribute(typeof(Order.OrderMetadata))]
public partial class Order
{
internal sealed class OrderMetadata
{
// Metadata classes are not meant to be instantiated.
private OrderMetadata() {
}
[Key]
public int Id { get; set; }
public int PRODID { get; set; }
public DateTime DATE { get; set; }
public bool DONE { get; set; }
public int QTY { get; set; }
[Association("OrderToProduct", "PRODID", "Id", IsForeignKey = true)]
[Include]
public Product Product { get; set; }
}
}
[MetadataTypeAttribute(typeof(Product.ProductMetadata))]
public partial class Product
{
internal sealed class ProductMetadata
{
private ProductMetadata() {
}
[Key]
public int Id { get; set; }
public int CAT { get; set; }
public string NAME { get; set; }
public string DESC { get; set; }
public decimal PRICE { get; set; }
public int QTY { get; set; }
public long UPC { get; set; }
[Association("ProdToCat", "CAT", "Id", IsForeignKey = true)]
[Include]
public Category Category { get; set; }
[Association("ProductToOrders", "Id", "PRODID")]
[Include]
public EntityCollection<Order> Orders { get; set; }
}
}
Now we need to tell RIA services we want it to load the association:
(Note: Intellisense says it's a dot separated list of property names to include, however I tried something like .Include("Category.SubCategory") and this failed with an exception... though .Include("Category").Include("SubCategory") worked like a charm!)
public IQueryable<Product> GetProducts() {
return this.ObjectContext.Products.Include("Category");
}
I can now access my "Category" property from the Silverlight client and it is not NULL :)
Same as SilverX: just had the issue, solved it and thought it could be useful to someone.
I too had all the configuration stuff correct ([Include] for RIA S, Include() for EF) but a navigation property was still null on the Silverlight side.
Turns out the domain service method was using the [Invoke] attribute (and returning a IEnumerable<T>). Removing this attribute solved the issue.
(just for the record, [Invoke] was being used because the method had a List<Entity> parameter)