I am trying to get the Current Cache which gives me null. In the below code, the value of cc is null. Can anyone let me know what wrong am I doing here?
public class SpCaseNewEntryExtension : PXGraphExtension<SPCaseNewEntry>
{
protected PXGraph _Graph = null;
public class CRCaseClassIDSelectorAttribute : PXCustomSelectorAttribute
{
public override void CacheAttached(PXCache sender)
{
_Graph = sender.Graph;
base.CacheAttached(sender);
}
public CRCaseClassIDSelectorAttribute()
: base(typeof(CRCase.caseClassID))
{
this.DescriptionField = typeof(CRCaseClass.description);
}
protected virtual IEnumerable GetRecords()
{
CRCase cc = ((CRCase)_Graph.Caches[typeof(CRCase)].Current);
foreach (PXResult<CRCaseClass> pc in PXSelectJoinGroupBy<CRCaseClass, InnerJoin<CaseContract, On<CaseContract.caseClassID, Equal<CRCaseClass.caseClassID>>,
InnerJoin<Contract, On<CaseContract.contractID, Equal<Contract.templateID>>,
InnerJoin<CRCase, On<Contract.contractID, Equal<Current<CRCase.contractID>>>>>>,
Where<CaseContract.active, Equal<True>>,
Aggregate<GroupBy<CRCaseClass.caseClassID>>>.Select(this._Graph))
{
yield return pc;
}
}
Related
I am using automapper to map expressions between classes that implement IEnumerable. The base classes look like this:
public abstract class EntityDtoBase<T> : DtoBase<T> where T : EntityDtoBase<T>
{
public virtual int Id { get; set; }
}
public abstract class PersistenceDtoBase<T> : DtoBase<T> where T : PersistenceDtoBase<T>
{
public virtual int Id { get; set; }
}
public abstract class DtoBase<T> : IEnumerable<T> where T : DtoBase<T>
{
private readonly IList<T> _items;
public int Count => _items.Count;
protected DtoBase()
{
this._items = new List<T>();
}
public void Add(T item)
{
_items.Add(item);
}
/* other methods like AddRange... */
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _items.GetEnumerator();
}
}
After migrating to Automapper 10, expression mapping between classes enheriting from EntityDtoBase and PersistenceDtoBase throws a System.EntryPointNotFoundException : Entry point was not found. The configuration I am using in my project is similar to the one used in this unit test:
public class UserEntityDto : EntityDtoBase<UserEntityDto> { }
public class UserPersistenceDto : PersistenceDtoBase<UserPersistenceDto> { }
public class UserProfile : Profile
{
public UserProfile() { CreateMap<UserEntityDto, UserPersistenceDto>().ReverseMap(); }
}
public class UnitTest
{
private readonly IMapper _mapper;
public UnitTest()
{
var sp = CreateServices();
_mapper = sp.GetRequiredService<IMapper>();
}
private static IServiceProvider CreateServices()
{
return new ServiceCollection()
.AddAutoMapper(cfg =>
{
cfg.AddExpressionMapping();
cfg.AddCollectionMappers();
cfg.ForAllMaps((map, exp) => exp.MaxDepth(1));
cfg.AllowNullCollections = true;
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
}, typeof(UnitTest).Assembly)
.BuildServiceProvider(false);
}
[Fact]
public void Should_Map_Expression()
{
Expression<Func<UserEntityDto, bool>> searchExpression = u => u.Id == 1;
var searchExpressionMapped = _mapper.Map<Expression<Func<UserPersistenceDto, bool>>>(searchExpression);
Assert.NotNull(searchExpressionMapped);
}
You can find the complete unit test project here. The test succeeds using Automapper 9 and fails using Automapper 10.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using PX.Common;
using PX.Data;
using PX.Objects.GL;
using PX.Objects.CM;
using PX.Objects.CS;
using PX.Objects.CR;
using PX.Objects.TX;
using PX.Objects.IN;
using PX.Objects.EP;
using PX.Objects.AP;
using PX.Objects.AR;
using PX.Objects.SO;
using PX.TM;
using SOOrder = PX.Objects.SO.SOOrder;
using SOLine = PX.Objects.SO.SOLine;
using PX.CS.Contracts.Interfaces;
using PX.Data.DependencyInjection;
using PX.Data.ReferentialIntegrity.Attributes;
using PX.Objects.PM;
using CRLocation = PX.Objects.CR.Standalone.Location;
using PX.Objects.AP.MigrationMode;
using PX.Objects.Common;
using PX.Objects.Common.Discount;
using PX.Objects;
using PX.Objects.PO;
using PX.Objects.CT;
using PX.Data.EP;
namespace PX.Objects.PO
{
public class POOrderEntry_Extension : PXGraphExtension<POOrderEntry>
{
#region Event Handlers
[Serializable]
public class ProjectFilter : IBqlTable
{
#region ContractID
public abstract class contractID : PX.Data.IBqlField { }
//[PXDefault(typeof(PMProject.contractID))]
[PXUIField(DisplayName = "Project ID")]
[Project(DescriptionField = typeof(PMProject.contractCD))]
public int? ContractID { get; set; }
#endregion
#region ProjectTaskID
public abstract class projectTaskID : PX.Data.BQL.BqlInt.Field<projectTaskID> { }
protected Int32? _ProjectTaskID;
[ProjectTask(typeof(PMProject.contractID), AlwaysEnabled = true, DirtyRead = true, IsKey = true)]
public virtual Int32? ProjectTaskID
{
get
{
return this._ProjectTaskID;
}
set
{
this._ProjectTaskID = value;
}
}
#endregion
}
//PXSelect<PMCostBudget, Where<PMCostBudget.projectID, Equal<Current<PMProject.contractID>>, And<PMCostBudget.type, Equal<GL.AccountType.expense>,
// And<Where<Current<CostBudgetFilter.projectTaskID>, IsNull, Or<Current<CostBudgetFilter.projectTaskID>, Equal<PMCostBudget.projectTaskID>>>>>>,
// OrderBy<Asc<PMCostBudget.projectID, Asc<PMCostBudget.projectTaskID, Asc<PMCostBudget.inventoryID, Asc<PMCostBudget.costCodeID, Asc<PMCostBudget.accountGroupID>>>>>>>(this);
//PXDelegateResult delResult = new PXDelegateRes
public PXFilter<ProjectFilter> projectfilter;
[PXFilterable]
[PXCopyPasteHiddenView]
public PXSelect<PMCostBudget,
Where<PMCostBudget.projectID, Equal<Current<ProjectFilter.contractID>>,
//And<PMCostBudget.projectTaskID, Equal<Current<ProjectFilter.projectTaskID>>,
//And<PMProject.locationID, Equal<Current<POOrder.vendorLocationID>>,
And<PMCostBudget.type, Equal<GL.AccountType.expense>>>> getCostBudget;
// public PXOrderedSelect<POOrder, POLine, Where<POLine.orderType, Equal<Current<POOrder.orderType>>, And<POLine.orderNbr, Equal<Optional<POOrder.orderNbr>>>>, OrderBy<Asc<POLine.orderType, Asc<POLine.orderNbr, Asc<POLine.sortOrder, Asc<POLine.lineNbr>>>>>> Transactions;
// PX.Objects.PM.PMProject project = PXSelect<PM.PMProject, Where<PM.PMProject.contractID, Equal<Required<PM.PMProject.contractID>>>>.Select(this, location.Current.VDefProjectID);
//if (project != null)
public PXAction<POOrder> AddProjectCostBudget;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Add Project Cost Budget")]
public virtual IEnumerable addProjectCostBudget(PXAdapter adapter)
{
if (getCostBudget.AskExt() == WebDialogResult.OK)
{
return addSelCostBudget(adapter);
}
return adapter.Get();
}
public PXAction<POOrder> AddSelCostBudget;
[PXUIField(DisplayName = "Add", Visible = false)]
[PXButton(CommitChanges = true)]
public virtual IEnumerable addSelCostBudget(PXAdapter adapter)
{
//Base.Transactions.Cache.ForceExceptionHandling = true;
foreach (PMCostBudget cost in getCostBudget.Cache.Cached)
{
//var costExt = cost.GetExtension<PMBudgetExt>();
if (cost.Selected == true)
{
POLine newline = Base.Transactions.Insert();
newline.ProjectID = cost.ProjectID;
newline.TaskID = cost.ProjectTaskID;
newline.CostCodeID = cost.CostCodeID;
newline.InventoryID = cost.InventoryID;
newline.UOM = cost.UOM;
Base.Transactions.Cache.Update(newline);
Base.Transactions.Update(newline);
}
}
//getCostBudget.Cache.Clear();
return adapter.Get();
}
//public override void Initialize()
//{
// base.Initialize();
// AddProjectCostBudget.SetEnabled(false);
//}
//protected virtual void PMCostBudget_ProjectTaskID_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
//{
// PMCostBudget row = e.Row as PMCostBudget;
// if (row == null) return;
// if (!(e.NewValue is Int32)) return;
// if (e.NewValue != null)
// {
// PXResultset<INLocation> projectLocations = PXSelectReadonly<INLocation,
// Where<INLocation.projectID, Equal<Required<INLocation.projectID>>,
// And2<Where<INLocation.taskID, IsNull>, Or<INLocation.taskID, Equal<Required<INLocation.taskID>>>>>>.Select(Base, row.ProjectID, e.NewValue);
// string taskCD = null;
// PMTask task = PXSelect<PMTask, Where<PMTask.taskID,
// Equal<Required<PMTask.taskID>>>>.Select(Base, row.TaskID);
// if (task != null)
// {
// taskCD = task.TaskCD;
// }
// else
// {
// sender.RaiseExceptionHandling<PMCostBudget.projectTaskID>(row, taskCD, new PXSetPropertyException(Messages.ProjectTaskIsNotAssociatedWithAnyInLocation, PXErrorLevel.Warning));
// }
// }
//}
//protected virtual void PMCostBudget_ProjectTaskID_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
//{
// PMCostBudget cost = e.Row as PMCostBudget;
// if (cost == null) return;
// PMTask task = PXSelect<PMTask,
// Where<PMTask.projectID, Equal<Current<ProjectFilter.contractID>>,
// And<PMTask.isDefault, Equal<True>>>>.Select(Base, cost.ProjectID, cost.ProjectTaskID);
// if (task == null) return;
// sender.SetValue<PMCostBudget.projectTaskID>(cost, task.TaskID);
//}
#endregion
}
}
I had created a pop-up panel to insert Project Cost budget into POLines in Purchase Order screen. every time I want to select and insert, It gives this error
Error: An error occurred during processing of the field Project Task: Error: 'Project Task' cannot be found in the system.
The code snippet of my view:[
[Serializable]
public class ProjectFilter : IBqlTable
{
#region ContractID
public abstract class contractID : PX.Data.IBqlField { }
//[PXDefault(typeof(PMProject.contractID))]
[PXUIField(DisplayName = "Project ID")]
[Project(DescriptionField = typeof(PMProject.contractCD))]
public int? ContractID { get; set; }
#endregion
#region ProjectTaskID
public abstract class projectTaskID : PX.Data.BQL.BqlInt.Field<projectTaskID> { }
protected Int32? _ProjectTaskID;
[ProjectTask(typeof(PMProject.contractID), AlwaysEnabled = true, DirtyRead = true, IsKey = true)]
public virtual Int32? ProjectTaskID
{
get
{
return this._ProjectTaskID;
}
set
{
this._ProjectTaskID = value;
}
}
#endregion
}
public PXFilter<ProjectFilter> projectfilter;
[PXFilterable]
[PXCopyPasteHiddenView]
public PXSelect<PMCostBudget,
Where<PMCostBudget.projectID, Equal<Current<ProjectFilter.contractID>>,
And<PMCostBudget.projectTaskID, Equal<Current<ProjectFilter.projectTaskID>>,
And<PMCostBudget.type, Equal<GL.AccountType.expense>>>>> getCostBudget;
]1 this is the error image
The ProjectTask attribute expects a reference to the currently selected project; you need to use ProjectFilter.projectID instead of PMProject.projectID
Build 18.203.0006
Page: AP301000
Good day, I have extended the APRegister Class by adding 2 new checkbox fields. I want to iterate through al the APTran transactions and look for suIDs that start with FBL and GAS. If I find them the new tick boxes should tick.
Currently, the fields(tick boxes) do not save to the database. I am not sure how to tell Acumatica the APRegisterExt has updated.
APInvoiceEntry_Extension:
namespace PX.Objects.AP
{
public class APRegisterExt : PXCacheExtension<PX.Objects.AP.APRegister>
{
#region UsrGroupAEmail
[PXDBBool]
[PXUIField(DisplayName="GroupA Email")]
public virtual bool? UsrGroupAEmail { get; set; }
public abstract class usrGroupAEmail : IBqlField { }
#endregion
#region UsrGroupBEmail
[PXDBBool]
[PXUIField(DisplayName="GroupB Email")]
public virtual bool? UsrGroupBEmail { get; set; }
public abstract class usrGroupBEmail : IBqlField { }
#endregion
}
}
APInvoiceEntry_Extension:
namespace PX.Objects.AP
{
public class APInvoiceEntry_Extension : PXGraphExtension<APInvoiceEntry>
{
#region Event Handlers
protected void APInvoice_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
APInvoice invoice = e.Row as APInvoice;
if (invoice == null) return;
var apRX = invoice.GetExtension<APRegisterExt>();
PXResultset<APTran> Tlist = PXSelectJoin<APTran,
LeftJoin<POReceiptLine,
On<POReceiptLine.receiptNbr, Equal<APTran.receiptNbr>,
And<POReceiptLine.lineNbr, Equal<APTran.receiptLineNbr>>>>,
Where<
APTran.tranType, Equal<Current<APInvoice.docType>>,
And<APTran.refNbr, Equal<Current<APInvoice.refNbr>>>>,
OrderBy<
Asc<APTran.tranType,
Asc<APTran.refNbr,
Asc<APTran.lineNbr>>>>>.Select(Base);
apRX.UsrGroupBEmail = false;
apRX.UsrGroupAEmail = false;
foreach (APTran item in Tlist)
{
if (item.SubID.Value.ToString().StartsWith("FBL") || item.SubID.Value.ToString().StartsWith("GAS"))
{
apRX.UsrGroupBEmail = true;
cache.SetValue<APRegisterExt.usrGroupBEmail>(e.Row, true);
}
else
{
apRX.UsrGroupAEmail = true;
}
}
}
#endregion
}
}
Can someone please show me the correct way of saving the data to the new tick boxes so that cache updates.
Consider moving the logic to APRegister_RowPersisting.
Importantly, APTran.subID is an Int dataType.
Read instead the Sub table/DAC during a For Loop of
Base.Transactions.Select() to detect SubCD startsWith FBL or GAS.
foreach(APTran item in Base.Transaction.Select())
{
Sub sub = PXSelect<Sub,
Where<Sub.subID, Equal<Required<Sub.subID>>>>
.Select(graph, item.SubID);
if (item.SubCD.StartsWith("FBL") ||
item.SubCD.StartsWith("GAS"))
{
apRX.UsrGroupBEmail = true;
}
else
{
apRX.UsrGroupAEmail = true;
}
}
To be complete, you may also need to reset both flags during APTran_SubID_FieldUpdated regardless of the changed value:
protected void APTran_SubID_FieldUpdated(PXCache sender,
PXFieldUpdatedEventArgs e, PXFieldUpdated del)
{
del?.Invoke(sender,e);
var apRX = Base.Document.Current.GetExtension<APRegisterExt>();
apRX.UsrGroupBEmail = false;
apRX.UsrGroupAEmail = false;
}
I'm using Asp.Net Core Console Application and Entiy Framework Core and Unit of Work repository pattern. When I'm using multi thread function, I get this error:
DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point. This can happen if a second operation is started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
UnitOfwork.cs
public interface IUnitOfWork : IDisposable
{
void Commit();
ApplicationDbContext GetContext();
}
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _applicationDbContext;
public UnitOfWork(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
}
public void Commit()
{
try
{
_applicationDbContext.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public ApplicationDbContext GetContext()
{
return _applicationDbContext;
}
public void Dispose()
{
_applicationDbContext.Dispose();
}
}
IRepository.cs
public interface IGenericRepository<T>
where T : class, IEntity
{
List<T> GetAll(Expression<Func<T, bool>> filter = null,
Func<IQueryable<T>, IOrderedEnumerable<T>> orderBy = null,
string includeProperties = "");
T FindSingle(int id);
T FindBy(Expression<Func<T, bool>> predicate, string includeProperties = "");
void Add(T toAdd);
void Update(T toUpdate);
void Delete(int id);
void Delete(T entity);
}
Repository.cs
public class GenericRepository<T> : IGenericRepository<T>
where T : class, IEntity
{
private readonly IUnitOfWork _unitOfWork;
public GenericRepository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public virtual List<T> GetAll(Expression<Func<T, bool>> filter = null,
Func<IQueryable<T>, IOrderedEnumerable<T>> orderBy = null,
string includeProperties = "")
{
IQueryable<T> query = _unitOfWork.GetContext().Set<T>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (string includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
return query.ToList();
}
public virtual T FindSingle(int id)
{
return _unitOfWork.GetContext().Set<T>().Find(id);
}
public virtual T FindBy(Expression<Func<T, bool>> predicate, string includeProperties = "")
{
IQueryable<T> query = _unitOfWork.GetContext().Set<T>();
foreach (string includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
return query.Where(predicate).FirstOrDefault();
}
public virtual void Add(T toAdd)
{
_unitOfWork.GetContext().Set<T>().Add(toAdd);
}
public virtual void Update(T toUpdate)
{
_unitOfWork.GetContext().Entry(toUpdate).State = EntityState.Modified;
}
public virtual void Delete(int id)
{
T entity = FindSingle(id);
_unitOfWork.GetContext().Set<T>().Remove(entity);
}
public virtual void Delete(T entity)
{
_unitOfWork.GetContext().Set<T>().Remove(entity);
}
}
Business Services;
public interface IUserService
{
void CreateUser(UserEntity userEntity, bool commit = false);
}
public class UserService : IUserService
{
private readonly IGenericRepository<UserEntity> _userRepository;
private readonly IUnitOfWork _unitOfWork;
public UserService(IUnitOfWork unitOfWork, IGenericRepository<UserEntity> userRepository)
{
_unitOfWork = unitOfWork;
_userRepository = userRepository;
}
public void CreateUser(UserEntity userEntity, bool commit = false)
{
try
{
_userRepository.Add(userEntity);
if (commit)
_unitOfWork.Commit();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console Main.cs;
class Program
{
public static ServiceProvider ServiceProvider;
static void Main(string[] args)
{
InitializeIoc();
Task.Run(() => { FuncA(); });
Task.Run(() => { FuncB(); });
Console.ReadLine();
}
private static void InitializeIoc()
{
ServiceProvider = new ServiceCollection()
.AddDbContext<ApplicationDbContext>()
.AddTransient<IUnitOfWork, UnitOfWork>()
.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>))
.AddTransient<IUserService, UserService>()
.BuildServiceProvider();
}
private static void FuncA()
{
var userService = ServiceProvider.GetService<IUserService>();
for (int i = 0; i < 100; i++)
{
userService.CreateUser(new UserEntity { FirstName = "FuncA_" + Guid.NewGuid(), LastName = "Last", CreatedDate = DateTime.Now }, false);
}
}
private static void FuncB()
{
var userService = ServiceProvider.GetService<IUserService>();
for (int i = 0; i < 100; i++)
{
userService.CreateUser(new UserEntity { FirstName = "FuncB_" + Guid.NewGuid(), LastName = "Last", CreatedDate = DateTime.Now }, false);
}
}
}
How can i solve this problem?
Thank you for your help.
The problem is that the used AddDbContext registers your ApplicationDbContext with ServiceLifetime.Scoped, but you are not creating scopes hence it effectively works as singleton, thus is shared and accessed concurrently by multiple threads which causes the exception in question (and potentially many other because DbContext is not thread safe).
The solution is to use scopes, e.g. call CreateScope and use returned object ServiceProvider property for resolving services:
private static void FuncA()
{
using (var scope = ServicePropvider.CreateScope())
{
var userService = scope.ServiceProvider.GetService<IUserService>();
// Do something ...
}
}
private static void FuncB()
{
using (var scope = ServicePropvider.CreateScope())
{
var userService = scope.ServiceProvider.GetService<IUserService>();
// Do something ...
}
}
Seeing a strange problem, getting empty metata pages for xml,json and jvs.
Using the following command line app. How does one debug these issues?
namespace ConsoleApplication2
{
public struct NativeUser
{
public int login;
public string group;
public string name;
}
[DataContract]
public class User
{
private NativeUser _native;
public User() { }
public User(NativeUser native)
{
_native = native;
}
public static implicit operator NativeUser(User user)
{
return user._native;
}
public static implicit operator User(NativeUser native)
{
return new User(native);
}
// ReSharper disable InconsistentNaming
[DataMember]
public int login
{
get { return _native.login; }
set { _native.login = value; }
}
[DataMember]
public string group
{
get { return _native.group; }
set { _native.group = value; }
}
[DataMember]
public string name
{
get { return _native.name; }
set { _native.name = value; }
}
}
[Description("GET account, all or by list of groups or by list of logins")]
[Route("/accounts/{groups}", "GET")]
[Route("/accounts/{logins}", "GET")]
[Route("/accounts/", "GET")]
public class Accounts : IReturn<User[]>
{
public string[] groups { set; get; }
public int[] logins { set; get; }
public Accounts() { }
public Accounts(params int[] logins)
{
this.logins = logins;
}
public Accounts(params string[] groups)
{
this.groups = groups;
}
}
public class Host : AppHostHttpListenerBase
{
public Host() : base("Test",
typeof(Accounts).Assembly)
{
}
public override void Configure(Funq.Container container)
{
}
}
public class Servce : IService
{
public object Get(Accounts request)
{
return new List<User>(){new User(new NativeUser())};
}
}
class Program
{
static void Main(string[] args)
{
var host = new Host();
host.Init();
host.Start("http://+:12345/");
global::System.Console.ReadLine();
}
}
}
Nm, found the bug :
public class Accounts : IReturn<User[]>
needs to be
public class Accounts : IReturn<List<User>>
Another very note worthy thing: All DTO's and objects being passed back and fourth in the DTO's require an empty constructor in order for the metata data to be properly generated.
Not sure if this is by design or a bug