I have an entity called Client which fails to insert.
Client inherits from Person inherits from Party.
Here is my model classes:
public class Client : Person
{
public Client()
{
CrimeIncidents = new List<CrimeIncident>();
Reseller = new Reseller();
}
public virtual ICollection<CrimeIncident> CrimeIncidents
{
get;
set;
}
public virtual Reseller Reseller { get; set; }
}
public class Person : Party
{
[CustomValidation(typeof(PartyRoleCustomValidation), "ValidateRSAIDNumber")]
public string IDSocialSecurityPassNum { get; set; }
[DisplayName("Name")]
public string NickName { get; set; }
[DisplayName("Full names")]
public string FullNames { get; set; }
public string Surname { get; set; }
public Gender? Gender { get; set; }
[DisplayName("Date of birth")]
[DataType(DataType.Date)]
public string DOB { get; set; }
}
public class Party
{
public Int64 Id { get; set; }
public virtual PartyRole PartyRole
{
get;
set;
}
//public List<Address> Adresses { get; set; }
public virtual string PostalAddress { get; set; }
[DataType(DataType.PostalCode)]
public virtual string PostalCode { get; set; }
public virtual string PhysicalAddress { get; set; }
[DataType(DataType.PostalCode)]
public virtual string StreetCode { get; set; }
//public List<EmailAddress> EmailAdresses { get; set; }
[DataType(DataType.EmailAddress)]
public virtual string EmailAddress { get; set; }
//public List<ContactNumber> ContactNumbers { get; set; }
[DataType(DataType.PhoneNumber)]
public virtual string CellNumber { get; set; }
[DataType(DataType.PhoneNumber)]
public virtual string PhoneNumber { get; set; }
[DataType(DataType.PhoneNumber)]
public virtual string FaxNumber { get; set; }
}
Here is the unit test, which fails on the assert statement (a manual check in the database confirms the unit test outcome):
[TestMethod]
public void AddClient()
{
JCGunsDbContext db = new JCGunsDbContext();
int initCount = db.Clients.Count();
string guid = Guid.NewGuid().ToString();
Client cl1 = new Client();
cl1.NickName = guid;
cl1.Surname = guid;
cl1.CellNumber = "0123456789";
cl1.EmailAddress = "test" + guid + "#test.com";
db.UserId = "SYSTEM_UNITTESTING";
db.Clients.Add(cl1);
db.SaveChanges();
Assert.IsTrue(db.Clients.Count() > initCount);
}
Why is this insert not working and how do I fix it?
You need to pass the Id the the client if this client is connected to an existed Person.
Otherwise you need to add Person object in the client.Person navigation property.
e.g.
Case you're connecting this client to an existed Person.
Client client = new Client();
client.Id = oldPerson.Id
Case you're adding new client with new Person.
Client client = new Client();
client.Person = new Person(){......}
The problem was the following statement within the constructor of Client:
Reseller = new Reseller();
Reseller is supposed to be optional. Setting a new Reseller instance which did not adhere to it's own annotated validation criteria caused the SaveChanges method to fail without throwing an exception. Removing the statement fixed the problem.
Related
I wanted to use a repository option that first retrieves all records but also the includes as well and then select one client based on a given Id using a where clause. I also wanted to include all the notes for this entity.. Client.. as well as its address and Jobs via the includes however if I do try and project the Address to an AddressDto and a collection of ClientNotes to a list of ClientNotesDto it errors with
"Data is Null. This method or property cannot be called on Null
values."
OK so here is the client entity where the clients are loaded from...
using JobsLedger.INTERFACES;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace JobsLedger.DATA.ENTITIES
{
#nullable enable
public class Client : IEntityBase, IAuditedEntityBase
{
public Client()
{
ClientNotes = new List<Note>();
Jobs = new List<Job>();
}
[Key]
public int Id { get; set; }
public string ClientNo { get; set; } = default!;
public bool Company { get; set; }
public string CompanyName { get; set; } = null!;
public string Abn { get; set; } = null!;
public bool IsWarrantyCompany { set; get; }
public bool RequiresPartsPayment { set; get; }
public string ClientFirstName { get; set; } = null!;
public string ClientLastName { get; set; } = null!;
public string Email { get; set; } = null!;
public string MobilePhone { get; set; } = null!;
public string Phone { get; set; } = null!;
public string Address1 { get; set; } = null!;
public string Address2 { get; set; } = null!;
public string BankName { get; set; } = null!;
public string BankBSB { get; set; } = null!;
public string BankAccount { get; set; } = null!;
public bool Active { get; set; }
public DateTime? DateDeActivated { get; set; }
public bool Activity { get; set; }
// One warranty company client to a job.
public int? WarrantyCompanyId { get; set; }
public virtual Job WarrantyCompany { get; set; } = null!;
// One suburb to a client.
public int? SuburbId { get; set; }
public virtual Suburb Suburb { get; set; } = null!;
// If its a warranty company then we simply link it one to one to the brand id.
public virtual Brand Brand { get; set; } = null!;
// Multiple notes for each client.
public virtual ICollection<Note> ClientNotes { get; }
// Multiple jobs for each client.
public virtual ICollection<Job> Jobs { get; set; }
public virtual ICollection<Job> WarrantyCompanyJobs { get; } = default!;
}
#nullable disable
}
Here is the clientDto..
using System.Collections.Generic;
using JobsLedger.MODELS.API.App.Job;
using JobsLedger.MODELS.Common.Address;
using JobsLedger.MODELS.Common.Notes;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Newtonsoft.Json;
namespace JobsLedger.MODELS.API.App.Client
{
public class ClientDetailsDto
{
public ClientDetailsDto()
{
Address = new AddressDto();
//ClientNotes = new List<ClientNoteDto>();
//ClientJobs = new List<ClientJobDto>();
}
[Key]
public int Id { get; set; }
public string ClientNo { get; set; }
public bool Company { get; set; }
public bool IsWarrantyCompany { set; get; }
public string CompanyName { get; set; }
public string ClientFirstName { get; set; }
public string ClientLastName { get; set; }
public string MobilePhone { get; set; }
public string DeActivated { get; set; }
public string CreatedOn { get; set; }
public string CreatedBy { get; set; }
public string ModifiedOn { get; set; }
public string ModifiedBy { get; set; }
public int SuburbId { get; set; }
public AddressDto Address { get; set; }
public ICollection<ClientJobDto> ClientJobs { get; set; }
public IQueryable<ClientNoteDto> ClientNotes { get; set; }
}
}
Yes there are a few properties but its fairly simple.
The repository is using this method to obtain all clients before doing the "Where" clause to get a single client..
public virtual IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = _context.Set<T>();
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query;
}
Now bear in mind I get the actual client and its actually projected to the DTO. However If I set project just to the ClientDto it works.. I get data back for a single client.
If uncomment the Address and ClientNotes projections it fails with an the above error.
I was under the impression that with includes I should actually get the associated or related entities and I am not. Is it because of lazy loaded etc..
Here is the statement:
var result = ClientDATARepository?.AllIncluding(c => c.Jobs, c => c.ClientNotes, c => c.Suburb).Where(x => x.Id == id).Select(fetchedClient =>
new ClientDetailsDto {
Id = fetchedClient.Id,
ClientNo = fetchedClient.ClientNo,
Company = fetchedClient.Company,
IsWarrantyCompany = fetchedClient.IsWarrantyCompany,
CompanyName = fetchedClient.CompanyName,
ClientFirstName = fetchedClient.ClientFirstName,
ClientLastName = fetchedClient.ClientLastName,
MobilePhone = fetchedClient.MobilePhone,
DeActivated = fetchedClient.DateDeActivated.HasValue ? "true" : "false",
CreatedOn = EF.Property<DateTime>(fetchedClient, "CreatedOn").ToString("dd/MM/yyyy", CultureInfo.CurrentCulture),
ModifiedOn = EF.Property<DateTime>(fetchedClient, "ModifiedOn").ToString("dd/MM/yyyy", CultureInfo.CurrentCulture),
Address = // ..I believe it fails here.
new AddressDto {
Address1 = fetchedClient.Address1,
Address2 = fetchedClient.Address2,
SuburbId = fetchedClient.Suburb.Id,
SuburbName = fetchedClient.Suburb.SuburbName,
StateShortName = fetchedClient.Suburb.State.StateShortName,
Postcode = fetchedClient.Suburb.PostCode,
},
ClientNotes = fetchedClient.ClientNotes.Select(fetchedClientNote => // ..and here..
new ClientNoteDto {
id = fetchedClientNote.Id,
Details = fetchedClientNote.Details,
NoteType = NoteTypeDATARepository.GetAll().FirstOrDefault(x => x.Id == fetchedClientNote.NoteTypeId).Name,
CreatedOnDate = EF.Property<DateTime>(fetchedClientNote, "CreatedOn").ToString("dd/MM/yyyy", CultureInfo.CurrentCulture),
CreatedOnTime = EF.Property<DateTime>(fetchedClientNote, "CreatedOn").ToString("h:mm tt", CultureInfo.CurrentCulture),
ModifiedOnDate = EF.Property<DateTime>(fetchedClientNote, "ModifiedOn").ToString("dd/MM/yyyy", CultureInfo.CurrentCulture),
ModifiedOnTime = EF.Property<DateTime>(fetchedClientNote, "ModifiedOn").ToString("h:mm tt", CultureInfo.CurrentCulture),
});
As I mentioned when I try and load Address and ClientNotes its throwing the above error saying data cannot be null..
I'd like to know why I cant load Address and ClientNotes and more importantly how to make it so I do load these.. Further.. why cant I do this and still have it as an IQueryable?
I using Realm database at my Xamarin project
I have realm object with this model
public class UserModel: RealmObject
{
public string Id { get; set;}
public string Email { get; set; }
public string Password { get; set; }
public byte[] UserAvatar { get; set; }
public string ApiKey { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Birthday { get; set; }
public int Country_id { get; set; }
public bool IsAuthorized { get; set; }
public string Base64Avatar { get; set; }
public string Telephone { get; set; }
}
I need to update Name property.
How I try to do this
var realm = Realm.GetInstance();
var user_check = realm.All<UserModel>().First();
user_check.Name = "Test"
and get this error
How I can fix this?
Adding/Updating/Deleting to Realm object must be done inside a transaction, easiest way is to wrap it in Write method.
realm.Write(() =>
{
user_check.Name = "Test";
});
For more info, check the Rleam Write docs
I'm using the MetaDataType Attribute on my domain model class. It it supposed to move the attribute information from the referenced class into the class that the MetadataType attribute has been set.
But it doesn't do as advertised. What is causing the issue here?
[MetadataType(typeof(ComponentModelMetaData))]
public partial class Component
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Repo> Repos { get; set; }
public string Description { get; set; }
}
public class ComponentModelMetaData
{
[Required(ErrorMessage = "Name is required.")]
[StringLength(30, MinimumLength = 3, ErrorMessage = "Name length should be more than 3 symbols.")]
public string Name { get; set; }
public ICollection<Repo> Repos { get; set; }
[Required(ErrorMessage = "Description is required.")]
public string Description { get; set; }
}
ASP.NET Core uses
Microsoft.AspNetCore.Mvc **ModelMetadataType**
instead of
System.ComponentModel.DataAnnotations.**MetadataType**
source
Try changing your attribute to [ModelMetadataType(typeof(ComponentModelMetaData))]
Entity class:
namespace CoreProject.Persistence.EFCore
{
public partial class User
{
public User()
{
Reader = new HashSet<Reader>();
Writer = new HashSet<Writer>();
}
public int UserId { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string PasswordHashKey { get; set; }
public byte Role { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime CreatedUtc { get; set; }
public DateTime LastUpdateUtc { get; set; }
public byte Status { get; set; }
public bool Deleted { get; set; }
public DateTime? ActivatedUtc { get; set; }
public bool Test { get; set; }
public virtual ICollection<Reader> Reader { get; set; }
public virtual ICollection<Writer> Writer { get; set; }
}
}
Metadata (Both have to use the same namespace):
namespace CoreProject.Persistence.EFCore
{
[ModelMetadataType(typeof(IUserMetadata))]
public partial class User : IUserMetadata
{
public string FullName => FirstName + " " + LastName;
}
public interface IUserMetadata
{
[JsonProperty(PropertyName = "Id")]
int UserId { get; set; }
[JsonIgnore]
string Password { get; set; }
[JsonIgnore]
string PasswordHashKey { get; set; }
[JsonIgnore]
byte Role { get; set; }
}
}
Another way... use same namespace
public class ApirolesMetaData
{
[Required]
public string Name { get; set; }
}
[ModelMetadataType(typeof(ApirolesMetaData))]
public partial class Apiroles
{
}
I'm in a similar situation, where the database existed before the coding began. So DB first seemed the natural choice. Generated classes, none of which have annotations. So added MetadataType, and found this stackOverflow page - and so tried ModelMetadataType. I tried it against a class, as well as an interface.
I tried using var validationContext = new ValidationContext(model);
instead of var editContext = new EditContext(model);
and got further when I used var result = Validator.TryValidateObject(model, validationContext, xxxxx, validateAllProperties: true);
I thought I figured it out when I tried editContext.AddDataAnnotationsValidation();
Although this works for a class with annotations, it doesn't work (for me) for partial classes using either [MetadataType] or [ModelMetadataType] attributes.
The only work-around I've found to work (for me) is to create a new class that wraps the model you're wanting to annotate. That means same properties but getters and setters pointing at original model. But at least the annotations will work!
public class Model
{
public int ID { get; set; }
public string Name { get; set; }
}
public class AnnotatedModel
{
private readonly Model model;
public AnnotatedModel(Model model)
{
this.model = model;
}
[Range(1, int.MaxValue)]
public int ID { get => model.ID; set => model.ID = value; }
[Required]
[StringLength(maximumLength: 10, ErrorMessage = "Name can't be longer than 10 characters.")]
public string Name { get => model.Name; set => model.Name = value; }
}
So to use the above, I needed to write this:
var model = new Model();
var annotatedModel = new AnnotatedModel(model);
var editContext = new EditContext(annotatedModel);
editContext.AddDataAnnotationsValidation();
var result = editContext.Validate();
If anyone can tell me what I'm missing, great! Otherwise I hope this work-around will be useful for somebody.
I'm having the strangest problem and I know it's because I'm missing something obvious because this set up works fine when I save data and even pull it down.
My app is set up like so ....
WebUI depends on Business Layer .. Business Layer depends on Data Layer (which is where I'm actually pulling the data). The Business Layer does all the "work".
This is where everything dies (Null Exception, Object Reference not set) as soon as I assign something to userInfo.userData.avatarFilepath (which is not null).
namespace pgl.businesslayer
{
public class userCtx
{
private pgl.datalayer.Concrete.EFDbContext context = new pgl.datalayer.Concrete.EFDbContext();
private pgl.datalayer.Concrete.EFUserContext userContext = new pgl.datalayer.Concrete.EFUserContext();
private pgl.datalayer.Concrete.EFDbCompany companyContext = new pgl.datalayer.Concrete.EFDbCompany();
public ViewModels.UserInfo getUserById(int userId)
{
ViewModels.UserInfo userInfo = new ViewModels.UserInfo();
pgl.datalayer.Dtos.pglUserDTO userDL = userContext.getUserByUserId(userId);
userInfo.userData.avatarFilepath = userDL.avatarFilepath;
userInfo.userData.createdBy = userDL.createdBy;
userInfo.userData.createdDate = userDL.createdDate;
userInfo.userData.email = userDL.email;
userInfo.userData.firstName = userDL.firstName;
userInfo.userData.lastName = userDL.lastName;
etc...
}
ViewModels.UserInfo looks like this...
namespace pgl.businesslayer.ViewModels
{
public class UserInfo
{
// user info
public pgl.businesslayer.Dto.pglUser userData { get; set; }
// salon info
public List<pglSalon> salonsData { get; set; }
// company info
public pglCompany companyData { get; set; }
}
pglUser in the business layer looks like this and is just a POCO
namespace pgl.businesslayer.Dto
{
public class pglUser
{
public int userId { get; set; }
public int companyId { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string email { get; set; }
public string username { get; set; }
public byte[] passwordSalt { get; set; }
public byte[] passwordKey { get; set; }
public DateTime createdDate { get; set; }
public int createdBy { get; set; }
public bool passwordResetRequired { get; set; }
public string passwordHash { get; set; }
public string tempPassword { get; set; }
public string userType { get; set; }
public string avatarFilepath { get; set; }
public string timeZone { get; set; }
}
}
And in userContext this is how I am pulling the user data...
public pgl.datalayer.Dtos.pglUserDTO getUserByUserId(int userId)
{
var getUser = (from u in context.pglUser
select new pgl.datalayer.Dtos.pglUserDTO
{
username = u.username,
companyId = u.companyId,
userId = u.userId,
userType = u.userType,
firstName = u.firstName,
lastName = u.lastName,
email = u.email,
createdDate = u.createdDate,
createdBy = u.createdBy,
passwordResetRequired = u.passwordResetRequired,
tempPassword = u.tempPassword,
avatarFilepath = u.avatarFilepath,
timeZone = u.timeZone
}).Where(u => u.userId == userId).FirstOrDefault();
return getUser;
}
pgl.datalayer.Dtos.pglUserDTO looks like this...
namespace pgl.datalayer.Dtos
{
public class pglUserDTO
{
public int userId { get; set; }
public int companyId { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string email { get; set; }
public string username { get; set; }
public byte[] passwordSalt { get; set; }
public byte[] passwordKey { get; set; }
public DateTime createdDate { get; set; }
public int createdBy { get; set; }
public bool passwordResetRequired { get; set; }
public string passwordHash { get; set; }
public string tempPassword { get; set; }
public string userType { get; set; }
public string avatarFilepath { get; set; }
public string timeZone { get; set; }
}
}
Even if I assign userInfo.userData.avatarFilepath = "WHATUP!!!" it throws the same error. This has got to be something stupid and simple. I can save data with no problem and when I debug I can see that it is actually pulling the correct user ID and it's associated data. It's just that pgl.businesslayer.ViewModels.UserInfo seems to be un-instantiated. I'm at a loss. I can provide more info... and keep in mind I'm kind of at my wits end so I tried doing weird things like adding (probably) unnecessary DTOs. Any ideas?
You don't appear to be creating the userData object anywhere. When you create a new instance of UserInfo, or in its constructor, try adding:
userData = new pgl.businesslayer.Dto.pglUser();
Or alternatively:
ViewModels.UserInfo userInfo = new ViewModels.UserInfo();
pgl.datalayer.Dtos.pglUserDTO userDL = userContext.getUserByUserId(userId);
userInfo.userData = new pgl.businesslayer.Dto.pglUser {
avatarFilepath = userDL.avatarFilepath,
createdBy = userDL.createdBy,
createdDate = userDL.createdDate,
email = userDL.email,
firstName = userDL.firstName,
lastName = userDL.lastName
};
If you always want to initialise UserInfo with an empty userData member, you could include the creation within the constructor:
namespace pgl.businesslayer.ViewModels
{
public class UserInfo
{
// Default constructor
public UserInfo()
{
userData = new pgl.businesslayer.Dto.pglUser();
}
// user info
public pgl.businesslayer.Dto.pglUser userData { get; set; }
// salon info
public List<pglSalon> salonsData { get; set; }
// company info
public pglCompany companyData { get; set; }
}
}
i have models in MVC 5
public class AppUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public PhoneNumbers CellNo { get; set; }
public PhoneNumbers WorkNo { get; set; }
public PhoneNumbers HomeNo { get; set; }
public Address OfficeAddress { get; set; }
}
public class PhoneNumbers
{
public string CountryCode { get; set; }
public string AreaCode { get; set; }
public string Number { get; set; }
}
public class Address
{
public string address { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string State { get; set; }
}
when i want to add data in the model AppUser like
AppUser.WorkNo.CountryCode= array[1];
it gives error
"An exception of type 'System.NullReferenceException' occurred in WebApp.dll but was not handled in user code
Additional information: Object reference not set to an instance of an object."
i think i am doing it wrong please some one guide me to solve this error?
thanks,
When you reference this:
AppUser.WorkNo.CountryCode
The WorkNo property is null. If there was a valid instance of WorkNo, you could reference CountryCode on that instance. But since WorkNo is a reference type it defaults to null unless you instantiate it.
Which you can do in your model's constructor:
public class AppUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public PhoneNumbers CellNo { get; set; }
public PhoneNumbers WorkNo { get; set; }
public PhoneNumbers HomeNo { get; set; }
public Address OfficeAddress { get; set; }
public AppUser()
{
CellNo = new PhoneNumbers();
WorkNo = new PhoneNumbers();
HomeNo = new PhoneNumbers();
OfficeAddress = new Address();
}
}
That way the responsibility of maintaining a valid state of the object is encapsulated within the object itself, instantiating its properties to be used by consuming code.