Automapper: map an object inside a Profile using a parameter - automapper

I have the following issue using ASP.NET Core 2.2 and Automapper 9.0.0:
I have a Entity that I map to a dto, this works fine. Inside that entity are a few entities as well. These cannot be flattened as they are also needed by our client. There is however one property within those nested entities that need additional mapping.
within the Profile class of the overlapping Entity i do this:
public class OperationalToPalletResultProfile : Profile
{
public OperationalToPalletResultProfile()
{
string lang = null;
CreateMap<TblDatOperationalHUTrace, PalletResult>()
.ForMember(dest => dest.TransCode, act => act.MapFrom((src, _, transactionCode, ctx) =>
ctx.Mapper.Map<TblLstCode, TransactionCodeResult>(src.TransCode)));
}
}
This effectively maps the TblLstCode to TransactionCodeResult correctly. However the "DisplayDesc" property of the TransactionCodeResult remains null...
Note: the "lang" property gets set with the "ProjectTo" method:
var values = await query
.Take(5000)
.ProjectTo<PalletResult>(mapper.ConfigurationProvider, new { lang = language })
.ToListAsync()
.ConfigureAwait(false);
I have tried:
1- Using "AfterMap" in Profile class (result: DisplayDesc = null)
CreateMap<TblDatOperationalHUTrace, PalletResult>()
.ForMember(dest => dest.TransCode, act => act.MapFrom((src, _, transactionCode, ctx) =>
ctx.Mapper.Map<TblLstCode, TransactionCodeResult>(src.TransCode, opt =>
opt.AfterMap((source, destination) =>
destination.DisplayDesc = $"[{destination.ShortName}] {destination.DescToLanguageDesc(destination, lang)}"))))
Note: DescToLanguageDesc is a function that finds a property in the entity with reflection (this works and is not part of the problem)
2- Creating an IMappingAction to use in the Profile of the SubEntity (result: DisplayDesc = null)
public class TransactionCodeTranslation : IMappingAction<TblLstCode, TransactionCodeResult>
{
private readonly IHttpContextAccessor httpContextAccessor;
public TransactionCodeTranslation(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void Process(TblLstCode source, TransactionCodeResult destination, ResolutionContext context)
{
//Get language from httpContext - this works correctly
destination.DisplayDesc = $"[{source.ShortName}] {source.DescToLanguageDesc(source, language)}";
}
}
public class TransactionCodeProfile : Profile
{
public TransactionCodeProfile()
{
string language = string.Empty;
CreateMap<TblLstCode, TransactionCodeResult>()
.AfterMap<TransactionCodeTranslation>();
}
}
This doesn't work. HOWEVER if I use option 2 directly:
var tc = await codeRepo.TableNoTracking.FirstOrDefaultAsync(x => x.ShortName == "[something]").ConfigureAwait(false);
var transactionCode = mapper.Map<TblLstCode, TransactionCodeResult>(tc);
Then it works correctly! But that would mean i would have to loop my result and map every object in the result again...
Is there a way to do it like option 1?
Thank you!
Edit 1:
Per Lucian Bargaoanu's request i added a BuildExecutionPlan:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TblDatOperationalHUTrace, PalletResult>()
.ForMember(dest => dest.TransCode, act => act.MapFrom((src, _, transactionCode, ctx) =>
ctx.Mapper.Map<TblLstCode, TransactionCodeResult>(src.TransCode, opt =>
opt.AfterMap((source, destination) =>
destination.DisplayDesc = $"[{destination.ShortName}] {destination.DescToLanguageDesc(destination, language)}"))));
cfg.CreateMap<TblLstCode, TransactionCodeResult>()
.AfterMap<TransactionCodeTranslation>();
});
var expression = config.BuildExecutionPlan(typeof(TblDatOperationalHUTrace), typeof(PalletResult));
var expression2 = config.BuildExecutionPlan(typeof(TblLstCode), typeof(TransactionCodeResult));
Result of expression:
(src, dest, ctxt) =>
{
PalletResult typeMapDestination;
return (src == null)
? null
: {
typeMapDestination = dest ?? new PalletResult();
try
{
var resolvedValue = mappingFunction.Invoke(
src,
typeMapDestination,
typeMapDestination.TransCode,
ctxt);
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.TransCode = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return null;
}
return typeMapDestination;
};
}
Result Expression2:
(src, dest, ctxt) =>
{
TransactionCodeResult typeMapDestination;
return (src == null)
? null
: {
typeMapDestination = dest ?? new TransactionCodeResult();
try
{
var resolvedValue = ((src == null) || false) ? default(int) : src.Id;
typeMapDestination.Id = resolvedValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return default(int);
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.ShortName;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.ShortName = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? default(int) : src.CodeTypeId;
typeMapDestination.CodeTypeId = resolvedValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return default(int);
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.DescLC;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescLC = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.DescEN;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescEN = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.DescFR;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescFR = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.DescGE;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescGE = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return null;
}
try
{
var resolvedValue = ((src == null) || false) ? null : src.DescNL;
var propertyValue = (resolvedValue == null) ? null : resolvedValue;
typeMapDestination.DescNL = propertyValue;
}
catch (Exception ex)
{
throw new AutoMapperMappingException(
"Error mapping types.",
ex,
AutoMapper.TypePair,
TypeMap,
PropertyMap);
return null;
}
afterFunction.Invoke(src, typeMapDestination, ctxt);
return typeMapDestination;
};
}
EDIT 2:
Ok, I actually found the solution. I was overcomplicating things for no reason...
I changed OperationalToPalletResultProfile to:
public class OperationalToPalletResultProfile : Profile
{
public OperationalToPalletResultProfile()
{
CreateMap<TblDatOperationalHUTrace, PalletResult>();
}
}
The TransactionCodeProfile remained the same:
public class TransactionCodeProfile : Profile
{
public TransactionCodeProfile()
{
CreateMap<TblLstCode, TransactionCodeResult>()
.AfterMap<TransactionCodeTranslation>();
}
}
And then where i use my mapping i changed this:
//results as TblDatOperationalHUTrace
var queryresult = await query
.Take(5000)
//.ProjectTo<PalletResult>(mapper.ConfigurationProvider) Don't do the mapping here anymore
.ToListAsync()
.ConfigureAwait(false);
//Map results to PalletResult
var values = mapper.Map<List<TblDatOperationalHUTrace>, List<PalletResult>>(queryresult); //Do the mapping here

Ok i actually found the solution myself:
I changed OperationalToPalletResultProfile to:
public class OperationalToPalletResultProfile : Profile
{
public OperationalToPalletResultProfile()
{
CreateMap<TblDatOperationalHUTrace, PalletResult>();
}
}
The TransactionCodeProfile remained the same:
public class TransactionCodeProfile : Profile
{
public TransactionCodeProfile()
{
CreateMap<TblLstCode, TransactionCodeResult>()
.AfterMap<TransactionCodeTranslation>();
}
}
And then where i use my mapping i changed this:
//results as TblDatOperationalHUTrace
var queryresult = await query
.Take(5000)
//.ProjectTo<PalletResult>(mapper.ConfigurationProvider) Don't do the mapping here anymore
.ToListAsync()
.ConfigureAwait(false);
//Map results to PalletResult
var values = mapper.Map<List<TblDatOperationalHUTrace>, List<PalletResult>>(queryresult); //Do the mapping here
And then it works like it's supposed to.

Related

Create user inside a transaction

When creating a user like so:
using var trans = Db.BeginTransaction();
AuthRepository.CreateUserAuth(newUser, request.Password);
AuthRepository.AssignRoles(created, new List<string> { request.role });
//.. do other stuff
throw new Exception("other code may throw this");
trans.Commit();
The Auth repo has it's own connection so it's not part of the transaction. This means, if my code bails out, I end up with an unwanted user.
Is there any way to use a transaction with AuthRepository or is only way to manually write to user and role tables? I couldn't find an example of creating the password hash in docs when manually saving, is there any example?
You can't use the existing Auth Repository APIs within a transaction because each uses its own DB connection.
If you want you can take the implementation of those OrmLite Auth Respository APIs in OrmLiteAuthRepository.cs and move them into your method so they're all using the same DB Connection + Transaction.
Also note that to use Transactions in OrmLite you should use OpenTransaction() instead, e.g:
using (var dbTrans = db.OpenTransaction())
{
}
Adding class I used here in case it helps anyone as needed a little refactoring:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using OutReachPete.ServiceModel.User;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.OrmLite;
namespace OutReachPete.ServiceInterface.User
{
public class UserHelper
{
public static bool ForceCaseInsensitiveUserNameSearch { get; set; } = true;
public static bool UseDistinctRoleTables { get; set; } = true;
public static UserAuthCustom CreateUserAuth(UserAuthCustom newUser, string password, IDbConnection db)
{
newUser.ValidateNewUser(password);
AssertNoExistingUser(db, newUser);
newUser.PopulatePasswordHashes(password);
newUser.CreatedDate = DateTime.UtcNow;
newUser.ModifiedDate = newUser.CreatedDate;
db.Save(newUser);
newUser = db.SingleById<UserAuthCustom>(newUser.Id);
return newUser;
}
public static void AssertNoExistingUser(IDbConnection db, IUserAuth newUser,
IUserAuth exceptForExistingUser = null)
{
if (newUser.UserName != null)
{
var existingUser = GetUserAuthByUserName(db, newUser.UserName);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format(ErrorMessages.UserAlreadyExistsTemplate1,
newUser.UserName.SafeInput()));
}
if (newUser.Email != null)
{
var existingUser = GetUserAuthByUserName(db, newUser.Email);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format(ErrorMessages.EmailAlreadyExistsTemplate1,
newUser.Email.SafeInput()));
}
}
public static UserAuthCustom GetUserAuthByUserName(IDbConnection db, string userNameOrEmail)
{
var isEmail = userNameOrEmail.Contains("#");
var lowerUserName = userNameOrEmail.ToLower();
UserAuthCustom userAuth = null;
// Usernames/Emails are saved in Lower Case so we can do an exact search using lowerUserName
if (HostContext.GetPlugin<AuthFeature>()?.SaveUserNamesInLowerCase == true)
{
return isEmail
? db.Select<UserAuthCustom>(q => q.Email == lowerUserName).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName == lowerUserName).FirstOrDefault();
}
// Try an exact search using index first
userAuth = isEmail
? db.Select<UserAuthCustom>(q => q.Email == userNameOrEmail).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName == userNameOrEmail).FirstOrDefault();
if (userAuth != null)
return userAuth;
// Fallback to a non-index search if no exact match is found
if (ForceCaseInsensitiveUserNameSearch)
{
userAuth = isEmail
? db.Select<UserAuthCustom>(q => q.Email.ToLower() == lowerUserName).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName.ToLower() == lowerUserName).FirstOrDefault();
}
return userAuth;
}
public static void AssignRoles(IAuthRepository userAuthRepo, IUserAuth userAuth, IDbConnection db,
ICollection<string> roles = null, ICollection<string> permissions = null)
{
if (userAuthRepo is IManageRoles managesRoles)
{
AssignRoles(userAuth.Id.ToString(), db, roles, permissions);
}
else
{
AssignRolesInternal(userAuth, roles, permissions);
SaveUserAuth(userAuth, db);
}
}
public static IUserAuth GetUserAuth(string userAuthId, IDbConnection db)
{
if (string.IsNullOrEmpty(userAuthId))
throw new ArgumentNullException(nameof(userAuthId));
return db.SingleById<UserAuthCustom>(int.Parse(userAuthId));
}
public static void AssignRoles(string userAuthId, IDbConnection db, ICollection<string> roles = null, ICollection<string> permissions = null)
{
var userAuth = GetUserAuth(userAuthId, db);
if (!UseDistinctRoleTables)
{
if (!roles.IsEmpty())
{
foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
{
if (userAuth.Roles == null)
userAuth.Roles = new List<string>();
userAuth.Roles.Add(missingRole);
}
}
if (!permissions.IsEmpty())
{
foreach (var missingPermission in permissions.Where(x => userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
{
if (userAuth.Permissions == null)
userAuth.Permissions = new List<string>();
userAuth.Permissions.Add(missingPermission);
}
}
SaveUserAuth(userAuth, db);
}
else
{
var now = DateTime.UtcNow;
var userRoles = db.Select<UserAuthRole>(q => q.UserAuthId == userAuth.Id);
if (!roles.IsEmpty())
{
var roleSet = userRoles.Where(x => x.Role != null).Select(x => x.Role).ToHashSet();
foreach (var role in roles)
{
if (!roleSet.Contains(role))
{
db.Insert(new UserAuthRole
{
UserAuthId = userAuth.Id,
Role = role,
CreatedDate = now,
ModifiedDate = now,
});
}
}
}
if (!permissions.IsEmpty())
{
var permissionSet = userRoles.Where(x => x.Permission != null).Select(x => x.Permission).ToHashSet();
foreach (var permission in permissions)
{
if (!permissionSet.Contains(permission))
{
db.Insert(new UserAuthRole
{
UserAuthId = userAuth.Id,
Permission = permission,
CreatedDate = now,
ModifiedDate = now,
});
}
}
}
}
}
private static void AssignRolesInternal(IUserAuth userAuth, ICollection<string> roles, ICollection<string> permissions)
{
if (!roles.IsEmpty())
{
foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
{
if (userAuth.Roles == null)
userAuth.Roles = new List<string>();
userAuth.Roles.Add(missingRole);
}
}
if (!permissions.IsEmpty())
{
foreach (var missingPermission in permissions.Where(x =>
userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
{
if (userAuth.Permissions == null)
userAuth.Permissions = new List<string>();
userAuth.Permissions.Add(missingPermission);
}
}
}
public static void SaveUserAuth(IUserAuth userAuth, IDbConnection db)
{
if (userAuth == null)
throw new ArgumentNullException(nameof(userAuth));
userAuth.ModifiedDate = DateTime.UtcNow;
if (userAuth.CreatedDate == default(DateTime))
userAuth.CreatedDate = userAuth.ModifiedDate;
db.Save((UserAuthCustom)userAuth);
}
}
}
Then just call it like:
var created = UserHelper.CreateUserAuth(newUser, request.Password, Db);
UserHelper.AssignRoles(AuthRepository, created, Db, new List<string> { request.CreateUserType.ToString() });
It will only use the connection passed. Just change UserAuthCustom to whatever you user class is.

Sending notification to requester when PO is created in Acumatica

I need to be able to send an email to the original requester when a PO is created from a Requisition in Acumatica 6.1.
Per Acumatica, the Notification screen cannot handle this functionality, so I have this code to extend the POOrder Entry graph, which sends an email to the Customer's contact email from the Requisition when a PO is created (along with the RQRequisitionEntryExt trigger):
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
private bool sendEmailNotification = false;
public bool SendEmailNotification
{
get
{
return sendEmailNotification;
}
set
{
sendEmailNotification = value;
}
}
[PXOverride]
public void Persist(Action del)
{
using (var ts = new PXTransactionScope())
{
if (del != null)
{
del();
}
if (SendEmailNotification)
{
bool sent = false;
string sError = "Failed to send E-mail.";
try
{
Notification rowNotification = PXSelect<Notification,
Where<Notification.name, Equal<Required<Notification.name>>>>
.Select(Base, "PurchaseOrderNotification");
if (rowNotification == null)
throw new PXException("Notification Template was not found.");
var order = Base.Document.Current;
var requisition = (RQRequisition)PXSelect<RQRequisition,
Where<RQRequisition.reqNbr, Equal<Current<POOrder.rQReqNbr>>>>
.SelectSingleBound(Base, new object[] { order });
if (requisition.CustomerID != null)
{
var customer = (BAccountR)PXSelectorAttribute.Select<RQRequisition.customerID>(
Base.Caches[typeof(RQRequisition)], requisition);
if (customer != null)
{
var defCustContact = (Contact)PXSelectorAttribute.Select<BAccountR.defContactID>(
Base.Caches[typeof(BAccountR)], customer);
if (String.IsNullOrEmpty(defCustContact.EMail))
throw new PXException("E-mail is not specified for Customer Contact.");
var sender = TemplateNotificationGenerator.Create(order,
rowNotification.NotificationID.Value);
sender.RefNoteID = order.NoteID;
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = defCustContact.EMail;
sent |= sender.Send().Any();
}
}
}
catch (Exception Err)
{
sent = false;
sError = Err.Message;
}
if (!sent)
throw new PXException(sError);
}
ts.Complete();
}
}
}
And this to modify RQRequisitionEntry:
public class RQRequisitionEntryExt : PXGraphExtension<RQRequisitionEntry>
{
public PXAction<RQRequisition> createPOOrder;
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.DataEntry)]
[PXUIField(DisplayName = Messages.CreateOrders)]
public IEnumerable CreatePOOrder(PXAdapter adapter)
{
PXGraph.InstanceCreated.AddHandler<POOrderEntry>((graph) =>
{
graph.GetExtension<POOrderEntryExt>().SendEmailNotification = true;
});
return Base.createPOOrder.Press(adapter);
}
}
In order to send an email to the Requester's (Employee) contact email from the Request, I modified the POOrderEntryExt to pull the information from the Request object and the Employee's Contact email (I left the RQRequisitionEntryExt the same and in place):
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
private bool sendEmailNotification = false;
public bool SendEmailNotification
{
get
{
return sendEmailNotification;
}
set
{
sendEmailNotification = value;
}
}
[PXOverride]
public void Persist(Action del)
{
using (var ts = new PXTransactionScope())
{
if (del != null)
{
del();
}
if (SendEmailNotification)
{
bool sent = false;
string sError = "Failed to send E-mail.";
try
{
Notification rowNotification = PXSelect<Notification,
Where<Notification.name, Equal<Required<Notification.name>>>>
.Select(Base, "PurchaseOrderNotification");
if (rowNotification == null)
throw new PXException("Notification Template was not found.");
var order = Base.Document.Current;
var requisition = (RQRequisition)PXSelect<RQRequisition,
Where<RQRequisition.reqNbr, Equal<Current<POOrder.rQReqNbr>>>>
.SelectSingleBound(Base, new object[] { order });
var request = (RQRequest)PXSelectJoin<RQRequest,
InnerJoin<RQRequisitionContent,
On<RQRequisitionContent.orderNbr, Equal<RQRequest.orderNbr>>>,
Where<RQRequisitionContent.reqNbr, Equal<POOrder.rQReqNbr>>>
.SelectSingleBound(Base, new object[] { order });
if (request.EmployeeID != null)
{
var employee = (BAccountR)PXSelectorAttribute.Select<RQRequest.employeeID>(
Base.Caches[typeof(RQRequest)], request);
if (employee != null)
{
var defEmpContact = (Contact)PXSelectorAttribute.Select<BAccountR.defContactID>(
Base.Caches[typeof(BAccountR)], employee);
if (String.IsNullOrEmpty(defEmpContact.EMail))
throw new PXException("E-mail is not specified for Employee Contact.");
var sender = TemplateNotificationGenerator.Create(order,
rowNotification.NotificationID.Value);
sender.RefNoteID = order.NoteID;
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = defEmpContact.EMail;
sent |= sender.Send().Any();
}
else
throw new PXException("Customer not found.");
}
else
throw new PXException("Request not found.");
}
catch (Exception Err)
{
sent = false;
sError = Err.Message;
}
if (!sent)
throw new PXException(sError);
}
ts.Complete();
}
}
}
I can get the original code to send an email in my development environment, but my modified code only returns the outer "Failed to send E-mail" error.
Can anyone help point me in the right direction to get my modifications to work?
Because in Acumatica there is one-to-many relationship between RQRequisition and RQRequest, I believe the better approach is to loop through all requests linked to the current requisition and compose a list of requester's emails. After that we can go ahead and send emails to all requesters as part of the Create Orders operation:
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
private bool sendEmailNotification = false;
public bool SendEmailNotification
{
get
{
return sendEmailNotification;
}
set
{
sendEmailNotification = value;
}
}
[PXOverride]
public void Persist(Action del)
{
using (var ts = new PXTransactionScope())
{
if (del != null)
{
del();
}
if (SendEmailNotification)
{
bool sent = false;
string sError = "Failed to send E-mail.";
try
{
Notification rowNotification = PXSelect<Notification,
Where<Notification.name, Equal<Required<Notification.name>>>>
.Select(Base, "PONotification");
if (rowNotification == null)
throw new PXException("Notification Template was not found.");
var order = Base.Document.Current;
var emails = new List<string>();
var requests = PXSelectJoinGroupBy<RQRequest,
InnerJoin<RQRequisitionContent,
On<RQRequest.orderNbr,
Equal<RQRequisitionContent.orderNbr>>>,
Where<RQRequisitionContent.reqNbr,
Equal<Required<RQRequisition.reqNbr>>>,
Aggregate<GroupBy<RQRequest.orderNbr>>>
.Select(Base, order.RQReqNbr);
foreach (RQRequest request in requests)
{
if (request.EmployeeID != null)
{
var requestCache = Base.Caches[typeof(RQRequest)];
requestCache.Current = request;
var emplOrCust = (BAccountR)PXSelectorAttribute
.Select<RQRequest.employeeID>(requestCache, request);
if (emplOrCust != null)
{
var defEmpContact = (Contact)PXSelectorAttribute
.Select<BAccountR.defContactID>(
Base.Caches[typeof(BAccountR)], emplOrCust);
if (!String.IsNullOrEmpty(defEmpContact.EMail) &&
!emails.Contains(defEmpContact.EMail))
{
emails.Add(defEmpContact.EMail);
}
}
}
}
foreach (string email in emails)
{
var sender = TemplateNotificationGenerator.Create(order,
rowNotification.NotificationID.Value);
sender.RefNoteID = order.NoteID;
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = email;
sent |= sender.Send().Any();
}
}
catch (Exception Err)
{
sent = false;
sError = Err.Message;
}
if (!sent)
throw new PXException(sError);
}
ts.Complete();
}
}
}

Error while adding entity object to DBContext object called from Parallel.Foreach loop

Error :
If the event originated on another computer, the display information had to be saved with the event.
The following information was included with the event:
Object reference not set to an instance of an object. at
System.Data.Objects.ObjectStateManager.DetectConflicts(IList1
entries) at System.Data.Objects.ObjectStateManager.DetectChanges()
at System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean
force) at
System.Data.Entity.Internal.Linq.InternalSet1.ActOnSet(Action action,
EntityState newState, Object entity, String methodName) at
System.Data.Entity.Internal.Linq.InternalSet1.Add(Object entity)
at System.Data.Entity.DbSet1.Add(TEntity entity) at
ESHealthCheckService.BusinessFacade.BusinessOperationsLayer.AddErrorToDbObject(Exception
ex, Server serverObj, Service windowsServiceObj)
the message resource is present but the message is not found in the string/message table
public void CheckForServerHealth()
{
businessLayerObj.SetStartTimeWindowsService();
List<ServerMonitor> serverMonitorList = new List<ServerMonitor>();
serverList = businessLayerObj.GetServerList();
Parallel.ForEach(
serverList,
() => new List<ServerMonitor>(),
(server, loop, localState) =>
{
localState.Add(serverStatus(server, new ServerMonitor()));
return localState;
},
localState =>
{
lock (serverMonitorList)
{
foreach (ServerMonitor serverMonitor in localState)
{
serverMonitorList.Add(serverMonitor);
}
}
});
businessLayerObj.SaveServerHealth(serverMonitorList);
}
public ServerMonitor serverStatus(Server serverObj, ServerMonitor serverMonitorObj)
{
if (new Ping().Send(serverObj.ServerName, 30).Status == IPStatus.Success)
{
serverMonitorObj.Status = true;
try
{
PerformanceCounter cpu = new PerformanceCounter("Processor", "% Processor Time", "_Total", serverObj.ServerName);
serverMonitorObj.CPUUtlilization = (cpu.NextValue());
}
catch (Exception ex)
{
businessLayerObj.AddErrorObjectToStaticList(ex, serverObj);
}
serverMonitorObj.ServerID = serverObj.ServerID;
try
{
string[] diskArray = serverObj.DriveMonitor.ToString().Split(':');
if (diskArray != null && diskArray.Contains("NA"))
{
serverMonitorObj.DiskSpace = "NA";
}
else
{
serverMonitorObj.DiskSpace = ReadFreeSpaceOnNetworkDrives(serverObj.ServerName, diskArray);
}
}
catch (Exception ex)
{
businessLayerObj.AddErrorObjectToStaticList(ex, serverObj);
}
serverMonitorObj.CreatedDateTime = DateTime.Now;
}
else
{
serverMonitorObj.Status = false;
serverMonitorObj.ServerID = serverObj.ServerID;
//return serverMonitorObj;
}
return serverMonitorObj;
}
public void AddErrorObjectToStaticList(Exception ex, Server serverObj = null, Service windowsServiceObj = null)
{
EShelathLoging esLogger = new EShelathLoging();
esLogger.CreateDatetime = DateTime.Now;
if (ex.InnerException != null)
{
esLogger.Message = (windowsServiceObj == null ? ex.InnerException.Message : ("Service Name : " + windowsServiceObj.ServiceName + "-->" + ex.InnerException.Message));
//esLogger.Message = "Service Name : " + windowsServiceObj.ServiceName + "-->" + ex.InnerException.Message;
esLogger.StackTrace = (ex.InnerException.StackTrace == null ? "" : ex.InnerException.StackTrace);
}
else
{
esLogger.Message = (windowsServiceObj == null ? ex.Message : ("Service Name : " + windowsServiceObj.ServiceName + "-->" + ex.Message));
//esLogger.Message = "Service Name : " + windowsServiceObj.ServiceName + "-->" + ex.Message;
esLogger.StackTrace = ex.StackTrace;
}
if (serverObj != null)
{
esLogger.ServerName = serverObj.ServerName;
}
try
{
lock (lockObject)
{
esHealthCheckLoggingList.Add(esLogger);
}
}
catch (Exception exe)
{
string logEntry = "Application";
if (EventLog.SourceExists(logEntry) == false)
{
EventLog.CreateEventSource(logEntry, "Windows and IIS health check Log");
}
EventLog eventLog = new EventLog();
eventLog.Source = logEntry;
eventLog.WriteEntry(exe.Message + " " + exe.StackTrace, EventLogEntryType.Error);
}
}
And then the below function is called to add objects from static list to the db object.
public void AddErrorToDbObject()
{
try
{
foreach (EShelathLoging eslogObject in esHealthCheckLoggingList)
{
lock (lockObject)
{
dbObject.EShelathLogings.Add(eslogObject);
}
}
}
catch (DbEntityValidationException exp)
{
string logEntry = "Application";
if (EventLog.SourceExists(logEntry) == false)
{
EventLog.CreateEventSource(logEntry, "Windows and IIS health check Log");
}
EventLog eventLog = new EventLog();
eventLog.Source = logEntry;
eventLog.WriteEntry(exp.Message + " " + exp.StackTrace, EventLogEntryType.Error);
}
catch (Exception exe)
{
string logEntry = "Application";
if (EventLog.SourceExists(logEntry) == false)
{
EventLog.CreateEventSource(logEntry, "Windows and IIS health check Log");
}
EventLog eventLog = new EventLog();
eventLog.Source = logEntry;
eventLog.WriteEntry(exe.Message + " " + exe.StackTrace, EventLogEntryType.Error);
}`enter code here`
}
DbSet<T> is not thread-safe, so you can't use it from multiple threads at the same time. It seems you're trying to fix that by using a lock, but you're doing that incorrectly. For this to work, all threads have to share a single lock object. Having separate lock object for each thread, like you do now, won't do anything.
Please note that I received the same exception with the application I was working on, and determined that the best way to resolve the issue was to add an AsyncLock, because of what #svick mentioned about how DbSet is not threadsafe. Thank you, #svick!
I'm guessing that your DbContext is inside your businessLayerObj, so here is what I recommend, using Stephen Cleary's excellent Nito.AsyncEx (see https://www.nuget.org/packages/Nito.AsyncEx/):
using Nito.AsyncEx;
// ...
private readonly AsyncLock _dbContextMutex = new AsyncLock();
public void CheckForServerHealth()
{
using (await _dbContextMutex.LockAsync().ConfigureAwait(false))
{
await MyDbContextOperations(businessLayerObj).ConfigureAwait(false);
}
}
private async Task MyDbContextOperations(BusinessLayerClass businessLayerObj)
{
await Task.Run(() =>
{
// operations with businessLayerObj/dbcontext here...
});
}

SD Card's Free Size

I want to check the free size available on My Device's SD Card. I know FileConnection API. How can I check the free size of the SD Card ?
The idea is to open the cfcard file connection and call availableSize() on it. to get the proper memory card location read it from the System.getProperty(...). In some devices the aforesaid property may fail hence constructing new path from the memory card name system property.
NOTE: In certain devices the hostname must be 'localhost', hence change the return string in getFilehost() appropriately.
PS: Since this is a code snippet certain form / command references may throw null pointer please resolve it accordingly.
Below code with get you the available size in 'BYTES' of the memory card
String memoryCardPath = System.getProperty("fileconn.dir.memorycard");
addToLogs(memoryCardPath);
if (null == memoryCardPath) {
displayAlert(null);
}
nativeHostname(memoryCardPath);
addToLogs(nativeHostname);
String path = buildPath(memoryCardPath.substring(getFilehost().length()));
addToLogs(path);
long availableSize = getAvailableSize(path);
addToLogs(String.valueOf(availableSize)+" Bytes");
if(availableSize == 0L) {
String memoryCardName = System.getProperty("fileconn.dir.memorycard.name");
addToLogs(memoryCardName);
path = buildPath(memoryCardName + "/");
addToLogs(path);
availableSize = getAvailableSize(path);
addToLogs(String.valueOf(availableSize)+" Bytes");
if(availableSize == 0L) {
displayAlert(null);
return;
}
}
displayAlert(String.valueOf(availableSize));
Supporting methods
public String buildPath(String foldername) {
if(null == foldername) {
foldername = "";
}
addToLogs("[BP]"+getFilehost());
addToLogs("[BP]"+foldername);
return new StringBuffer().append(getFilehost()).append(foldername).toString();
}
String nativeHostname = null;
public void nativeHostname(String url) {
String host = url.substring("file://".length());
addToLogs("[NH]"+host);
int index = host.indexOf('/');
addToLogs("[NH]"+String.valueOf(index));
if(index > 0) {
nativeHostname = host.substring(0, index);
} else {
nativeHostname = "/";
}
addToLogs("[NH]"+nativeHostname);
}
public boolean tryLocalhost = false;
public String getFilehost() {
final StringBuffer buf = new StringBuffer().append("file://");
if (null != nativeHostname && nativeHostname.length() > 0) {
buf.append(nativeHostname);
}
addToLogs("[GFH] "+buf.toString());
return buf.toString();
}
public long getAvailableSize(String path) {
FileConnection fcon = null;
try {
fcon = (FileConnection) Connector.open(path, Connector.READ);
if(null != fcon && fcon.exists()) {
return fcon.availableSize();
} else {
return 0L;
}
} catch (IOException ex) {
ex.printStackTrace();
addToLogs("[GAS]"+"ERROR : "+ex.getMessage());
return 0L;
} finally {
try {
fcon.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public void displayAlert(String text) {
Alert alert = new Alert(
null == text ? "Warning" : "Info",
null == text ? "Memory card not available" : text,
null,
null == text ? AlertType.WARNING : AlertType.INFO);
alert.setTimeout(Alert.FOREVER);
Display.getDisplay(this).setCurrent(alert, Display.getDisplay(this).getCurrent());
}
public void addToLogs(String log) {
log = null == log ? "null" : log;
getFormLogs().append(new StringItem("", log+"\n"));
}

Not able to get DirContext ctx using Spring Ldap

Hi i am using Spring ldap , after execution below program It display I am here only and after that nothing is happening, program is in continue execution mode.
public class SimpleLDAPClient {
public static void main(String[] args) {
Hashtable env = new Hashtable();
System.out.println("I am here");
String principal = "uid="+"a502455"+", ou=People, o=ao, dc=com";
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "MYURL");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS,"PASSWORD");
DirContext ctx = null;
NamingEnumeration results = null;
try {
ctx = new InitialDirContext(env);
System.out.println(" Context" + ctx);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
results = ctx.search("", "(objectclass=aoPerson)", controls);
while (results.hasMore()) {
SearchResult searchResult = (SearchResult) results.next();
Attributes attributes = searchResult.getAttributes();
Attribute attr = attributes.get("cn");
String cn = (String) attr.get();
System.out.println(" Person Common Name = " + cn);
}
} catch (NamingException e) {
throw new RuntimeException(e);
} finally {
if (results != null) {
try {
results.close();
} catch (Exception e) {
}
}
if (ctx != null) {
try {
ctx.close();
} catch (Exception e) {
}
}
}
}
}
Try fixing the below lines, i removed "ao" and it works fine.
results = ctx.search("", "(objectclass=Person)", controls);
You need to give search base as well
env.put(Context.PROVIDER_URL, "ldap://xx:389/DC=test,DC=enterprise,DC=xx,DC=com");
Refer this link as well http://www.adamretter.org.uk/blog/entries/LDAPTest.java

Resources