I am trying to add Selector Fields into a grid. These selectors are not bound to any data on the grid as it's just for data entry so there is no filtering. I am wanting to add the Item Class and Price Class on the Shipping Terms grid.
This is my code:
using PX.Data;
using PX.Objects.CM;
using PX.Objects.CS;
using PX.Objects.IN;
using PX.Objects.AR;
using PX.Objects;
using System.Collections.Generic;
using System;
namespace PX.Objects.CS
{
public class ShipTermsDetailExt : PXCacheExtension<PX.Objects.CS.ShipTermsDetail>
{
#region priceCode
public abstract class priceCode : PX.Data.IBqlField
{
}
[PXString]
[PXUIField(DisplayName="Price Code")]
[PXSelector(typeof(Search<ARPriceClass.PriceClassID>),
typeof(ARPriceClass.Description),
SubstituteKey = typeof(ARPriceClass.Description),
ValidateValue = false)]
public virtual string UsrPriceCode { get; set; }
#endregion
#region itemClass
public abstract class itemClass : PX.Data.IBqlField
{
}
[PXString]
[PXUIField(DisplayName="Item Class")]
[PXSelector(typeof(Search<INItemClass.ItemClassID>),
typeof(INItemClass.ItemClassCD),
SubstituteKey = typeof(INItemClass.ItemClassCD),
ValidateValue = false)]
public virtual string UsrItemClass { get; set; }
#endregion
}
}
However I am getting these errors:
Building directory '\WebSiteValidationDomain\App_RuntimeCode\'.
\App_RuntimeCode\ShipTermsDetail.cs(20): error CS0118: 'PX.Objects.AR.ARPriceClass.PriceClassID' is a 'property' but is used like a 'type'
\App_RuntimeCode\ShipTermsDetail.cs(21): error CS0118: 'PX.Objects.AR.ARPriceClass.Description' is a 'property' but is used like a 'type'
\App_RuntimeCode\ShipTermsDetail.cs(22): error CS0118: 'PX.Objects.AR.ARPriceClass.Description' is a 'property' but is used like a 'type'
\App_RuntimeCode\ShipTermsDetail.cs(33): error CS0118: 'PX.Objects.IN.INItemClass.ItemClassID' is a 'property' but is used like a 'type'
\App_RuntimeCode\ShipTermsDetail.cs(34): error CS0118: 'PX.Objects.IN.INItemClass.ItemClassCD' is a 'property' but is used like a 'type'
\App_RuntimeCode\ShipTermsDetail.cs(35): error CS0118: 'PX.Objects.IN.INItemClass.ItemClassCD' is a 'property' but is used like a 'type'
\App_RuntimeCode\ShipTermsDetail.cs(20): error CS0118: 'PX.Objects.AR.ARPriceClass.PriceClassID' is a 'property' but is used like a 'type'
Since these two fields are not bound by what is selected in the first couple of columns, I didn't think a .this or base would be needed but I could be wrong?
Remember to watch the naming convention in Acumatica customizations. I just had to change the fields to lowercase for the first letter.
Here is the fixed code:
using PX.Data;
using PX.Objects.CM;
using PX.Objects.CS;
using PX.Objects.IN;
using PX.Objects.AR;
using PX.Objects;
using System.Collections.Generic;
using System;
namespace PX.Objects.CS
{
public class ShipTermsDetailExt : PXCacheExtension<PX.Objects.CS.ShipTermsDetail>
{
#region priceCode
public abstract class priceCode : PX.Data.IBqlField
{
}
[PXString]
[PXUIField(DisplayName="Price Code")]
[PXSelector(typeof(Search<ARPriceClass.priceClassID>),
typeof(ARPriceClass.description),
SubstituteKey = typeof(ARPriceClass.description),
ValidateValue = false)]
public virtual string UsrPriceCode { get; set; }
#endregion
#region itemClass
public abstract class itemClass : PX.Data.IBqlField
{
}
[PXString]
[PXUIField(DisplayName="Item Class")]
[PXSelector(typeof(Search<INItemClass.itemClassID>),
typeof(INItemClass.itemClassCD),
SubstituteKey = typeof(INItemClass.itemClassCD),
ValidateValue = false)]
public virtual string UsrItemClass { get; set; }
#endregion
}
}
Related
My project throws a convert error when I use fluentvalidation class as a request model.
I have a BaseRequest. It is inherited from AbstractValidatator
public abstract class BaseRequest<TModel> : AbstractValidator<TModel>
{
}
Another model is CreateRuleRequest and it is inherited from BaseRequest. And also it includes some rule for its properties.
public class CreateRuleRequest : BaseRequest<CreateRuleRequest>
{
public CreateRuleRequest()
{
this.RuleFor(k => k.Rules).NotEmpty();
}
public List<RuleModel> Rules { get; set; }
}
And I am using an Extension to load all rules that are inherited from BaseRequest. Actually, AbstractValidator extends from IValidator so that I can catch all the classes that includes rules.
public static IServiceCollection AddValidation(this IServiceCollection services)
{
var validators = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s =>
s.GetTypes())
.Where(t => t.GetInterfaces().Contains(typeof(IValidator))).Where(k =>
k.GetInterfaces().Contains(typeof(IQuery)) ||
k.GetInterfaces().Contains(typeof(ICommand)));
foreach (var validator in validators)
{
services.AddValidatorsFromAssemblyContaining(validator);
}
return services;
}
Lastly, I have a controller that has a Create method. This method is using CreateRuleRequest as a parameter.
public async Task<IActionResult> Create([FromBody] CreateRuleRequest command) =>
and the application throws The JSON value could not be converted CreateRuleRequest. I am wondering why? is it mandatory to create another model binder or is there any other way
I have an entity called "Role" in my asp.net core 6 Web API application. Below is the DTO of the "Role" entity
public class CreateUpdateRoleDto
{
public Guid Id { get; set; }
.....
}
and I have implemented the Command like mentioned below
public class CreateRoleCommand : CreateUpdateRoleDto, IRequest<Guid>
{
}
with the corresponding Validator
public class CreateRoleCommandValidator : AbstractValidator<CreateRoleCommand>
{
}
Now, I want to implement the batch processing for the same "Role" entity and I have implemented the Command like mentioned below
public class CreateRoleBatchCommand : CreateUpdateRoleDto, IRequest
{
}
with its validator
public class CreateRoleBatchCommandValidator : AbstractValidator<CreateRoleBatchCommand>
{
}
CreateRoleBatchCommandValidator is just a copy of the CreateRoleCommandValidator except that it implements IRequest instead of IRequest but I don't think we need this additional validator as this is just a duplicate code.
Below is the implementation of BatchCommand
public class BatchCommand : IRequest
{
public IEnumerable<IRequest> Requests { get; set; }
}
The batch command validator uses the PolymorphicValidator and sets CreateRoleBatchCommandValidator as a validator for CreateRoleBatchCommand.
public class BatchCommandValidator : AbstractValidator<BatchCommand>
{
public BatchCommandValidator()
{
this.RuleForEach(x => x.Requests).SetAsyncValidator(new PolymorphicValidator<BatchCommand, IRequest>()
.Add<CreateRoleBatchCommand>(new CreateRoleBatchCommandValidator()));
}
}
However, I want to set CreateRoleCommand as a validator for CreateRoleBatchCommand, something like this
public class BatchCommandValidator : AbstractValidator<BatchCommand>
{
public BatchCommandValidator()
{
this.RuleForEach(x => x.Requests).SetAsyncValidator(new PolymorphicValidator<BatchCommand, IRequest>()
.Add<CreateRoleBatchCommand>(new CreateRoleCommandValidator()));
}
}
However it throws the following error.
CS1503 Argument 1: cannot convert from 'CreateRoleCommandValidator' to
'FluentValidation.IValidator'
I'm using FluentValidation.WebApi 6.2.1.0 in Web API project. Is there a way to validate enum with FluentValidation and return custom message?
my controller action is as following,
public IHttpActionResult Get([FromUri]CheckUpdateVM info)
{
...
}
My Model,
[Validator(typeof(CheckUpdateVMValidator))]
public class CheckUpdateVM
{
public DeviceTypes Device { get; set; }
}
I'm looing for something like this,
public class CheckUpdateVMValidator : AbstractValidator<CheckUpdateVM>
{
public CheckUpdateVMValidator()
{
RuleFor(x => x.Device).Must(x => Enum.IsDefined(typeof(DeviceTypes), x)).WithMessage("xxx");
}
}
With above code, Model binder validate the value of "Device" parameter and response with an error. but I can't customize the error message. (If I set the "Device" property type to string, this works fine.)
Creating custom validator could be better approach in this scenario.
public class DeviceEnumValidator<T> : PropertyValidator {
public DeviceEnumValidator()
: base("Invalid Enum value!") { }
protected override bool IsValid(PropertyValidatorContext context) {
DeviceTypes enumVal= (DeviceTypes) Enum.Parse(typeof(DeviceTypes), context.PropertyValue);
if (!Enum.IsDefined(typeof(DeviceTypes), enumVal)
return false;
return true;
}
}
To use DeviceEnumValidator you can call SetValidator when defining a validation rule.
public CheckUpdateVMValidator()
{
RuleFor(x => x.Device).SetValidator(new DeviceEnumValidator<DeviceTypes>());
}
I'm using Microsoft.AspNet.Identity 2.0.0-beta1 and Entity Framework 6.1.0-beta1 (released 11 Feb 2014).
I'm getting the following error when I try to change the default type of User ID Primary Key from string to int AND when I try to use custom table names (so User.MyUsers instead of dbo.AspNetUsers):
"The entity types 'IdentityUser' and 'ApplicationUser' cannot share table 'MyUsers' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them."
I can successfully change the default type of User ID PK from string to int OR change the default identity table names but I cannot do both together without hitting this error.
My solution is based on:
1: Section "Make the type of Primary Key be extensible for Users and Roles" from http://blogs.msdn.com/b/webdev/archive/2013/12/20/announcing-preview-of-microsoft-aspnet-identity-2-0-0-alpha1.aspx.
2: How can I change the table names when using Visual Studio 2013 ASP.NET Identity?.
My actual code is:
using Microsoft.AspNet.Identity.EntityFramework;
namespace Musetone.Models
{
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
}
public class CustomRole : IdentityRole<int, CustomUserRole>
{
public CustomRole() { }
public CustomRole(string name) { Name = name; }
}
public class CustomUserRole : IdentityUserRole<int> { }
public class CustomUserClaim : IdentityUserClaim<int> { }
public class CustomUserLogin : IdentityUserLogin<int> { }
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>().ToTable("MyUsers", "User").HasKey(u => u.Id).Property(u => u.Id).HasColumnType("int");
modelBuilder.Entity<ApplicationUser>().ToTable("MyUsers", "User").HasKey(u => u.Id).Property(u => u.Id).HasColumnType("int");
modelBuilder.Entity<IdentityUserRole>().ToTable("MyUserRoles", "User").HasKey(r => new { r.RoleId, r.UserId }).Property(r => r.UserId).HasColumnType("int");
modelBuilder.Entity<IdentityUserLogin>().ToTable("MyUserLogins", "User").HasKey(l => new { l.UserId, l.LoginProvider, l.ProviderKey }).Property(l => l.UserId).HasColumnType("int");
modelBuilder.Entity<IdentityUserClaim>().ToTable("MyUserClaims", "User").Property(c => c.UserId).HasColumnType("int");
modelBuilder.Entity<IdentityRole>().ToTable("MyRoles", "User").HasKey(r => r.Id);
}
}
}
I get this error even if I remove HasColumnType("int").
Any help would be greatly appreciated.
I think the mapping might be incorrect. While defining the ApplicationDbContext class, you are using the custom classes defined for roles, logins and claims for generic but passing the base classes for mapping the tables. For the ChangePK example the following mapping worked for me. Let me know if this works for you too. The mapping should be simple enough
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>().ToTable("MyUsers");
modelBuilder.Entity<CustomUserRole>().ToTable("MyUserRoles");
modelBuilder.Entity<CustomUserLogin>().ToTable("MyUserLogins");
modelBuilder.Entity<CustomUserClaim>().ToTable("MyUserClaims");
modelBuilder.Entity<CustomRole>().ToTable("MyRoles");
}
I have a Model with some complex Properties:
public class TestModel
{
public string Prop1 { get; set; }
public SubClass Prop2 { get; set; }
}
public class SubClass
{
public string Test { get; set; }
}
public class TestModelMetadata : ModelMetadataConfiguration<TestModel>
{
public TestModelMetadata ()
{
Configure(m => m.Prop1).DisplayName("is going to be displayed");
Configure(m => m.Prop2.Test).DisplayName("is NOT going to be displayed");
}
}
When i am trying to display the Model on the View:
#Html.LabelFor(m => m.Prop1)
#Html.LabelFor(m => m.Prop2.Test)
the correct Label for Prop1 is displayed, for Prop2.Test not.
does anybody know a solution for that? thanks!!!!!
Metadata configuration should be created for each type as I know, in your case for SubClass :
public class SubClassMetadata : ModelMetadataConfiguration<SubClass>
{
public SubClassMetadata()
{
Configure(m => m.Test).DisplayName("is going to be displayed1");
}
}
Also you should override display template for object if you want to display model with subclass properties using #Html.DisplayForModel because default mvc (3 - i don't know if it was changed in v4) display template for object skip "ComplexType" properties. The start point for customizing default template can be https://github.com/ASP-NET-MVC/ASP.NET-Mvc-3/blob/master/mvc3/src/MvcFuturesFiles/DefaultTemplates/DisplayTemplates/Object.ascx
Hope it helps.