FindTypeMapFor method not exists in AutoMapper 11 - asp.net-core-6.0

I am currently upgrading a web API that was developed on NET Core 5.0 and upgrading it to NET Core 6.0. When upgrading the NuGet AutoMapper package to version 11.0.1, I find that the FindTypeMapFor method does not exist in the ConfigurationProvider definition.
public Dictionary<string, PropertyMappingValue> GetPropertyMappingFromAutomapper<TSource, TDestination>(List<string> reverseOrderProperties) where TSource : class where TDestination : class
{
Dictionary<string, PropertyMappingValue> dictionaryPropertyMapping = new(StringComparer.OrdinalIgnoreCase);
if (typeof(TSource).Equals(typeof(ForNotIncludeDto)) || typeof(TSource).Equals(typeof(ForNotSortingDto)) || typeof(TSource).Equals(typeof(ForNotDistinctDto)))
{
return dictionaryPropertyMapping;
}
TypeMap typeMap = this.Mapper.ConfigurationProvider.FindTypeMapFor<TSource, TDestination>();
if (typeMap is null)
{
throw new Exception($"Cannot find exact property mapping instance " + $"for <{typeof(TSource)},{typeof(TDestination)}>");
}
List<PropertyMap> propertyMaps = typeMap.PropertyMaps.Where(x => x.Ignored == false).ToList();
List<PathMap> pathMaps = typeMap.PathMaps.Where(x => x.Ignored == false).ToList();
foreach (MemberInfo member in typeMap.SourceTypeDetails.AllMembers)
{
List<string> originPropertyMap = propertyMaps.Where(x => x.SourceMember is not null && x.SourceMember.Name.Equals(member.Name)).Select(x => x.DestinationName).ToList();
if (originPropertyMap.Count.Equals(0))
{
originPropertyMap = pathMaps.Where(x => x.SourceMember is not null && x.SourceMember.Name.Equals(member.Name)).Select(x => x.DestinationName).ToList();
}
if (originPropertyMap.Count > 0)
{
dictionaryPropertyMapping.Add(member.Name, new PropertyMappingValue(originPropertyMap, reverseOrderProperties.Where(x => x.Equals(member.Name)).Any()));
}
}
return dictionaryPropertyMapping;
}
How can I get the TypeMap object of a specific mapping?
How can I use FindTypeMapFor or some method that will replace it?

This was moved to the "Internal" object on AutoMapper 11.
Here's the new usage:
using AutoMapper.Internal;
Mapper.ConfigurationProvider.Internal().FindTypeMapFor

Related

How do I add an EPTimecardDetail record to a timecard?

I’m writing a customization to add records to a timecard and I’m trying to create a new record to add to the timecard. Using the logic in T230 I’m creating a variable and I’m being told by the compiler that EPTimecardDetail cannot be found.
I’ve added using PX.Objects.EP and PX.Objects.PM but I figure that if TimeCardMaint can be found then EPTimecardDetail should be able be found as well. I’ve included my using code as well but I think I’m missing something else.
using System;
using System.Collections;
using PX.Data;
using PX.Data.BQL.Fluent;
using PX.Data.BQL;
using PX.Objects.CS;
using PX.Objects.PM;
using PX.Objects.EP;
using PX.Objects.CR;
using PX.Objects.AR;
using PX.Objects.CT;
using PX.Objects.GL.FinPeriods;
using PX.TM;
using System.Collections.Generic;
namespace TimecardImport
{
public class NLTimecardLineEntry : PXGraph<NLTimecardLineEntry>
{
private static void DoPopulateTimeCard(Int32 employeeID, DateTime startDate, NLTimecardLine record)
{
TimeCardMaint graph = PXGraph.CreateInstance<TimeCardMaint>();
Int32 cardWeekID = PXWeekSelector2Attribute.GetWeekID(graph, startDate);
//look for an employee timecard with the current weekID
EPTimeCard card = PXSelectReadonly<EPTimeCard,
Where<EPTimeCard.employeeID, Equal<Required<EPTimeCard.employeeID>>,
And<EPTimeCard.weekId, Equal<Required<EPTimeCard.weekId>>>>>.SelectWindowed(graph, 0, 1, employeeID, cardWeekID);
if (card == null) //if a card was not found, create one
{
card = (EPTimeCard)graph.Document.Cache.CreateInstance();
card.WeekID = cardWeekID;
card.EmployeeID = employeeID;
card = graph.Document.Insert(card);
}
//at this point card is the card that we're going to work with
var detailLine = (EPTimecardDetail)graph.Activities.Cache.CreateCopy(
graph.Activities.Insert());
//detailLine.SetValueExt<detailLine.Date_Date>(record, record.InDate);
//detailLine.EarningTypeID = "RG";
//detailLine = graph.Activities.Update(detailLine);
graph.Save.Press();
}
}}
The error I'm getting is "The type or namespace name 'EPTimecardDetail' could not be found (are you missing a using directive or an assembly reference?)".
EPTimecardDetail is defined within PX.Objects.EP so I'm not sure why I'm having a problem there. Or, perhaps this is not the way to add records to the Details tab of the Employee Time Card screen?
For the namespace issue you can declare using PX.Object.EP and refer to the type as TimeCardMaint.EPTimecardDetail
Or you can declare using static PX.Objects.EP.TimeCardMaint and refer to the type as EPTimecardDetail
For inserting the record check the source code in file TimeCardMaint.cs There are examples on how to insert this DAC record.
Make sure the fields used for SQL joins like OrigNoteID and RefNoteID have the proper value (non null).
This example is from the Correct action in TimeCardMaint:
[PXUIField(DisplayName = Messages.Correct)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.Release)]
public virtual IEnumerable Correct(PXAdapter adapter)
{
if (Document.Current != null)
{
EPTimeCard source = GetLastCorrection(Document.Current);
if (source.IsReleased != true)
return new EPTimeCard[] { source };
EPTimeCard newCard = (EPTimeCard)Document.Cache.CreateInstance();
newCard.WeekID = source.WeekID;
newCard.OrigTimeCardCD = source.TimeCardCD;
newCard = Document.Insert(newCard);
newCard.EmployeeID = source.EmployeeID;
PXNoteAttribute.CopyNoteAndFiles(Document.Cache, source, Document.Cache, newCard, true, true);
bool failed = false;
Dictionary<string, TimeCardSummaryCopiedInfo> summaryDescriptions = new Dictionary<string, TimeCardSummaryCopiedInfo>();
foreach (EPTimeCardSummary summary in Summary.View.SelectMultiBound(new object[] { source }))
{
string key = GetSummaryKey(summary);
if (!summaryDescriptions.ContainsKey(key))
{
string note = PXNoteAttribute.GetNote(Summary.Cache, summary);
var info = new TimeCardSummaryCopiedInfo(summary.Description, note);
summaryDescriptions.Add(key, info);
}
}
foreach (EPTimecardDetail act in TimecardActivities.View.SelectMultiBound(new object[] { source }))
{
EPTimecardDetail newActivity = PXCache<EPTimecardDetail>.CreateCopy(act);
newActivity.Released = false;
newActivity.Billed = false;
newActivity.NoteID = null;
newActivity.TimeCardCD = null;
newActivity.TimeSheetCD = null;
newActivity.OrigNoteID = act.NoteID; //relation between the original activity and the corrected one.
newActivity.Date = act.Date;
newActivity.Billed = false;
newActivity.SummaryLineNbr = null;
newActivity.NoteID = null;
newActivity.ContractCD = null;
isCreateCorrectionFlag = true;
try
{
newActivity = Activities.Insert(newActivity);
}
catch (PXSetPropertyException ex)
{
failed = true;
Activities.Cache.RaiseExceptionHandling<EPTimecardDetail.summary>(act, act.Summary, new PXSetPropertyException(ex.MessageNoPrefix, PXErrorLevel.RowError));
continue;
}
newActivity.TrackTime = act.TrackTime; //copy as is.
isCreateCorrectionFlag = false;
newActivity.ApprovalStatus = ActivityStatusAttribute.Completed;
newActivity.RefNoteID = act.NoteID == act.RefNoteID ? newActivity.NoteID : act.RefNoteID;
newActivity.ContractCD = act.ContractCD;
PXNoteAttribute.CopyNoteAndFiles(Activities.Cache, act, Activities.Cache, newActivity);
Activities.Cache.SetValue<EPTimecardDetail.isCorrected>(act, true);
Activities.Cache.SetStatus(act, PXEntryStatus.Updated);
}
if (failed)
{
throw new PXException(Messages.FailedToCreateCorrectionTC);
}
foreach (EPTimeCardItem item in Items.View.SelectMultiBound(new object[] { source }))
{
EPTimeCardItem record = Items.Insert();
record.ProjectID = item.ProjectID;
record.TaskID = item.TaskID;
record.Description = item.Description;
record.InventoryID = item.InventoryID;
record.CostCodeID = item.CostCodeID;
record.UOM = item.UOM;
record.Mon = item.Mon;
record.Tue = item.Tue;
record.Wed = item.Wed;
record.Thu = item.Thu;
record.Fri = item.Fri;
record.Sat = item.Sat;
record.Sun = item.Sun;
record.OrigLineNbr = item.LineNbr;//relation between the original activity and the corrected one.
}
foreach (EPTimeCardSummary summary in Summary.Select())
{
string key = GetSummaryKey(summary);
if (summaryDescriptions.ContainsKey(key))
{
PXNoteAttribute.SetNote(Summary.Cache, summary, summaryDescriptions[key].Note);
Summary.Cache.SetValue<EPTimeCardSummary.description>(summary, summaryDescriptions[key].Description);
}
}
return new EPTimeCard[] { newCard };
}
return adapter.Get();
}

Rewrite code for Automapper v5.0 to v4.0

Automapper v4.0 was very straight forward to use within a method, can someone help rewrite this for v5.0 please (specifically the Mapper code):
public IEnumerable<NotificationDto> GetNewNotifications()
{
var userId = User.Identity.GetUserId();
var notifications = _context.UserNotifications
.Where(un => un.UserId == userId && !un.IsRead)
.Select(un => un.Notification)
.Include(n => n.Gig.Artist)
.ToList();
Mapper.CreateMap<ApplicationUser, UserDto>();
Mapper.CreateMap<Gig, GigDto>();
Mapper.CreateMap<Notification, NotificationDto>();
return notifications.Select(Mapper.Map<Notification, NotificationDto>);
}
UPDATE:
It seems that EF Core doesn't project what AutoMapper is mapping with:
return notifications.Select(Mapper.Map<Notification, NotificationDto>);
But I do get results in Postman with the following code:
return notifications.Select(n => new NotificationDto()
{
DateTime = n.DateTime,
Gig = new GigDto()
{
Artist = new UserDto()
{
Id = n.Gig.Artist.Id,
Name = n.Gig.Artist.Name
},
DateTime = n.Gig.DateTime,
Id = n.Gig.Id,
IsCancelled = n.Gig.IsCancelled,
Venue = n.Gig.Venue
},
OriginalVenue = n.OriginalVenue,
OriginalDateTime = n.OriginalDateTime,
Type = n.Type
});
If you want to keep using static instance - the only change is in mapper initialization:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicationUser, UserDto>();
cfg.CreateMap<Gig, GigDto>();
cfg.CreateMap<Notification, NotificationDto>();
});
Also you should run this code only once per AppDomain (somewhere on startup for example) and not every time you calling GetNewNotifications.

Changing content of MvxPickerViewModel

I am writing a simple application that contains a database of items. The items have a type, manufacturer, model, and a few other properties. I have a implemented three UIPickerView's with MvxPickerViewModel's as outlined in N=19 of the N+1 series for MvvmCross. There is one UIPickerView/MvxPickerViewModel for each the type, the manufacturer, and the model (only one is ever on the screen at a time). However if I update the ItemSource data for a MvxPickerViewModel, the rows that were already visible in the UIPickerView do not refresh until they are scrolled off the screen. The N=19 example, does not update the list of items in the UIPickerView so it isn't clear that the problem didn't exist there. Have I made a mistake or has anyone else experienced this? Is there a work around?
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
NavigationController.NavigationBarHidden = true;
var comparableTableViewSource = new MvxStandardTableViewSource(ComparableLV);
ComparableLV.Source = comparableTableViewSource;
var ManufacturerPicker = new UIPickerView();
var manufacturerPickerModel = new MvxPickerViewModel(ManufacturerPicker);
ManufacturerPicker.Model = manufacturerPickerModel;
ManufacturerPicker.ShowSelectionIndicator = true;
ManufacturerTextField.InputView = ManufacturerPicker;
var ModelPicker = new UIPickerView();
var modelPickerModel = new MvxPickerViewModel(ModelPicker);
ModelPicker.Model = modelPickerModel;
ModelPicker.ShowSelectionIndicator = true;
ModelTextField.InputView = ModelPicker;
var TypePicker = new UIPickerView();
var typePickerModel = new MvxPickerViewModel(TypePicker);
TypePicker.Model = typePickerModel;
TypePicker.ShowSelectionIndicator = true;
TypeTextField.InputView = TypePicker;
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
set.Bind(comparableTableViewSource).For(s => s.ItemsSource).To(vm => vm.Comparables);
set.Bind(manufacturerPickerModel).For(p => p.ItemsSource).To(vm => vm.Manufacturers);
set.Bind(manufacturerPickerModel).For(p => p.SelectedItem).To(vm => vm.SelectedManufacturer);
set.Bind(ManufacturerTextField).To(vm => vm.SelectedManufacturer);
set.Bind(modelPickerModel).For(p => p.ItemsSource).To(vm => vm.Models);
set.Bind(modelPickerModel).For(p => p.SelectedItem).To(vm => vm.SelectedModel);
set.Bind(ModelTextField).To(vm => vm.SelectedModel);
set.Bind(typePickerModel).For(p => p.ItemsSource).To(vm => vm.Types);
set.Bind(typePickerModel).For(p => p.SelectedItem).To(vm => vm.SelectedType);
set.Bind(TypeTextField).To(vm => vm.SelectedType);
set.Apply();
var g = new UITapGestureRecognizer(() => {
HornTextField.ResignFirstResponder();
ManufacturerTextField.ResignFirstResponder();
ModelTextField.ResignFirstResponder();
});
View.AddGestureRecognizer(g);
}
Looking at MvxPickerViewModel.cs I'm suspicious that there is no call to ReloadAllComponents (or to ReloadComponent[0]) when the ItemsSource itself changes, but there is a call when the Collection internally changes.
As a workaround, perhaps try a subclass like:
public class MyPickerViewModel
: MvxPickerViewModel
{
private readonly UIPickerView _pickerView;
public MyPickerViewModel(UIPickerView pickerView)
: base(pickerViww)
{
_pickerView = pickerView;
}
[MvxSetToNullAfterBinding]
public override IEnumerable ItemsSource
{
get { return base.ItemsSource; }
set
{
base.ItemsSource = value;
if (value != null)
_pcikerView.ReloadComponent(0);
}
}
}
Would also be great to get a fix back into MvvmCross...

CRM 2011 development: Get the following error if I try to get related entity: Object reference not set to an instance of an object

I try to get a related entity and get this error: Object reference not set to an instance of an object.
See below my code:
public IEnumerable<RUBAnnotation> GetAnnotationsByServiceRequestId(string serviceRequestId)
{
List<Annotation> annotationList = new List<Annotation>();
try
{
using (OrganizationServiceProxy organizationProxy = new OrganizationServiceProxy(organisationWebServiceUri, null, userCredentials, deviceCredentials))
{
organizationProxy.EnableProxyTypes();
var service = (IOrganizationService)organizationProxy;
OrganizationServiceContext orgContext = new OrganizationServiceContext(service);
Relationship rel = new Relationship("Incident_Annotation");
// get all incidents by incidentId
IEnumerable<Incident> incidents = from c in orgContext.CreateQuery<Incident>()
where c.Id == new Guid(serviceRequestId)
select c;
return GetAnnotationsEntities(incidents);
}
}
catch (Exception)
{
throw;
}
return null;
}
private List<RUBAnnotation> GetAnnotationsEntities(IEnumerable<Incident> incidents)
{
List<RUBAnnotation> annotationsList = new List<RUBAnnotation>();
try
{
foreach (Incident incident in incidents)
{
foreach (var annotation in incident.Incident_Annotation) // HERE OCCURS THE EXCEPTION!!
{
if (!string.IsNullOrEmpty(annotation.NoteText))
{
var customAnnotation = new RUBAnnotation();
customAnnotation.Id = annotation.Id.ToString();
if (annotation.Incident_Annotation != null)
{
customAnnotation.ServiceRequestId = annotation.Incident_Annotation.Id.ToString();
}
customAnnotation.Message = annotation.NoteText;
customAnnotation.CreatedOn = (DateTime)annotation.CreatedOn;
customAnnotation.UserId = annotation.CreatedBy.Id.ToString();
annotationsList.Add(customAnnotation);
}
}
}
}
catch (Exception e)
{
throw e;
}
return annotationsList;
}
Why do I get this error when I try to get incident.Incident_Annotation ? I think incident.Incident_Annotation is NULL, but why? Al my incidents have minimal 1 or more annotations.
Related entities must be explicitly loaded, you can find more information on this MSDN article:
MSDN - Access Entity Relationships

Mapping umbraco node to strongtyped object

I am working with Umbraco 4.7.1 and I am trying to map the content-nodes to some autogenerated strong typed objects. I have tried using both valueinjecter and automapper, but OOTB neither of them map my properties. I guess it is because all properties on an Umbraco node (the cms document) are retrieved like this:
node.GetProperty("propertyName").Value;
And my strongly typed objects are in the format of MyObject.PropertyName. So how do I map the property on the node which is retrieved using a method and a string beginning with a lowercase character into a property on MyObject where the property begins with an uppercase character ?
UPDATE
I managed to create the following code which maps the umbraco node as intended, by digging around in the Umbraco sourcecode for some inspiration on how to cast string-properties to strongly typed properties:
public class UmbracoInjection : SmartConventionInjection
{
protected override bool Match(SmartConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
protected override void Inject(object source, object target)
{
if (source != null && target != null)
{
Node node = source as Node;
var props = target.GetProps();
var properties = node.Properties;
for (int i = 0; i < props.Count; i++)
{
var targetProperty = props[i];
var sourceProperty = properties[targetProperty.Name];
if (sourceProperty != null && !string.IsNullOrWhiteSpace(sourceProperty.Value))
{
var value = sourceProperty.Value;
var type = targetProperty.PropertyType;
if (targetProperty.PropertyType.IsValueType && targetProperty.PropertyType.GetGenericArguments().Length > 0 && typeof(Nullable<>).IsAssignableFrom(targetProperty.PropertyType.GetGenericTypeDefinition()))
{
type = type.GetGenericArguments()[0];
}
targetProperty.SetValue(target, Convert.ChangeType(value, type));
}
}
}
}
}
As you can see I use the SmartConventionInjection to speed things up.
It still takes approximately 20 seconds to map something like 16000 objects. Can this be done even faster ?
thanks
Thomas
with ValueInjecter you would do something like this:
public class Um : ValueInjection
{
protected override void Inject(object source, object target)
{
var node = target as Node;
var props = source.GetProps();
for (int i = 0; i < props.Count; i++)
{
var prop = props[i];
target.GetProperty(prop.Name).Value;
}
}
}

Resources