Using Data Annotiations in Razor Pages while cycling through class properties - razor-pages

I am fairly new to Razor Pages and even .Net Core so please bear with me.
I am using Dapper to fill a class with data from an SQL Database which is working great. I can't change the long existing field names. So when I try to get headlines in a table by cycling through the properties and displaying the names in the headlines, I end up with the fairly cryptic field names. I try to add Data Annotiations to the class properties, but those do not seem to catch on to the names of the properties.
My class with annotiations:
namespace HelloRazorPage.Models
{
[BindProperties]
public class Apotheke
{
[Display(Name ="KdNr")]
public string KUNDENNUMM { get; set; }
public string BGANr { get; set; }
[Display(Name = "Name")]
public string NAMA { get; set; }
[Display(Name = "Straße")]
public string STRASSE{ get; set; }
[Display(Name = "Haus Nr.")]
public string HAUSNR { get; set; }
public string PLZ { get; set; }
[Display(Name = "Ort")]
public string ORT { get; set; }
public List<Vertraege> Vertraege { get; set; }
public bool IsNull { get; set; }
}
}
And cycling through my properties:
<table class="tableScroll">
#{var apo = (Models.Apotheke)ViewData["Apo"]; }
#if (apo != null)
{
<thead>
<tr>
#foreach(var prop in apo.GetType().GetProperties())
{<th> #prop.Name </th>}
</tr>
</thead>
This works like it's intendet to. It just does not show the annotiatons.
Tried to add data annotiatons to my class and want them to show up in the name property of [object].apo.GetType().GetProperties()

Related

Autmapper nested mapping

I have the following main class:
public class ResearchOutcome
{
public ResearchOutcomeCategory ResearchOutcomeCategory { get; set; }
public string? UniqueIdentifier { get; set; }
}
And the category class is:
public class ResearchOutcomeCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}
The View models for above classes are:
public class ResearchOutcomeDetailVm : IMapFrom<ResearchOutcome>
{
public int Id { get; set; }
public virtual ResearchOutcomeCategoryDetailVm ResearchOutcomeCategory { get; set; }
}
public class ResearchOutcomeCategoryDetailVm : IMapFrom<ResearchOutcomeCategory>
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Now, I have used the following mapping profile:
// First this one
profile.CreateMap<ResearchOutcomeCategory, ResearchOutcomeCategoryDetailVm>();
profile.CreateMap<ResearchOutcome, ResearchOutcomeDetailVm>();
//Then I tried this one
profile.CreateMap<ResearchOutcome, ResearchOutcomeDetailVm>()
.ForMember(o => o.ResearchOutcomeCategory,
cat => cat.MapFrom( o => o.ResearchOutcomeCategory));
But the ResearchOutcomeCategory is always null. Any help would be appreciated.
After digging more, I identified that I was not "Including" the relevant item in the query, hence, the view model was always empty. Pretty dumb on my part :D
Regarding the mapping, if the properties (even complex ones) have the same names, then the mapper will map them automatically. So simply this line worked
profile.CreateMap<ResearchOutcomeCategory, ResearchOutcomeCategoryDetailVm>();
Hope it helps someone

Issue with using a list to populate a table in an MVC 5 View

I have the following code in my controller:
public ActionResult Index(int Id)
{
Landbase _db = new Landbase();
OwnerWorkingInterests workingInterests = new OwnerWorkingInterests();
//Owner owner = new Owner();
var query = (from wg in _db.WorkingInterestGroups
join wi in _db.WorkingInterests on wg.Id equals wi.WorkingInterestGroupId
join l in _db.Leases on wg.LeaseId equals l.Id
where wi.OwnerId.Equals(Id)
select new OwnerWorkingInterests()
{
LeaseId = l.Id,
WorkingInterestAmount = wi.WorkingInterestAmount,
WorkingInterestGroupName = wg.Name,
ClientAlias = l.ClientAlias,
Lessor = l.Lessor,
Lessee = l.Lessee,
VolDocNumber = l.VolumeDocumentNumber,
County = l.County,
District = l.District
}).ToList();
//List<string> OwnerWorkingInterest = query.ToList<string>();
return View(query);
}
I have the following code in my view:
<div id="OwnerWorkingInterests" class="tab-pane fade">
<h3>Working Interests</h3>
<table class="table">
<thead>
<tr>
<td>Lease Id:</td>
<td>Working Int:</td>
<td>WI Group Name:</td>
<td>Alias:</td>
<td>Lessor:</td>
<td>Lessee:</td>
<td>VolPg:</td>
<td>County:</td>
<td>District0:</td>
</tr>
</thead>
<tbody>
#foreach (var owi in OwnerWorkingInterests)
{
<tr>
<td>#owi.LeaseId</td>
<td>#owi.WorkingInterestAmount</td>
<td>#owi.WorkingInterestGroupName</td>
<td>#owi.ClientAlias</td>
<td>#owi.Lessor</td>
<td>#owi.Lessee</td>
<td>#owi.VolDocNumber</td>
<td>#owi.County</td>
<td>#owi.District</td>
</tr>
}
</tbody>
</table>
</div>
I thought this would populate the table with the proper information
This is the viewmodel:
namespace LandPortal.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class WorkingInterest
{
public int Id { get; set; }
public int? OwnerId { get; set; }
[Column("WorkingInterest")]
public decimal? WorkingInterestAmount { get; set; }
[StringLength(45)]
public string CreateUser { get; set; }
[StringLength(45)]
public string ModifyUser { get; set; }
public Guid? CreateUserId { get; set; }
public Guid? ModifyUserId { get; set; }
public DateTime? CreateDate { get; set; }
public DateTime? ModifyDate { get; set; }
public int? WorkingInterestGroupId { get; set; }
public WorkingInterestGroup WorkingInterestGroup { get; set; }
public decimal? ORRI { get; set; }
public int? ORRIOwnerId { get; set; }
public virtual Owner Owner { get; set; }
}
}
So what happens is it throws a very vague error when I run it in debugger. It literally just says Error: An error occurred when processing your request. So I am assuming that the list is populating but not working in the foreach in the view. I could be wrong at this point.
Here are the model directives for the view
#using LandPortal.Models
#using LandPortal.ViewModels
#using Microsoft.Ajax.Utilities
#model LandPortal.Models.Owner
If the view expects a model that is of type LandPortal.Models.Owner, and Index returns an entire ActionResult, then Index needs to return a model of that type.
A tiny example:
public ActionResult Index(int Id)
{
Landbase _db = new Landbase();
Owner owner = new Owner();
// some query has to set properties on this owner object
// let's pretend there's a property named OwnerWorkingInterests on it
owner.OwnerWorkingInterests = query.ToList(); // you will have to define "query" and set it similar to how you already did
return View(owner);
}
Now your view can access the property on the model as so
#foreach (var owi in Model.OwnerWorkingInterests)
This is a very high level example, but I see you have a partial class and mentioned partial views in your comment. If you have a large view and are trying to break up a query into pieces, that can be done with PartialViewResult and will be a bit different from this.

EntityFramework : Invalid column name *_ID1

I am trying to implement DbContext for couple of tables called 'Employee' and 'Department'
Relationship between Employee and Department is many to one. i.e. department can have many employees.
Below are the EntityFramework classes I designed ( CodeFirst approach )
[Table("Employee")]
public class Employee
{
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("Name")]
public string Name { get; set; }
[Column("Department_ID")]
public int Department_ID { get; set; }
public virtual Department Department { get; set; }
}
[Table("Department")]
public class Department
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Column("Name")]
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
While adding Employee record I am getting below exception
"Invalid column name 'Department_ID1'."
I am not sure why EF is referring to Department_ID1. Do I need to add configuration in OnModelCreating method of DbContext?
I am using EF version 6.1.1
I've also gotten this problem in my EF one-many deals where the one has a List of the many property and my mapping didn't specify that property. For example take:
public class Notification
{
public long ID { get; set; }
public IList<NotificationRecipient> Recipients { get; set; }
}
then
public class NotificationRecipient
{
public long ID { get; set; }
public long NotificationID { get; set; }
public Notification Notification { get; set; }
}
Then in my mapping, the way that caused the Exception (the incorrect way):
builder.HasOne(x => x.Notification).WithMany()
.HasForeignKey(x => x.NotificationID);
What fixed it (the correct way) was specifying the WithMany property:
builder.HasOne(x => x.Notification).WithMany(x => x.Recipients)
.HasForeignKey(x => x.NotificationID);
Hi After spending some time I could fix this problem by using ForeignKey attribute on public virtual Department Department { get; set; } property of Employee class.
Please see below code.
[Table("Employee")]
public class Employee
{
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("Name")]
public string Name { get; set; }
[Column("Department_ID")]
public int Department_ID { get; set; }
[ForeignKey("Department_ID")]
public virtual Department Department { get; set; }
}
This fixed my problem. Are there any other solution to fix this? Using fluent API?
For me, the issue was resolved by removing a (duplicate?) virtual property.
Using the OP's example:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Department_ID { get; set; }
public virtual Department Department { get; set; }
}
public class Department
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
Turns into:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Department_ID { get; set; }
}
public class Department
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
In my case I added a virtual property on top of the auto generated property
I fixed it by adding the NotMapped attribute to my property, or you could configure with fluent api
public partial class Control
{
[NotMapped]
public virtual ICollection<Control> Children { get => this.InverseParent; set => this.InverseParent = value; }
}
I had the same error, my issue was the FK was a long but I had it as an int in the model. EF generated a new column because it didn't match types on the FK so it assumed they weren't the same and went ahead with making another one but putting 1 at the end because there was already one with the proper name. Making sure the types matched resolved the issue for me.
This can be fixed simply by putting [NotMapped] annotation on your virtual properties.
public class Employee
{
[ForeignKey("Department")]
public int Department_ID
[NotMapped]
public virtual Department Department { get; set; }
}
And in you modelBuilder:
modelBuilder.Entity<Employee>(entity =>
{
entity.HasOne(e => e.Department);
});
Just flip this around if you want to call by Department.
We use the [NotMapped] annotation so that EF Core will disregard it when looking at your database.

how do i use AutoMapper in ICollation<> Fields

when i use AutoMapper for mapping my ViewModels and get All News, thrown error for me.
Errors...
The following property on Mosque.Core.ViewModels.CategoryViewModel cannot be mapped:
Categories
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type Mosque.Core.ViewModels.CategoryViewModel.
please help me, thank you
//Models
public class News
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual User User { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<News> News { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<News> News { get; set; }
}
//ViewModels
public class NewsViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<CategoryViewModel> Categories { get; set; }
public virtual UserViewModel User { get; set; }
}
public class CategoryViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<NewsViewModel> News { get; set; }
}
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<NewsViewModel> News { get; set; }
}
how do i use for select All News?
--Update1--
I used onion architecture in the project and i installed AutoMapper in the Service layer and i want get all news from repository and fill into ViewModels and pass to the UI.
my code in service layer is...
public List<NewsViewModel> GetAll()
{
Mapper.CreateMap<News, NewsViewModel>()
.ForMember(dest => dest.Categories, src => src.MapFrom(p => p.Categories))
.ForMember(dest => dest.User, src => src.MapFrom(p => p.User));
Mapper.AssertConfigurationIsValid();
var viewModels = new List<NewsViewModel>();
foreach (var item in _newsRepository.GetAll())
{
var viewModel = Mapper.Map<News, NewsViewModel>(item);
viewModels.Add(viewModel);
}
return viewModels;
}
You don't seem to have created maps for Catagory and User.
Add the following maps:
Mapper.CreateMap<User, UserViewModel>();
Mapper.CreateMap<Category, CategoryViewModel>();
By the way, why are you creating the maps inside the GetAll method? You can create the maps once, usually at application startup.

EF Code First - Many To Many

I have a problem with devising a many to many relationship in code first. EF is creating the Junction table and associating the Fk's as I would expect, however when i try to access the User's MailingList collection, there are no entries.
I've implemented test data on Initialise via Seeding, the data is al present in the database.
I think the problem lies with the constructors for Users and MailingLists, but I'm uncertain. I want to be able to navigate the navigational property of User.MailingLists.
var user = db.Users.Find(1);
Console.WriteLine("{0}", user.EmailAddress); //This is Fine
Console.WriteLine("{0}", user.Address.PostCode); /This is Fine
foreach (MailingList ml in user.MailingLists) // this is 0
{
Console.WriteLine("{0}", ml.Name);
}
My model is below:-
public class User : IEntityBase
{
public User()
{
MailingLists = new List<MailingList>();
}
public int Id { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
public DateTime? DateLastUpdated { get; set; }
public DateTime DateCreated { get; set; }
public bool IsDeleted { get; set; }
public virtual Address Address { get; set; }
public ICollection<MailingList> MailingLists { get; set; }
}
public class MailingList : IEntityBase
{
public MailingList()
{
Users = new List<User>();
}
public int Id { get; set; }
public string Name { get; set; }
public DateTime? DateLastUpdated { get; set; }
public DateTime DateCreated { get; set; }
public bool IsDeleted { get; set; }
public ICollection<User> Users { get; set; }
}
public class Address : IEntityBase
{
public int Id { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
public string City { get; set; }
public string County { get; set; }
public string PostCode { get; set; }
public DateTime? DateLastUpdated { get; set; }
public DateTime DateCreated { get; set; }
public bool IsDeleted { get; set; }
}
Any suggestions welcome.
You are neither eager loading the MailingList entries with the query, nor fulfulling the requirements for a lazy loading proxy so there is no way EF can populate the collection.
To allow lazy loading, change the MailingList property to be virtual to allow the EF proxy to override it.
To use eager loading, use Include() (an extension method in System.Data.Entity) in the query to specify that the MailingList should be loaded.

Resources