I am creating a project using asp.net core web api and cosmos db. I generate the id as GUID value, I auto generate the id.But it create duplicate value.
work.cs file:
public class work
{
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("name")]
public string name { get; set; }
public List<Industy> Industy { get; set; }
public work()
{
if (Id == null)
{
Id = Guid.NewGuid();
}
else
{
Id = Id;
}
}
}
Industry.cs file:
public class Industy
{
[JsonProperty("Id")]
public Guid Id { get; set; }
[JsonProperty("IdustryId")]
public int IdustryId { get; set; }
[JsonProperty("IdustryName")]
public string IdustryName { get; set; }
public Industy()
{
if (Id == null)
{
Id = Guid.NewGuid();
}
else
{
Id = Id;
}
}
}
output:
> {
> "id": "00000000-0000-0000-0000-000000000000",
> "Name": "string",
> "industy": {
> "id": "00000000-0000-0000-0000-000000000000",
> "IdustryId": 0,
}
> }
If i enter more than one value without id it show me the error ,id is already exist. please help me to fix it.
Mark public Guid Id { get; set; } as nullable in both models:
public Guid? Id { get; set; }
Guid is a struct and value type. This means you have to compare with its default value instead of null or mark it as nullable.
#mexanich has good solution. Still you can try one more approach. If you don’t want to change your property to nullable then Update condition like below. Also there is no need for else block. You can eliminate it.
if (Id == default(Guid))
{
Id = Guid.NewGuid();
}
Related
May someone show an example with a partial update entity? All examples into git/docs/stack have only update method, which replaces all fields, even I sending null.
It looks like I need to use HttpPatch and json-patch. But it's so much extra code...
UserEntity
public class User: AbpUser<User>
{
[Required(AllowEmptyStrings = true)]
[StringLength(MaxNameLength)]
public override string Name { get; set; }
[Required(AllowEmptyStrings = true)]
[StringLength(MaxNameLength)]
public override string Surname { get; set; }
public string FatherName { get; set; }
public DateTime BirthDate { get; set; }
public long? DepartmentId { get; set; }
public long? ManagerId { get; set; }
public long? PositionId { get; set; }
public override string FullName => $"{Surname} {Name} {FatherName}";
}
CreateUserDto
{
"name": "Test",
"surname": "Test",
"emailAddress": "test#test.test"
}
UpdateUserDto
{
"id": 1,
"name": "Василий",
"surname": "Пупкин",
"fatherName": "Иванович",
"birthDate": "1993-02-21",
"emailAddress": "vasiliyp#test.test",
"phonenumber": "+79378889911",
"departmentId": 1,
"positionId" : 1
}
Second UpdateUserDto
{
"id": 1,
"name": "Василий",
"surname": "Пупкин",
"fatherName": "Иванович"
}
After the second update I want to get a partial update, but it updates all fields include those I do not send. For example, PositionId will be null, instead of 1.
UserAppService
public override async Task<UserDto> UpdateAsync(UpdateUserDto input)
{
var user = await _userManager.GetUserByIdAsync(input.Id);
MapToEntity(input, user);
return await GetAsync(input);
}
protected override async void MapToEntity(UpdateUserDto input, User user)
{
ObjectMapper.Map(input, user);
user.SetNormalizedNames();
if (!user.DepartmentId.HasValue || user.DepartmentId == 0) return;
var department = await _departmentRepository.GetAsync((long)user.DepartmentId);
user.ManagerId = department.ManagerId;
}
Update
Yesterday I find a way: it needs to customize automapper that to merge models.
Configuration.Modules.AbpAutoMapper().Configurators.Add(
cfg => cfg.ForAllMaps((obj, cnfg) => cnfg.ForAllMembers(
opts => opts.Condition((src, dest, srcMember) => srcMember != null))));
But there are problems:
All value types into DTO must be nullable because the automapper will get default values.
Even though I have defined DateTime as nullable, the automatic conversion converts it to the default (DateTime). I have not yet found a way to fix this without a crutch.
if(input.BirthDate == null) input.BirthDate = user.BirthDate;
UPDATE 2
[AutoMapTo(typeof(User))]
public class UpdateUserDto : EntityDto<long>
{
[StringLength(AbpUserBase.MaxNameLength)]
public string Name { get; set; }
[StringLength(AbpUserBase.MaxSurnameLength)]
public string Surname { get; set; }
public string FatherName { get; set; }
public DateTime? BirthDate { get; set; }
[EmailAddress]
[StringLength(AbpUserBase.MaxEmailAddressLength)]
public string EmailAddress { get; set; }
public string PhoneNumber { get; set; }
public long? DepartmentId { get; set; }
public long? PositionId { get; set; }
}
you can create new Dto with specific fields and create new Update function for that.
As you have pointed out, all values in the update DTO must be declared nullable, or it will be assigned default values.
Then you also need to convert nullable type to using destination value instead.
// example for int, double and DateTime
CreateMap<int?, int>().ConvertUsing((src, dest) => src ?? dest);
CreateMap<double?, double>().ConvertUsing((src, dest) => src ?? dest);
CreateMap<DateTime?, DateTime>().ConvertUsing((src, dest) => src ?? dest);
// ignore if null
CreateMap<UpdateDTO, UpdateEntity>()
.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
I have the classes below:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class ParentEntity
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public ICollection<ChildEntity> ChildEntities { get; set; }
}
public class ChildEntity
{
public Guid Id { get; set; }
public int Vote { get; set; }
public Guid UserId { get; set; }
}
public class ReturnedParentDto
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public int Vote { get; set; }
}
I want to be able to return a full list of ParenEntities, but take an Id of the User class (UserClassId), then filter the ParentEntity's ICollection where UserUid = UserClassId, so only 1 ChildEntity is always returned. Then I would want to extract a specific field from that returned ChildEntity and merge it with the ParentEntity fields. The end result should be like the ReturnedParentDto.
I want to do it in the style like
ParentEntities.Include(v => v.ChildEntities).ToList()
That seems to be possible in EF Core 5, but my project is in 3.1.
You can do this as below
Approach 1:
var result = result = parentEntities.Include(x => x.ChildEntities.Where(y => y.UserId == userId))
.Select(x => new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault()?.Vote // userId is the variable here
});
Approach 2:
var result = parentEntities.Select(x =>
new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault(y => y.UserId == userId)?.Vote // userId is the variable here
});
How do work with a model with a model property inside of it?
I am pulling info from an api successfully but it does not work after I try to change my model from int to model like below:
public class TypeModel
{
[PrimaryKey]
public int pType { get; set; }
public DepartmentModel fDepartment { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Comments { get; set; }
public string Version { get; set; }
}
Here is the department model
public class DepartmentModel
{
public int pDepartment { get; set; }
public string Name { get; set; }
}
My ViewModel had this code and was working. Been trying to make changes as I think I need to change it in here somehow.
Types.Clear();
IEnumerable<TypesModel> types = await DataSource.GetTypesAsync(typeinfo.pType, true);
foreach (var column in types)
{
Types.Add(column);
}
Here is the deserialization from the api.
IEnumerable<TypeModel> TypeEnumerator;
public async Task<IEnumerable<TypeModel>> GetTypesAsync(bool r = false)
{
if (r)
{
var j = await HttpConstructor.GetStringAsync($"api/gettypes");
return await Task.Run(() => JsonConvert.DeserializeObject<IEnumerable<TypeModel>>(j));
}
return TypeEnumerator; ;
}
Here is the json information being produced from the api for types
{
"pType": 10,
"fDepartment": 1,
"title": "Bigwig",
"description": "For the bigwigs",
"comments": "high priority",
"version": "1.2.3"
},
{
"pType": 11,
"fDepartment": 1,
"title": "Frontdesk",
"description": "front end people",
"comments": "none",
"version": "1.2.4"
}
this is what I would do. There are undoubtedly other ways to approach it
public class TypeModel
{
[PrimaryKey]
public int pType { get; set; }
public int fDepartment { get; set; }
public DepartmentModel Department { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Comments { get; set; }
public string Version { get; set; }
}
List<TypesModel> types = await DataSource.GetTypesAsync(typeinfo.pType, true);
foreach (var type in types)
{
type.Department = new DepartmentModel
{
pDepartment = type.fDeparment,
Name = "???"
};
}
Got a solution going by using a Dictionary collection and avoided adjusting the model and mess up the business logic throughout the app.
I kept the original model and created a new one called TypesModel to use for list views.
public class TypesModel
{
[PrimaryKey]
public int pType { get; set; }
public Dictionary<int, string> fDepartment { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Comments { get; set; }
public string Version { get; set; }
}
Then I used Linq join to combine the information and also fill in the dictionary values.
var query = from t in types
join d in departments
on t.fDeparment equals d.pDepartment
select new TypesModel
{
pType = t.pType,
fDepartment = new Dictionary<int, string>()
{
{ d.pDepartment, d.Name }
},
Title = t.Title,
Description = t.Description
};
I am building an MVC5 application and I have the following viewmodels:
public class UserPartyViewModel
{
public UserPartyViewModel()
{
Entitlements = new Collection<AssignedClaims>();
}
public Guid PartyID { get; set; }
public string PartyName { get; set; }
public ICollection<AssignedClaim> AssignedClaims{ get; set; }
}
public class AssignedClaims
{
public AssignedClaims()
{
ClaimValues = new Collection<AssignedClaimValue>();
}
public string Name { get; set; }
public int Max { get; set; }
public int Min { get; set; }
public ICollection<AssignedClaimValue> ClaimValues { get; set; }
}
public class AssignedClaimValue
{
public Guid ClaimValueID { get; set; }
public string ClaimValue { get; set; }
public bool Assigned { get; set; }
}
Contained in the UserPartyViewModel will always be an assignedclaim with a name of "Security" and the assignedclaimvalue with a claimvalue of "User"
If the ClaimValue of user is Assigned then I need to validate the rest of the model. If it is not then no further validation should take place.
Within AssignedClaims there is a min and max, these are the minimum and maximum number of assignedclaimvalues that should be Assigned.
I have tried to use AttributeValidate cannot stop it validating the rest of the model.
I have also looked at the IValidatableObject interface but also can't work out how to control the validation of the child collections depending on the User claim.
What's the best way to achieve this?
Found a solution which appears to do what I want:
public class UserPartyViewModel : IValidatableObject
{
public UserPartyViewModel()
{
Entitlements = new Collection<AssignedClaims>();
}
public string AccessLevel { get; set; }
public Guid PartyID { get; set; }
public string PartyName { get; set; }
public ICollection<AssignedClaims> Entitlements { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var isUser = Entitlements.Any(c => c.Name == "Security" && c.ClaimValues.Any(v => v.Assigned == true && v.ClaimValue == "User"));
if (isUser)
{
int i = 0;
foreach (var result in Entitlements)
{
yield return result.Validate(i++);
}
}
else
{
yield return ValidationResult.Success;
}
}
}
public class AssignedClaims
{
public AssignedClaims()
{
ClaimValues = new Collection<AssignedClaimValue>();
}
public string Name { get; set; }
public string Description { get; set; }
public int Max { get; set; }
public int Min { get; set; }
public ICollection<AssignedClaimValue> ClaimValues { get; set; }
public ValidationResult Validate(int item)
{
int min = Min;
int max = (ClaimValues.Count() < Max) ? ClaimValues.Count() : Max;
int assignedCount = ClaimValues.Where(i => i.Assigned == true).Count();
if (!(min <= assignedCount && assignedCount <= max))
{
string errMessage = String.Format("{2} should have between {0} and {1} Security Claims checked.", min, max, Name);
return new ValidationResult(errMessage, new[] { string.Format("Entitlements[{0}]", item) });
}
else
{
return ValidationResult.Success;
}
}
}
The only issue I had was trying to get the error messages appearing in the correct place. In my view for assignedclaims I added:
#Html.ValidationMessageFor(model => model, "", new { #class = "text-danger" })
and passed the iteration through to the validate function on assignedclaim to ensure it was added to the correct member.
By injecting values into my domain object, I would keep the values of some properties.
Example:
Domain model
public class Person
{
public string Name { get; set; }
public Guid ID { get; set; }
public DateTime CreateAt { get; set; }
public string Notes { get; set; }
public IList<string> Tags { get; set; }
}
View Model
public class PersonViewMode
{
public string Name { get; set; }
public Guid ID { get; set; }
public DateTime CreateAt { get; set; }
public string Notes { get; set; }
public IList<string> Tags { get; set; }
public PersonViewMode() { ID = Guid.NewGuid(); } //You should use this value when it is the Target
}
Sample
var p = new Person
{
ID = Guid.NewGuid() //Should be ignored!
,
Name = "Riderman"
,
CreateAt = DateTime.Now
,
Notes = "teste de nota"
,
Tags = new[] {"Tag1", "Tag2", "Tag3"}
};
var pvm = new PersonViewMode();
pvm.InjectFrom(p); //Should use the ID value generated in the class constructor PersonViewMode
if you delete the set; from from the ViewModel's ID then it won't be set;
otherwise you could save the value of ID in a separate variable and put it back after injecting,
or you can create a custom valueinjection that would ignore "ID" or would receive a list of properties to ignore as a parameter
here's the example for a custom injection that receives a list of property names to ignore:
public class MyInj : ConventionInjection
{
private readonly string[] ignores = new string[] { };
public MyInj(params string[] ignores)
{
this.ignores = ignores;
}
protected override bool Match(ConventionInfo c)
{
if (ignores.Contains(c.SourceProp.Name)) return false;
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Type == c.TargetProp.Type;
}
}
and use it like this:
pvm.InjectFrom(new MyInj("ID"), p);
if you need to ignore more, you can do like this:
pvm.InjectFrom(new MyInj("ID","Prop2","Prop3"), p);