EF code first uncertainty principle - referred entities may not get loaded - entity-framework

With automatic database recreation, it is uncertain whether referred entities will get loaded or not.
The context is EF CTP 5 and ASP.NET MVC 2. In the global.asax a database initializer is set that forces recreation of the database every time the application starts up.
Successfully retrieving an entity from a context in a controller action may still cause null reference errors when traversing references even if the references are marked as required (not null in the database). Turning off lazy loading does not make any difference.
This behaviour cannot be reproduced reliably but was observed on two different workstations (XP, 7) using Cassini.
Below are the models. Null reference exception is thrown when trying to access the NewsProvider property of the NewsFeed object. It makes no difference taking off the virtual qualifier.
public class NewsProvider
{
public int Id { get; set; }
[Required(ErrorMessage = "Please enter a name")]
[StringLength(50, ErrorMessage = "The name is too long")]
public string Name { get; set; }
}
public class NewsFeed
{
public int Id { get; set; }
[Required(ErrorMessage = "Please enter a name")]
[StringLength(50, ErrorMessage = "The name is too long")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter a URL")]
[StringLength(300, ErrorMessage = "The URL is too long")]
public string Url { get; set; }
[Required(ErrorMessage = "Please enter a news provider")]
public virtual NewsProvider NewsProvider { get; set; }
}

This is just a guess, but complex types can NEVER be null. So if you have any reference to a complex type (ICollection) you should initialize them from the Entity constructor.
Example:
public class NewsProvider
{
public int Id { get; set; }
[Required(ErrorMessage = "Please enter a name")]
[StringLength(50, ErrorMessage = "The name is too long")]
public string Name { get; set; }
}
public class NewsFeed
{
public NewsFeed() {
//Never allow NewsProvider to be null
NewsProvider = new NewsProvider();
}
public int Id { get; set; }
[Required(ErrorMessage = "Please enter a name")]
[StringLength(50, ErrorMessage = "The name is too long")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter a URL")]
[StringLength(300, ErrorMessage = "The URL is too long")]
public string Url { get; set; }
[Required(ErrorMessage = "Please enter a news provider")]
public virtual NewsProvider NewsProvider { get; set; }
}
For more info, here is a great blog post:
http://weblogs.asp.net/manavi/archive/2010/12/11/entity-association-mapping-with-code-first-part-1-one-to-one-associations.aspx

Related

Overriding default Required ErrorMessage property in .NET Core

I have generated my DbContext using EF with DataAnnotations set and would like to override the Required ErrorMessage property using ModelMetadataType
eg
Generated class
public partial class ControlType
{
public ControlType()
{
Rule = new HashSet<Rule>();
}
[Key]
public int ControlTypeId { get; set; }
[Required]
[StringLength(200)]
public string Name { get; set; }
[InverseProperty("ControlType")]
public virtual ICollection<Rule> Rule { get; set; }
}
My extension class
[ModelMetadataType(typeof(ControlTypeMeta))]
public partial class ControlType
{
}
public class ControlTypeMeta
{
[Display(Name = "Id")]
public int ControlTypeId { get; set; }
[Display(Name = "Control type")]
[Required(ErrorMessage = "'{0}' is required")]
public string Name { get; set; }
}
With the above I am expecting the Required ErrorMessage to be output as "'Control type' is required", but instead I get the default "The Control type field is required"
Does anyone know how I can override the message (without creating view models)

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

EF 4.4 InverseProperty does not quite inverse

This might really be something obvious but I'm not sure why my InverseProperty annotation does not work the other way.
I have this 2 classes (simplified):
public class Cluster
{
[Key]
public int ClusterId { get; set; }
[Required]
[MaxLength(80, ErrorMessage = "DimensionCluster Name cannot be more than 80 characters in length.")]
[Display(Name = "DimensionCluster Name")]
public string ClusterName { get; set; }
[InverseProperty("DimensionCluster")]
public virtual IEnumerable<Dimension> Dimensions { get; set; }
}
public class Dimension
{
[Key]
public int DimensionId { get; set; }
[Required]
[MaxLength(80, ErrorMessage = "Dimension Name cannot be more than 80 characters in length.")]
[Display(Name = "Dimension Name")]
public string DimensionName { get; set; }
[Required]
[Display(Name = "Short Definition")]
public string ShortDefinition { get; set; }
[Required]
[Display(Name = "DimensionCluster Name")]
public int ClusterId { get; set; }
[ForeignKey("ClusterId")]
public virtual Cluster DimensionCluster { get; set; }
}
The InverseProperty annotation above does not work. I get:
The InversePropertyAttribute on property 'Dimensions' on type
'PDguide.Models.Cluster' is not valid. The property 'DimensionCluster'
is not a valid navigation property on the related type
'System.Collections.Generic.IEnumerable`1[PDguide.Models.Dimension]'.
Ensure that the property exists and is a valid reference or collection
navigation property.
Tried a lot of MSDN documents and SO answers. And I finally tried it the other way (code below), and it worked!
public class Cluster
{
[Key]
public int ClusterId { get; set; }
[Required]
[MaxLength(80, ErrorMessage = "DimensionCluster Name cannot be more than 80 characters in length.")]
[Display(Name = "DimensionCluster Name")]
public string ClusterName { get; set; }
public virtual IEnumerable<Dimension> Dimensions { get; set; }
}
public class Dimension
{
[Key]
public int DimensionId { get; set; }
[Required]
[MaxLength(80, ErrorMessage = "Dimension Name cannot be more than 80 characters in length.")]
[Display(Name = "Dimension Name")]
public string DimensionName { get; set; }
[Required]
[Display(Name = "Short Definition")]
public string ShortDefinition { get; set; }
[Required]
[Display(Name = "DimensionCluster Name")]
public int ClusterId { get; set; }
[ForeignKey("ClusterId")]
[InverseProperty("Dimensions")]
public virtual Cluster DimensionCluster { get; set; }
}
I have read somewhere (I could not find that reference now, or I may have inferred it incorrectly) that you can specify the InverserProperty annotation on either end of the relationship. But that does not seem to be the case here?
Am I right in my understanding that InverseProperty should work with either property?
You are right. In Programming Entity Framework: Code First
by Lerman and Miller it says on page 72
You can place the annotations on either end of the relationship (or both ends if you
want).
When I look in the current EF source, it seems that only collection properties of type ICollection<T> are recognized as valid inverse properties. So I think that changing the type of your Dimensions property into ICollection<Dimension> would allow you to put the InversePropertyAttribute there as well.

Create ModelStateErrorMessage in mvc2

I create Models for manually showing error. For Example, I have Organization Model. In this i have fields like,
Name
EmailAddress
Mobilenumber
IndustryId.
In the above if any item is not fill in user page, It shows "Name is required" like that.
My code in Model
public class Organization_Validation
{
[Required(ErrorMessage = "Name is required")]
[StringLength(200, ErrorMessage = "Name cannot exceed 50 characters")]
public string Name { get; set; }
[Required(ErrorMessage = "Industry is required")]
[Range(1, int.MaxValue, ErrorMessage = "Industry is required")]
public int IndustryId { get; set; }
[Required(ErrorMessage = "MobileNumber is Required")]
public string MobileNumber { get; set; }
[Required(ErrorMessage = "Email Address is required")]
public string Email { get; set; }
}
Here I want to check Mobilenumber and Email address. Anyone of these two can enter. If user enter Mobilenumber without email means it should not showing error. it will update. How to do this?
You may take a look at FluentValidation.NET which allows you to replace the declarative Data Annotations (which are very limited in such scenarios) with imperative validation rules. It has a great integration with ASP.NET MVC and in addition to that it allows you to easily unit test your validation rules.