Two columns on the same property - entity-framework

I have a database table called Person having this columns {id, first_name, last_name}. Than I have a read model called PersonView having only two properties Id and Nominative. I have a mapping for this type:
public class PersonViewMapping : EntityTypeConfiguration<PersonView>
{
public PersonViewMapping()
{
this.ToTable("Person", "schema");
this.HasKey(d => d.id);
}
}
In this mapping I would say map the concatenation of Person.first_name and Person.last_name to PersonView.Nominative.
How can I do that?

you have to build your model as of the database, if you have to save the first and last name then you entity must have those two properties. so in your case as I understand ... you need to write your class as below
public class PersonView
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Nominative { get { return string.Concat(this.Firstname, " ", this.Lastname); } }
}
and in your configuration just Ignore that property
public class PersonViewMapping : EntityTypeConfiguration<PersonView>
{
public PersonViewMapping()
{
this.ToTable("Person");
this.HasKey(d => d.Id);
this.Property(d => d.Firstname);
this.Property(d => d.Lastname);
this.Ignore(d => d.Nominative);
}
}

I think you can do it this way, without mapping, as your nomination is not the actual field in your table and for this mapping not required.
public abstract class Person
{
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
[Column("FirstName")]
[Display(Name = "First Name")]
public string FirstMidName { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
}

Related

Error "The JSON value could not be converted to System.String. Path: $[1].Interests[1].Meta[9].Content | LineNumber: 0 | BytePositionInLine: 10073."

public Class Employee{
public string Name { get; set; }
[Column(TypeName = "jsonb")]
public List<Section> Sections { get; set; }
}
public Class Sections{
public string Booking { get; set; }
[Column(TypeName = "jsonb")]
public List<Interest> Interests { get; set; }
}
public Class Interest{
public string Title { get; set; }
public List<Meta> Meta { get; set; }
public List<WithAlt> Images { get; set; }
}
public Class Meta{
public string Type { get; set; }
public string Content { get; set; }
}
public Class WithAlt{
public string content { get; set; }
public string Alt { get; set; }
}
I fetch data from the Employee table
Employee while fetching the data Sections Column I got
The JSON value could not be converted to System.String. Path: $[1].Interests[1].Meta[9].Content | LineNumber: 0 | BytePositionInLine: 10073.
Error at
public Task<Employee> CheckEmployee(string name){
// error throw Line
var query= await catalogDbContext.Employee
.Where(i.Name === name)
.FirstOrDefault();
}
Not for all value but some value that List<Section> or
List<Interest> or List<Meta> or List<WithAlt> have null value
When I manually add the value to sections column bellow
{
"Booking": "",
"Interests":[
{
"Title":"",
"Meta":[
{
"Type" : " ",
"Content" : " "
}
],
"Images" : [
{
"content" : " ",
"alt" : " "
}
]
}
],
}
it will not throw the error
Are there any way to define the default value to the above fields using code first approach
when I initialize Sections property like
public List<Section> Sections { get; set; }={};
it shows the following error
Can only use array initializer expressions to assign to array types. Try using a new expression instead.
and also
public List<Section> Sections { get; set; }= new List<Section> Sections();
and
public List<Meta> Meta { get; set; }= = new List<Meta>();
and
public List<WithAlt> Images { get; set; }= new List<WithAlt>();
throw Error "The JSON value could not be converted to System.String. Path: $[1].Interests[1].Meta[9].Content | LineNumber: 0 | BytePositionInLine: 10073."
Can only use array initializer expressions to assign to array types. Try using a new expression instead.
You can convert the json data to Section type rather than List<Section> type.
var json = "{\"Booking\":\"\",\"Interests\":[{\"Title\":\"\",\"Meta\":[{\"Type\":\" \",\"Content\":\" \"}],\"Images\":[{\"content\":\" \",\"alt\":\" \"}]}]}";
var s = JsonConvert.DeserializeObject<Section>(json);
//If you want to set Employee.Sections with json data,try this
Employee e = new Employee { Sections = new List<Section> { s } };
Models(change class name Sections to Section,Interests to Interest):
public class Employee
{
public string Name { get; set; }
[Column(TypeName = "jsonb")]
public List<Section> Sections { get; set; }
}
public class Section
{
public string Booking { get; set; }
[Column(TypeName = "jsonb")]
public List<Interest> Interests { get; set; }
}
public class Interest
{
public string Title { get; set; }
public List<Meta> Meta { get; set; }
public List<WithAlt> Images { get; set; }
}
public class Meta
{
public string Type { get; set; }
public string Content { get; set; }
}
public class WithAlt
{
public string content { get; set; }
public string Alt { get; set; }
}
I just deserialiazed you json , everything is working properly, I couldn' t find any errros
public static void Main()
{
var json = "{\"Booking\":\"\",\"Interests\":[{\"Title\":\"\",\"Meta\":[{\"Type\":\" \",\"Content\":\" \"}],\"Images\":[{\"content\":\" \",\"alt\":\" \"}]}]}";
var jd = JsonConvert.DeserializeObject<Data>(json);
}
classes
public class Data
{
public string Booking { get; set; }
public List<Interest> Interests { get; set; }
}
public class Interest
{
public string Title { get; set; }
public List<Meta> Meta { get; set; }
public List<Image> Images { get; set; }
}
public class Meta
{
public string Type { get; set; }
public string Content { get; set; }
}
public class Image
{
public string content { get; set; }
public string alt { get; set; }
}

Calling Include in Entity Framework Core 2.0 makes the entity null if the property you are trying to include is null

I am getting an EF Core Entity like this:
Audit audit = _auditRepo.Audits
.Include(a => a.Status)
.Include(a => a.AuditType)
.Include(a => a.Office)
.Include(a => a.LeadOffice)
.Include(a => a.External)
.Include(a => a.External).ThenInclude(e => e.AuditResult)
.Include(a => a.External).ThenInclude(e => e.PreparerType)
.Include(a => a.External).ThenInclude(e => e.Auditee)
.Where(a => a.AuditID == id)
.FirstOrDefault();
My audit comes back null because it has no office. If I have an office it the audit gets populated.
If I don't have an office but I comment out:
.Include(a => a.Office)
the audit also comes back populated.
Here is my Audit entity :
[Table("audit")]
public class Audit
{
private string _auditAcnCd;
private string _title;
private string _summary;
[Key]
[Column("audit_id")]
public int AuditID { get; set; }
[Required(ErrorMessage = "ACN Required")]
[Display(Name="ACN:")]
[Column("audit_acn_cd")]
public string AuditAcnCd
{
get
{
return _auditAcnCd;
}
set
{
_auditAcnCd = value?.Trim();
}
}
[Required(ErrorMessage = "Title Required")]
[Display(Name = "Title:")]
[Column("audit_report_title_tx")]
public string Title
{
get
{
return _title;
}
set
{
_title = value?.Trim();
}
}
[StringLength(maximumLength: 1000, ErrorMessage = "Max Length: 1000")]
[Display(Name = "Summary:")]
[Column("audit_summary_tx")]
public string Summary
{
get
{
return _summary;
}
set
{
_summary = value?.Trim();
}
}
[Required(ErrorMessage = "Issuer Required")]
[Display(Name="Issuer:")]
[Column("audit_issuer_tx")]
public string Issuer { get; set; }
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "Priority must be a number.")]
[Display(Name = "Priority:")]
[Column("audit_priority_cd")]
public short? Priority { get; set; }
[Display(Name = "Lead Office:")]
[Column("audit_lead_office_id")]
public short? LeadOfficeID { get; set; }
#region Navigation Properties
[Required(ErrorMessage = "Audit Type Required.")]
[Display(Name = "Audit Type:")]
[Column("audit_audit_type_id")]
public short AuditTypeID { get; set; }
[Display(Name = "Audit Type:")]
public AuditType AuditType { get; set; }
[Column("audit_status_id")]
public int StatusID { get; set; }
public Status Status { get; set; }
[Required(ErrorMessage = "Office is Required.")]
[Display(Name = "Offices:")]
[Column("audit_office_id")]
public short? OfficeID { get; set; }
// [ForeignKey("OfficeID")]
public Office Office { get; set; }
[ForeignKey("AuditID")]
public External External { get; set; }
public IEnumerable<AuditLog> AuditLogs { get; set; }
public IEnumerable<Finding> Findings { get; set; }
public IEnumerable<Assignment> Assignments { get; set; }
[Column("audit_update_staff_id")]
public short UpdateStaffID { get; set; }
[Column("audit_fsa_office_id")]
[Display(Name = "FSA Audit Lead:")]
public int? FsaLeadOfficeId { get; set; }
[Display(Name = "FSA Audit Lead:")]
[ForeignKey("FsaLeadOfficeId")]
public FSAOffice FsaLeadOffice { get; set; }
[ForeignKey("LeadOfficeID")]
public Office LeadOffice { get; set; }
}
Here is my Office entity:
[Table("office")]
public class Office
{
private string _OfficeCd;
private string _OfficeNm;
private string _OfficeOrganizationCd;
[Key]
[Column("office_id")]
public short OfficeID { get; set; }
[Required(ErrorMessage = "Numeric Code is required")]
[StringLength(2, ErrorMessage = "Max Length is two")]
[Display(Name = "Office Numeric Code:")]
[Column("office_cd")]
public string OfficeCd
{
get
{
return _OfficeCd;
}
set
{
_OfficeCd = value?.Trim();
}
}
[Required(ErrorMessage = "Office Name is required")]
[Display(Name = "Office Name:")]
[Column("office_nm")]
public string OfficeNm
{
get
{
return _OfficeNm;
}
set
{
_OfficeNm = value?.Trim();
}
}
[Required(ErrorMessage = "Office Abbreviation is required")]
[Display(Name = "Office Abbreviation:")]
[Column("office_organization_cd")]
public string OfficeOrganizationCd
{
get
{
return _OfficeOrganizationCd;
}
set
{
_OfficeOrganizationCd = value?.Trim();
}
}
[Display(Name = "Status:")]
[Column("office_active_cd")]
public string OfficeActiveCd { get; set; }
[Display(Name = "Parent Office:")]
[Column("office_parent_id")]
public short? OfficeParentId { get; set; }
[Display(Name = "Parent Office:")]
[ForeignKey("OfficeParentId")]
public Office ParentOffice { get; set; }
public List<StaffOffice> StaffOffices { get; set; }
}
All my other properties work ok. Do I need a configure this in onModelCreating?
The explanation is simple - there is discrepancy between your entity model and the database.
When the FK is nullable like your OfficeId, the relationship is optional, EF Core expects that the column can be null and uses left outer join when retrieving the related data (requested via Include).
But at some point you seem to added [Required] attribute to the FK property:
[Required(ErrorMessage = "Office is Required.")] // <-- the problem
[Display(Name = "Offices:")]
[Column("audit_office_id")]
public short? OfficeID { get; set; }
// [ForeignKey("OfficeID")]
public Office Office { get; set; }
Note that the [Required] attribute and IsRequired() fluent API take precedence over the data type (of course you cannot make non nullable type nullable, but the other way around is possible). The effect is that now EF considers the OfficeID FK to be required (i.e. non nullable column in the database) and performs inner join, which of course filters the result in case the record contains null FK value.
The solution is to remove that attribute. And in general, always keep the model and the database in sync. Anytime you change something in the model, add new migration. In case it's empty, remove it, otherwise see what database changes EF assumes based on your model metadata/configuration.

AutoMapper returns null for all values in simple mapping case

I am using Entity Framework 6 with MVC and Web API and I have been toying with whether to use Data Transfer Objects (DTO's) for the JSON api side of things..
I came across AutoMapper and having written some DTO's manually thought (still think) this should be a good way to manage the DTO's.
So, New to it, installed via Nuget, I tried my first mapping. The mblUser class derived from IdentityUser as the source, and I want a simple APIUser to go down the wire..heres the classes:
public class mblUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<mblUser> manager, string authenticationType = DefaultAuthenticationTypes.ApplicationCookie)
{
//authentication type added to provide for Bearer type of authentication. Still defaults to cookie though so as not to impact the code
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType );
// Add custom user claims here
return userIdentity;
}
[Display(Name = "Date of Birth IDVIEW")]
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:d}",
NullDisplayText = "Please Enter Your Date of Birth",
ApplyFormatInEditMode = true)]
public DateTime? DateOfBirth { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName {get; set;}
public string DisplayName { get; set; }
public virtual Leader Leader { get; set;}
public virtual Guardian Guardian { get; set; }
public virtual Participant Participant { get; set; }
[NotMapped]
[Display(Name ="Full Name")]
public string FullName
{
get
{
return FirstName + (MiddleName !=null ? " " + MiddleName : "") + (LastName != null ? " " + LastName :"");
}
}
[NotMapped]
[Display(Name ="Details Complete")]
public bool DetailsComplete
{
get
{
if (string.IsNullOrEmpty(FullName.Trim()) || string.IsNullOrEmpty(Email) )
{
// set true if we have something of a name and an email address..
return false;
}
else
{
return true;
}
}
}
}
.. and heres the APIUser:
public class APIUser
{
private string Id { get; set; }
private string Email { get; set; }
private string UserName { get; set; }
private string Title { get; set; }
private string FirstName { get; set; }
private string MiddleName { get; set; }
private string LastName { get; set; }
private string DisplayName { get; set; }
private string PhoneNumber { get; set; }
}
... and heres the mapping code:
// GET: api/userSettings/5
[ResponseType(typeof(APIUser))]
public async Task<IHttpActionResult> GetmblUser(string username)
{
mblUser User = await UserManager.FindByNameAsync(username);
if (User == null)
{
return NotFound();
}
Mapper.CreateMap<mblUser, APIUser>();
APIUser dto = Mapper.Map<APIUser>(User); <-- here all User members are valid and populated.
return Ok(dto); <--at a breakpoint here.. all dto members are null.
}
I figure I must be doing some kind of Newbie error or something but cant figure out what? I did wonder if it is because the mblUser is derived from IdentityUser but the documentation is far from clear on that issue.
Out of interest, I did spot another issue that seems related here and I do indeed have AutoMapper.Net4 in my references as well as AutoMapper, but I have also seen that it is required elsewhere, so am a bit stuck ..
Any help would be appreciated.
You need to make your properties public before they can be mapped.
public class APIUser
{
public string Id { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string DisplayName { get; set; }
public string PhoneNumber { get; set; }
}

how to concatenate first name and last name and display as full name in view when EF databasefirst

I want to concatenate first name and last name and display it as fullname in view.i tried in usermeta because when you again generate edmx file it will wont affect but error unrecognised fields,any idea how?
public partial class userdetail
{
public userdetail()
{
this.orderdetails = new HashSet<orderdetail>();
}
public string userid { get; set; }
public string username { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public virtual ICollection<orderdetail> orderdetails { get; set; }
}
I created one more class usermeta for validations.
public class Usemeta
{
[Required]
public string userid { get; set; }
[Required]
public string username { get; set; }
[Required]
public string firstname { get; set; }
[Required]
public string lastname { get; set; }
//[Required]
//public string Fullname { get { return string.Concat(firstname + "" + lastname); } }
}
And then I created a partial class.
[MetadataType(typeof(Usemeta))]
public partial class userdetail
{
}
Just add another property in your View and concatenate FirstName and LastName as a read only property , Somthing like this :
public string FullName
{
get { return FirstName + " " +LastName ;}
}
Also you can use expression body:
public string FullName => FirstName + " " + LastName;

Entity framework join 2 tables

Beginner with entity framework and mvc here.
I have 2 table models:
UserProfile
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
and
ChatLogs
[Table("ChatLogs")]
public class ChatLogs
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ChatLogId { get; set; }
[Column("Message")]
public string Message { get; set; }
[Column("UserId")]
public int UserId { get; set; }
public override string ToString()
{
return "Person: " + Message + " " + UserId;
}
}
UserId in table ChatLogs is foreign key to UserPorfile UserId primary key.
I am trying to join these 2 tables in Asp.NET MVC 4
Tested SQL query:
select * from UserProfile as a join ChatLogs as b on a.UserId = b.UserId
Tested Linq query:
from b in db.ChatLogs
select new {
ChatLogId = b.ChatLogId,
Message = b.Message,
UserId = b.UserId,
Column1 = (int?)b.UserProfile.UserId,
UserName = b.UserProfile.UserName,
Email = b.UserProfile.Email
}
I am using software called "Linqer" for learning purposes. It conversts SQL to Linq.
ActionResult code:
private ChatLogContext db = new ChatLogContext();
public ActionResult Index()
{
var list = from b in db.ChatLogs
select new
{
ChatLogId = b.ChatLogId,
Message = b.Message,
UserId = b.UserId,
Column1 = (int?)b.UserProfile.UserId,
UserName = b.UserProfile.UserName,
Email = b.UserProfile.Email
};
var vm = new ChatLogsViewModel { LogListString = string.Join("\n", list) };
return View(vm);
}
ChatLogViewModel has only a string variable for printing list in view.
But I get an error:
'Chat.Models.ChatLogs' does not contain a definition for 'UserProfile' and no extension method 'UserProfile' accepting a first argument of type 'Chat.Models.ChatLogs' could be found (are you missing a using directive or an assembly reference?)
So do I have to connect those 2 entities somehow so they would know about each other?
Try this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
**public virtual ICollection<ChatLogs> ChatLogs { get; set; }**
}
[Table("ChatLogs")]
public class ChatLogs
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ChatLogId { get; set; }
[Column("Message")]
public string Message { get; set; }
[Column("UserId")]
public int UserId { get; set; }
**public virtual UserProfile UserProfile { get;set; }**
public override string ToString()
{
return "Person: " + Message + " " + UserId;
}
}
The easiest way to connect is to make Chatlogs available on the user as a List:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public List<ChatLog> ChatLogs{ get; set;}
}
Now you can do the following:
var users = Users.Include("ChatLogs");
Every user will now have its list of ChatLogs filled in correctly.