AutoMapper returns null for all values in simple mapping case - entity-framework

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; }
}

Related

Why does PersistentObjectSpace sometimes return a proxy and sometimes return an object?

The debugger shows me that in the following code
_taxRate =
PersistentObjectSpace.FindObject<TaxRate>(CriteriaOperator.Parse("[TaxCodeId] = ?", TaxCodeId));
var _product2 =
PersistentObjectSpace.FindObject<Product>(CriteriaOperator.Parse("[ItemId] = ?", ItemId));
_taxRate is a poco but _product2 is a proxy
The objects are
[Table("TaxCode")]
[DefaultProperty("TaxCode")]
[ImageName("BO_List")]
public class TaxRate : BasicBo
{
[Key] public short TaxCodeId { get; set; }
[Required]
[RuleRequiredField(DefaultContexts.Save)]
[StringLength(20, ErrorMessage = "The field cannot exceed 20 characters. ")]
public string TaxCode { get; set; }
[Required]
[RuleRequiredField(DefaultContexts.Save)]
public decimal Percentage { get; set; }
public override string ToString()
{
return TaxCode;
}
}
and
[Table("MyExtItem")]
[DefaultProperty("ProductCode")]
[NavigationItem("Config")]
public class Product : BasicBo
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
[Column("Item Number")] public string ProductCode { get; set; }
[MaxLength(10)] public string UnitName { get; set; }
public int? ProductImageId { get; set; }
[ForeignKey("ProductImageId")] public virtual ProductImage ProductImage { get; set; }
[ForeignKey("ItemId")] public virtual ExtMaterialProperty ExtMaterial { get; set; }
}
This is expected behaviour when EF is configured to support lazy loading.
TaxRate holds no references to other entities so EF can return a concrete instance.
Product contains two references to other entities, ProductImage and ExtMaterial.
If I run the code:
var product = context.Products.Single(x => x.ItemId == itemId);
to get a product, EF uses a proxy in order to be prepared for when I try to access something like ProductImage.
var imageName = product.ProductImage.Name;
You can disable the proxies using Configuration.ProxyCreationEnabled on the DbContext. (EF6) This does mean that any references will need to be eager loaded or explicitly loaded, as Lazy Loading will not function without the proxies.

LINQ query throw exception on FirstOrDefault method

I'm using EF core, and I have a many-to-many relationship between two entity
IotaProject <--> User
Here's entities & dto related to the question
public class IotaProject
{
[Key]
public int Id { get; set; }
[Required]
public string ProjectName { get; set; }
[Required]
public DateTime Create { get; set; }
public ICollection<ProjectOwnerJoint> Owners { get; set; } = new List<ProjectOwnerJoint>();
}
public class ProjectOwnerJoint
{
public int IotaProjectId { get; set; }
public IotaProject IotaProject { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string ShortName { get; set; }
[Required]
public string Email { get; set; }
public ICollection<ProjectOwnerJoint> OwnedProjects { get; set; } = new List<ProjectOwnerJoint>();
}
public class ApplicationDbContext : DbContext
{
public DbSet<IotaProject> IotaProjects { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<ProjectOwnerJoint> ProjectOwnerJoint { get; set; }
}
public class IotaProjectDisplayDto
{
public int Id { get; set; }
public string ProjectName { get; set; }
public DateTime Create { get; set; }
public UserMinDto Owner { get; set; }
public int Count { get; set; }
public IEnumerable<UserMinDto> Reviewers { get; set; }
}
public class UserMinDto
{
public int Id { get; set; }
public string FullName { get; set; }
public string ShortName { get; set; }
}
Following LINQ is the problem, the LINQ purpose is to convert IotaProject to IotaProjectDisplayDto, and key part is that Owners property of IotaProject is ICollection and Owner property in IotaProjectDisplayDto is just one single element UserMinDto, so I only need to get the first element of IotaProject's Owners and that's FirstOrDefault() comes.
IEnumerable<IotaProjectDisplayDto> results = _db.IotaProjects.Select(x => new IotaProjectDisplayDto
{
Id = x.Id,
ProjectName = x.ProjectName,
Create = x.Create,
Owner = x.Owners.Select(y => y.User).Select(z => new UserMinDto { Id = z.Id, FullName = z.FullName, ShortName = z.ShortName }).FirstOrDefault()
});
return results;
it throws run-time exception
Expression of type 'System.Collections.Generic.List`1[ToolHub.Shared.iota.UserMinDto]' cannot be used for parameter
of type 'System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto]'
of method 'ToolHub.Shared.iota.UserMinDto FirstOrDefault[UserMinDto](System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto])' (Parameter 'arg0')
I'm guessing it's probably related to deferred execution, but after read some posts, I still can't resolve it.
Any tips would be appreciated.
Right now, the only way I can get this work is I change type of Owner property in IotaProjectDisplayDto into IEnumrable, which will no longer need FirstOrDefault() to immediate execution. And later on, I manually get the first element in the client to display.
This issue happened in Microsoft.EntityFrameworkCore.SqlServer 3.0.0-preview7.19362.6
I end up downgrade to EF core stable 2.2.6 as Ivan suggested in comment, and everything works fine.

EF 6 Code First Mapping one-to-many

Can someone please help me with mapping the following hierarchy with EF 6 code first? I can find anything useful for the below example in the documentation.
namespace Contacts {
public class Person
{
public Person()
{
this.Emails = new HashSet<Email>();
}
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Email> Emails { get; set; }
}
public class Company
{
public Company()
{
this.Emails = new HashSet<Email>();
}
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Email> Emails { get; set; }
}
public class Email
{
public string Value { get; set; }
public string Label { get; set; }
public string TargetId { get; set; }
public string TargetType { get; set; }
}
}
TargetType can be set to Company or Person depending on the entity that contains the Email instance.
DB Schema:

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;

MVC4 Entity Framework 6 Model State Validation Issues

I'm working on an MVC4 with EF6 project, and have run into a slightly small but frustrating problem. I have a situation where I have the [Required] attribute set in my dbcontext, however, I do want to allow said property to be okay with empty strings.
I have tried what was suggested in this article, http://www.dzone.com/articles/ef-code-firstmvc, as well as putting [DisplayFormat(ConvertEmptyStringToNull = false)] on my context properties.
When I POST from my login page, the First Name, Last Name, Email, Phone properties are null, which is throwing the ModelState out of whack, even though I've set it to allow those properties to be empty strings. Am I missing something?
Model / DBContext
public class User : Entity
{
[StringLength(200)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public String UserName { get; set; }
[StringLength(250)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public String Password { get; set; }
[StringLength(200)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public String FirstName { get; set; }
[StringLength(200)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public String LastName { get; set; }
[StringLength(200)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public String Email { get; set; }
[StringLength(200)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public String Phone { get; set; }
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(UserViewModel Model)
{
var _UM = Model.User;
var User = _repo.GetSingle<User>(x => x.UserName == _UM.UserName);
if(User != null)
{
if (Hash.ValidatePassword(_UM.Password, User.Password))
{
return RedirectToAction("Dashboard");
}
else
{
ModelState.AddModelError("InvalidPass", "Invalid Credentials");
}
}
else
{
ModelState.AddModelError("NoUser", "Invalid Credentials");
}
return View(Model);
}
If invalid credentials are set, I would expect the ModelState keys to only have one of the items that I am explicity setting. However, it has 6 keys (First Name, Last Name, etc are required).
I ended up solving this by moving my EF context configurations to use the Fluent API instead of Data Annotations.
Model / DBContext
public class User : Entity
{
public String UserName { get; set; }
public String Password { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public String Phone { get; set; }
}
DBContext File
public class DBContext : DbContext
{
public DBContext()
: base("ConString")
{
}
public DbSet<User> Users { get; set; }
public DbSet<UserRole> UserRoles { get; set; }
public DbSet<Region> Regions { get; set; }
public DbSet<InboundFile> InboundFiles { get; set; }
public DbSet<FileType> FileTypes { get; set; }
//configure objects
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().Property(x => x.FirstName).IsRequired().HasMaxLength(200);
modelBuilder.Entity<User>().Property(x => x.LastName).IsRequired().HasMaxLength(200);
modelBuilder.Entity<User>().Property(x => x.Phone).IsRequired().HasMaxLength(200);
modelBuilder.Entity<User>().Property(x => x.Email).IsRequired().HasMaxLength(200);
modelBuilder.Entity<User>().Property(x => x.UserName).IsRequired().HasMaxLength(200);
modelBuilder.Entity<User>().Property(x => x.Password).IsRequired().HasMaxLength(200);
}
}