Error in Process screen when attaching report to email - acumatica

We have a requirement to attach custom report to email as an attachment. As per our requirement we created a process screen for this. Issue we are facing is, when we use below line of code we are getting “OBJECT REFERENCE ERROR”, can you please have a look at below sample code.
public class ProcessReports : PXGraph<ProcessReports>
{
[InjectDependency]
protected IReportLoaderService ReportLoader { get; private set; }
[InjectDependency]
protected IReportRenderer ReportRenderer { get; private set; }
public PXCancel<DACName> Cancel;
public PXProcessing<DACName, Where<DACName.DACName, Equal<OPR>>> QueueList;
public ProcessReports()
{
QueueList.SetProcessDelegate(delegate (List<DACName> list)
{
SaveFile(list, true);
});
}
public static void SaveFile(List<DACName> list, bool aIsMassProcess)
{
new KWProcessWorkCenterOpenJobReports().save(list, aIsMassProcess);
}
public virtual void save(List<DACName> list, bool aIsMassProcess)
{
//here at this point we are getting error
Report _report = ReportLoader.LoadReport("AA501108", null);
}
}

Looking at your code my inference is the object with the IReportLoaderService and IReportRenderer interface decleration never properly gets initialized with PXGraph.CreateInstance<...> before calling save method.
new KWProcessWorkCenterOpenJobReports().save(list, aIsMassProcess);
The following code snippet will generate a report from the given paramaters provided and then save the file within the Acumatica system, though it can be further emailed out as mentioned in your original prompt.
using PX.Data;
using PX.Objects.AR;
using PX.Reports;
using PX.Reports.Controls;
using PX.Reports.Data;
using PX.SM;
using System.Collections.Generic;
using MailMessage = PX.Reports.Mail.Message;
namespace StackOverflow
{
public class EmailReportProc : PXGraph<EmailReportProc>
{
#region Constructor
public EmailReportProc()
{
Documents.SetProcessDelegate(generateAcumaticaReports);
}
#endregion
#region Properties
[InjectDependency]
protected IReportLoaderService ReportLoader { get; set; }
[InjectDependency]
protected IReportDataBinder ReportDataBinder { get; set; }
#endregion
#region Views
public PXCancel<ARInvoice> Cancel;
public PXProcessing<ARInvoice, Where<ARInvoice.status, Equal<ARDocStatus.pendingEmail>>> Documents;
#endregion
#region Methods
private static void generateAcumaticaReports(List<ARInvoice> Documents)
{
//Initialize new graph instance for use within static processing method.
EmailReportProc graph = PXGraph.CreateInstance<EmailReportProc>();
foreach (ARInvoice document in Documents)
{
//Paramaters for report
Dictionary<string, string> parameters = new Dictionary<string, string>
{
{ "ARInvoice.DocType", document.DocType },
{ "ARInvoice.RefNbr", document.RefNbr }
};
//Load report - ReportID must be valid sitemap entry in system
Report report = graph.ReportLoader.LoadReport("AR622000", null);
//Initialize the parameters for the report
graph.ReportLoader.InitDefaultReportParameters(report, parameters);
ReportNode reportNode = graph.ReportDataBinder.ProcessReportDataBinding(report);
//Generate PDF Report and creates Acumatica file.
FileInfo fileInfo = new FileInfo(reportNode.ExportFileName + ".pdf", null, MailMessage.GenerateReport(reportNode, RenderType.FilterPdf)[0]);
UploadFileMaintenance uploadFileMaintenance = PXGraph.CreateInstance<UploadFileMaintenance>();
//Save Generated file into system
_ = uploadFileMaintenance.SaveFile(fileInfo);
}
}
#endregion
}
}

Related

How can I load data for multiple TabPages concurrently in a WinForms application

I have a Windows Forms application with a TabControl containing multiple TabPages. Each TabPage contains many controls whose data is read from a database via a web api. I want to be able to display at least one TabPage, with it's data, while the remaining TabPages are loading. I know I can't load a control in a thread, so I need a way to do the data retrieval in the background, concurrently, for each of the TabPages and then use the data retrieved to fill in the controls on each TabPage. So my question is: what would be the ideal method for doing this? I want to prioritize performance. I also want to have some visual indication on each TabPage header that the page is still loading. Thoughts? Ideas? Direct answers?
You can load data in parallel threads and just after loading data completed, then update UI (legally) by calling Invoke method to prevent cross-thread exception. You can also load data using tasks in parallel, and update data when each task is completed.
In the following examples, the data is loading in parallel threads and as soon as each thread/task is done, the UI will be updated.
Example 1 - Using Task to load data into UI in parallel
private NorthwindClient client = new NorthwindClient();
private void Form1_Load(object sender, EventArgs e)
{
LoadCategories();
LoadProducts();
}
private void LoadProducts()
{
productsTabPage.Text = "Loading ...";
var products = client.GetProductsAsync().GetAwaiter();
products.OnCompleted(() =>
{
productsDataGridView.DataSource = products.GetResult();
productsTabPage.Text = "Products";
});
}
private void LoadCategories()
{
categoriesTabPage.Text = "Loading ...";
var categories = client.GetCategoriesAsync().GetAwaiter();
categories.OnCompleted(() =>
{
categoriesDataGridView.DataSource = categories.GetResult();
categoriesTabPage.Text = "Categories";
});
}
Example 2 - Using Thread to load data into UI in parallel
private NorthwindClient client = new NorthwindClient();
private void Form1_Load(object sender, EventArgs e)
{
LoadCategories();
LoadProducts();
}
private void LoadProducts()
{
productsTabPage.Text = "Loading ...";
new Thread(() =>
{
var products = client.GetProducts();
Invoke(new Action(() =>
{
productsDataGridView.DataSource = products;
productsTabPage.Text = "Products";
}));
}).Start();
}
private void LoadCategories()
{
categoriesTabPage.Text = "Loading ...";
new Thread(() =>
{
var categories = client.GetCategories();
Invoke(new Action(() =>
{
categoriesDataGridView.DataSource = categories;
categoriesTabPage.Text = "Categories";
}));
}).Start();
}
NorthwindClient - Common code for both examples
Both above examples rely on the following code to load data:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
public class NorthwindClient
{
static HttpClient client;
static NorthwindClient()
{
client = new HttpClient();
client.BaseAddress = new Uri("https://northwind.vercel.app/api/");
}
public async Task<IEnumerable<Category>> GetCategoriesAsync()
{
var response = await client.GetStringAsync("categories");
return JsonConvert.DeserializeObject<IEnumerable<Category>>(response);
}
public async Task<IEnumerable<Product>> GetProductsAsync()
{
var response = await client.GetStringAsync("products");
return JsonConvert.DeserializeObject<IEnumerable<Product>>(response);
}
public IEnumerable<Category> GetCategories()
{
var response = client.GetStringAsync("categories").Result;
return JsonConvert.DeserializeObject<IEnumerable<Category>>(response);
}
public IEnumerable<Product> GetProducts()
{
var response = client.GetStringAsync("products").Result;
return JsonConvert.DeserializeObject<IEnumerable<Product>>(response);
}
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class Product
{
public int Id { get; set; }
public int SupplierId { get; set; }
public int categoryId { get; set; }
public string QuantityPerUnit { get; set; }
public decimal UnitPrice { get; set; }
public int UnitsInStock { get; set; }
public int UnitsOnOrder { get; set; }
public int ReorderLevel { get; set; }
public bool Discontinued { get; set; }
public string Name { get; set; }
}

Error when adding Where or OrderBy clauses to Azure Mobile Apps request

I'm developing an Azure Mobile App service to interface to my Xamarin application.
I've created, connected and successfully populated an SQL Database, but when I try to add some filters to my request, for example an orderby() or where() clauses, it returns me a Bad Request error.
For example, this request: https://myapp.azurewebsites.net/tables/Race?$orderby=iRound%20desc,iYear%20desc&$top=1&ZUMO-API-VERSION=2.0.0 gives me {"message":"The query specified in the URI is not valid. Could not find a property named 'IYear' on type 'MyType'."}.
My configuration method is this:
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.AddTablesWithEntityFramework()
.ApplyTo(config);
config.MapHttpAttributeRoutes();
Database.SetInitializer(new CreateDatabaseIfNotExists<MainDataContext>());
app.UseWebApi(config);
and my DbContext is this:
public class MainDataContext : DbContext
{
private const string connectionStringName = "Name=MS_TableConnectionString";
public MainDataContext() : base(connectionStringName)
{
Database.Log = s => WriteLog(s);
}
public void WriteLog(string msg)
{
System.Diagnostics.Debug.WriteLine(msg);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
public DbSet<Race> Race { get; set; }
public DbSet ...ecc...
}
Following this guide, I added a migration after creating my TableControllers. So the TableController for the example type shown above is pretty standard:
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public class RaceController : TableController<Race>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MainDataContext context = new MainDataContext();
DomainManager = new EntityDomainManager<Race>(context, Request);
}
// GET tables/Race
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Race> GetAllRace()
{
return Query();
}
// GET tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Race> GetRace(string id)
{
return Lookup(id);
}
// PATCH tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Race> PatchRace(string id, Delta<Race> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Race
public async Task<IHttpActionResult> PostRace(Race item)
{
Race current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteRace(string id)
{
return DeleteAsync(id);
}
}
As you can see, I already tried to add the EnableQuery attribute to my TableController, as seen on Google. I also tried to add these filters to the HttpConfiguration object, without any success:
config.Filters.Add(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
config.AddODataQueryFilter(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
I don't know what to investigate more, as things seems to be changing too fast for a newbie like me who's first got into Azure.
EDIT
I forgot to say that asking for the complete table, so for example https://myapp.azurewebsites.net/tables/Race?ZUMO-API-VERSION=2.0.0, returns correctly the entire dataset. The problem occurs only when adding some clauses to the request.
EDIT 2
My model is like this:
public class Race : EntityData
{
public int iRaceId { get; set; }
public int iYear { get; set; }
public int iRound { get; set; }
ecc..
}
and the database table that was automatically created is this, including all the properties inherited from EntityData:
Database table schema
Digging into the source code, Azure Mobile Apps sets up camelCase encoding of all requests and responses. It then puts them back after transmission accordign to rules - so iRaceId becomes IRaceId on the server.
The easiest solution to this is to bypass the auto-naming and use a JsonProperty attribute on each property within your server-side DTO and client-side DTO so that they match and will get encoding/decoded according to your rules.
So:
public class Race : EntityData
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("raceId")]
public int iRaceId { get; set; }
[JsonProperty("year")]
public int iYear { get; set; }
[JsonProperty("round")]
public int iRound { get; set; }
etc..
}

Building processing screen for customer locations

I need to build a processing screen for customer locations that determines and then updates the residential flag on locations.
This code correctly processes each selected record and appears to update the appropriate fields. But the problem I am encountering is that my changes to Location are not being saved back to the database.
The Customer Locations graph requires the business account to be specified before you can enter a Location ID, and I suspect that because of that I cannot simply update the Locations view on the graph. But I cannot find any documentation or code examples indicating what approach I should use here.
Here is the code on my processing screen graph:
public class ProcessCustomerLocations : PXGraph<ProcessCustomerLocations>
{
public PXCancel<Location> Cancel;
public PXProcessing<Location, Where<Location.isActive, Equal<True>>> Locations;
public static void Process(List<Location> locations)
{
var graph = PXGraph.CreateInstance<CustomerLocationMaint>();
CustomerLocationMaint_Extension graphExt = graph.GetExtension<CustomerLocationMaint_Extension>();
foreach (var location in locations)
{
graphExt.UpdateLocation(location, true);
}
}
public ProcessCustomerLocations()
{
Locations.SetProcessDelegate(Process);
}
}
And here is my code on the CustomerLocationMaint_Extension graph:
public class CustomerLocationMaint_Extension : PXGraphExtension<CustomerLocationMaint>
{
public void UpdateLocation(Location location, bool isMassProcess = false)
{
bool isRes = false;
Base.Location.Current = Base.Location.Search<Location.locationID>(location.LocationID, location.BAccountID);
LocationExt locationExt = location.GetExtension<LocationExt>();
// INSERT CODE TO DETERMINE VALUE OF isRes
locationExt.UsrResidentialValidated = true;
location.CResedential = isRes;
Base.Location.Update(location);
Base.Actions.PressSave();
}
}
One of the fields I am updating on Location is a custom field called UsrResidentialValidated. Here is the code for that field.
namespace PX.Objects.CR
{
public class LocationExt : PXCacheExtension<PX.Objects.CR.Location>
{
#region UsrResidentialValidated
[PXDBBool]
[PXUIField(DisplayName="Residential Validated")]
public virtual bool? UsrResidentialValidated { get; set; }
public abstract class usrResidentialValidated : IBqlField { }
#endregion
}
}
Update
Thanks to some help from #Samvel I've modified the UpdateLocation code as follows. The following code does save the changes to the database (both on the custom field and the non-custom field), which is great. However, in order to do that, I had to create a new Location object "myLocation" and am no longer using the "location" object that the PXProcessing graph passed to UpdateLocation. This means that after processing, when the processing screen displays the processed records with the modified data (after processing finishes and before you refresh the screen), it does not show the updated values. Is there any way to both have the processing screen show the updated values and save the changes to the database?
public void UpdateLocation(PX.Objects.CR.Location location, bool isMassProcess = false)
{
bool isRes = true;
Location myLocation = PXSelect<Location,
Where<Location.bAccountID, Equal<Required<Location.bAccountID>>, And<Location.locationID, Equal<Required<Location.locationID>>>>>
.Select(this.Base, location.BAccountID, location.LocationID);
this.Base.Location.Current = myLocation;
LocationExt locationExt = myLocation.GetExtension<LocationExt>();
locationExt.UsrResidentialValidated = true;
myLocation.CResedential = isRes;
Base.Location.Current = Base.Location.Update(myLocation);
this.Base.Save.Press();
}
UPDATED
I have updated the code to correspond to your case. After processing all the records the records in the grid are being updated and showing modified records.
You can download the customization package for this code by this link
To create a processing page for updating Location you should do the following steps:
Add "Selected" field to the Location DAC
public sealed class LocationExt: PXCacheExtension<Location>
{
#region Selected
public abstract class selected : IBqlField
{ }
[PXBool()]
[PXDefault(true,PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Selected")]
public bool? Selected { get; set; }
#endregion
#region UsrResidentialValidated
[PXDBBool]
[PXUIField(DisplayName = "Residential Validated")]
public bool? UsrResidentialValidated { get; set; }
public abstract class usrResidentialValidated : IBqlField { }
#endregion
}
This step is required because otherwise your delegate for SetProcessDelegate will never be called.
Acumatica is checking if there is at least one selected record before calling Process Delegate.
Create the Processing Graph like below:
using PX.Data;
using PX.Objects.CR;
using System.Collections.Generic;
namespace CustomerLocationUpdate
{
public class ProcessCustomerLocations : PXGraph<ProcessCustomerLocations>
{
public PXCancel<Location> Cancel;
public PXProcessingJoin<Location,InnerJoin<BAccountR,On<Location.bAccountID,Equal<BAccountR.bAccountID>>>,
Where<Location.isActive, Equal<True>,And<Location.locType, Equal<PX.Objects.CR.LocTypeList.customerLoc>>>> Locations;
public static void Process(List<Location> locations)
{
var graph = PXGraph.CreateInstance<PX.Objects.AR.CustomerLocationMaint>();
CustomerLocationMaint_Extension graphExt = graph.GetExtension<CustomerLocationMaint_Extension>();
foreach (var location in locations)
{
graphExt.UpdateLocation(location, true);
graph.Clear();
}
}
public ProcessCustomerLocations()
{
Locations.SetProcessDelegate(Process);
}
}
}
As you can see I have implicitly specified PX.Objects.AR and PX.Objects.CR for some reason the program has worked only this way on my instance.
Create the UpdateLocation method in the GraphExtension:
using PX.Data;
namespace CustomerLocationUpdate
{
public class CustomerLocationMaint_Extension : PXGraphExtension<PX.Objects.AR.CustomerLocationMaint>
{
public void UpdateLocation(PX.Objects.CR.Location location, bool isMassProcess = false)
{
bool isRes = false;
this.Base.Location.Current = PXSelect<PX.Objects.CR.Location,Where<PX.Objects.CR.Location.bAccountID,Equal<Required<PX.Objects.CR.Location.bAccountID>>,And<PX.Objects.CR.Location.locationID,Equal<Required<PX.Objects.CR.Location.locationID>>>>>.Select(this.Base,location.BAccountID,location.LocationID);
this.Base.Location.Current.CResedential = isRes;
LocationExt locationExt = PXCache<PX.Objects.CR.Location>.GetExtension<LocationExt>(this.Base.Location.Current);
locationExt.UsrResidentialValidated = false;
this.Base.Location.Current = this.Base.Location.Update(this.Base.Location.Current);
this.Base.Save.Press();
}
}
}
As you can see I am setting the Location.Current using PXSelect and not Location.Current.Search.
For some reason Location.Current.Search is always returning null.
May be it is caused by the PXProjectionAttribute applied to it, I am not sure what is the exact reason.

How to get nested element using ServiceStack?

Although I am able to access the SchemaVersion using code below, I cannot access FormatDocID nested element.
Any ideas how can I easily get FormatDocID using ServiceStack and AutoQueryFeature (or similar)?
I put only relevant parts of code here
public override void Configure(Container container)
{
JsConfig.DateHandler = DateHandler.ISO8601;
SetupValidators(container);
SetupIOC(container);
SetupPlugins(container, log);
ContentTypes.Register("application/xml"
, CLXmlSerializer.Serialize, ServiceStack.Text.XmlSerializer.DeserializeFromStream);
SetupMetaDataRedirectionPath();
SetupGlobalResponseFilters();
}
Setup plugins
private void SetupPlugins(Container container)
{
Plugins.Add(new ValidationFeature());
Plugins.Add(new SwaggerFeature());
Plugins.Add(new AutoQueryFeature
{
MaxLimit = 1000,
EnableUntypedQueries = false,
IncludeTotal = true
});
Plugins.Add(new AutoQueryDataFeature {MaxLimit = 100}
.AddDataSource(ctx => ctx.MemorySource(new List<WordDocument>
{
new WordDocument()
{
SchemaVersion = "",
Format = new Word.DocumentFormat()
{
FormatDocID = 254
}
}
}))
);
typeof(RequestLogs).AddAttributes(new RestrictAttribute {VisibilityTo = RequestAttributes.None});
typeof(AssignRoles).AddAttributes(new RestrictAttribute {VisibilityTo = RequestAttributes.None});
typeof(UnAssignRoles).AddAttributes(new RestrictAttribute {VisibilityTo = RequestAttributes.None});
typeof(Authenticate).AddAttributes(new RestrictAttribute {VisibilityTo = RequestAttributes.None});
}
Serializable classes
public abstract class Document
{
public DocumentFormat Format;
public class DocumentFormat
{
[XmlAttribute] public int Version;
public int FormatDocID;
public string DocShortName;
}
}
public class WordDocument : Document
{
[XmlAttribute] public string SchemaVersion { get; set; } = "1.0";
}
Thanks in advance for the answers.
It's not clear what you're trying to achieve or why, AutoQuery creates Auto Queryable APIs where the Response is the API Response serialized in the specified Response Content Type.
If you want to intercept the Typed Response DTO before it's returned you can create a Custom AutoQuery Implementation and introspect the response that way, e.g:
public class MyQueryServices : Service
{
public IAutoQueryData AutoQuery { get; set; }
//Override with custom implementation
public object Any(MyQuery query)
{
var q = AutoQuery.CreateQuery(query, base.Request);
var response = AutoQuery.Execute(query, q);
return response;
}
}
But the AutoQuery Memory Data Source you're using lets you provide your own collection of Typed POCOs as the Data source so you already have access to them when you create it, but the source POCOs should be a flat Type with public properties (in contrast to your class with public fields and nested types) - it's not possible to query nested object graph values.
This is an example of a POCO that doesn't use nested classes, or public fields:
public abstract class Document
{
public int Version { get; set; }
public int FormatDocID { get; set; }
public string DocShortName { get; set; }
}
So the solution if you want to use AutoQuery would be to change your Data Source to use Flat POCOs with public properties otherwise you'd need to create the impl of your Service yourself.

Automapper ObservableCollection – refreshing is not working

I have small WPF application. There are 5 projects in solution.
I want separate DOMAIN classes with UI ENTITIES and I want to use AUTOMAPPER.
You can download whole solution here: TestWPFAutomapper.zip
Domain class(Domain.Source.cs) with UI Entity(Entities.Destination.cs) have same signature.
In Entities.Destination.cs I would like to put other logic.
namespace DOMAIN
{
public class Source
{
public int Id { get; set; }
public int Position { get; set; }
}
}
using System.ComponentModel;
namespace ENITITIES
{
public class Destination : INotifyPropertyChanged
{
private int _id;
private int _position;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public int Position
{
get { return _position; }
set
{
_position = value;
OnPropertyChanged("Position");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
My data comes from DAL.DataContext using Entity Framework with CodeFirst. Here I´m using Source class.
using System.Data.Entity;
using DOMAIN;
namespace DAL
{
public class DataContext : DbContext
{
public DbSet<Source> Sources { get; set; }
}
}
Mapping is in BL.MyAppLogic.cs . In this class I have property Items which is ObservableCollection.
After puting another item into DB for Source class collection get refresh but for Destination is not refreshing.
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using AutoMapper;
using DAL;
using DOMAIN;
using ENITITIES;
namespace BL
{
public class MyAppLogic
{
private readonly DataContext _dataContext = new DataContext();
public ObservableCollection<Source> Items { get; set; }
//public ObservableCollection<Destination> Items { get; set; }
public MyAppLogic()
{
Database.SetInitializer(new MyInitializer());
Mapping();
_dataContext.Sources.Load();
Items = _dataContext.Sources.Local;
//Items = Mapper.Map<ObservableCollection<Source>, ObservableCollection<Destination>>(_dataContext.Sources.Local);
}
private void Mapping()
{
Mapper.CreateMap<Source, Destination>().ReverseMap();
// I tried also Mapper.CreateMap<ObservableCollection<Source>, ObservableCollection<Destination>>().ReverseMap();
}
public int GetLastItem()
{
return _dataContext.Database.SqlQuery<int>("select Position from Sources").ToList().LastOrDefault();
}
public void AddNewItem(Destination newItem)
{
_dataContext.Sources.Add(Mapper.Map<Destination, Source>(newItem));
_dataContext.SaveChanges();
}
}
}
My problem is not with mapping, that’s works good, but with refreshing collection after adding or removing items from db. If I use DOMAIN.Source class everything works, collection is refreshing. But when I’m using ENTITIES.Destination data comes from DB and also I can put som new data to DB but refresing ObservableCollection is not working.
Please try to comment lines(14 & 23) in BL.MyAppLogic.cs and uncomment(15 & 24) and you’ll see what I mean.
Thank you for any help.
I got it but I don´t know if is correct.
Local has CollectionChanged event
so in constructor I put these lines
public MyAppLogic()
{
Database.SetInitializer(new MyInitializer());
Mapping();
_dataContext.Sources.Load();
_dataContext.Sources.Local.CollectionChanged += SourcesCollectionChanged;
Items = Mapper.Map<ObservableCollection<Source>, ObservableCollection<Destination>>(_dataContext.Sources.Local);
}
and handler looks
private void SourcesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var source = sender as ObservableCollection<Source>;
Mapper.Map(source, Items);
}
Now is my collection automating refreshing when I put something to DB in my UI.
Looks like automapper don´t put reference into Items, but create new instance.

Resources