Cannot update contract in D365 for Finance and Operations - c#

I am trying to update EmploymentEndDate in Contract in D365 Finance and Operation with oData using DataServiceCollection.
There is no error and it seems everything went well, but in the Contract EmploymentEndDate has no changes.
Similar code working well for other entities as Workers, Employee etc...
Has anyone experienced a similar situation, or to suggest other solution for this problem?
Thank You
public bool UpdateContractNG(string pPersonnelNumber, DateTime pEndEmploymentDate)
{
try
{
Resources _contextUpd = Queries365.CreateErpContext();
var queryUpd = from Employment
in _contextUpd.Employments
where Employment.PersonnelNumber == pPersonnelNumber
orderby Employment.EmploymentStartDate descending
select Employment;
DataServiceCollection<Employment> employmentCollection = new DataServiceCollection<Employment>(queryUpd);
if (employmentCollection != null)
{
foreach (Employment emp in employmentCollection)
{
Console.WriteLine($"OLD EMPLOYMENT: {JsonConvert.SerializeObject(emp)}");
emp.EmploymentEndDate = pEndEmploymentDate.ToUniversalTime();
_contextUpd.UpdateObject(emp);
Console.WriteLine($"IN MEMORY CHANGED EMPLOYMENT: {JsonConvert.SerializeObject(emp)}");
break;
}
}
DataServiceResponse response = _contextUpd.SaveChanges(SaveChangesOptions.PostOnlySetProperties);
// for testing (same context Check)
Employment emp2 = Queries365.GetContract(_contextUpd, pPersonnelNumber);
Console.WriteLine($"FROM SAME CONTEXT EMPLOYMENT: {JsonConvert.SerializeObject(emp2)}");
// new context refreshed data
Resources _newContext = Queries365.CreateErpContext();
Employment empAfterNew = Queries365.GetContract(_newContext, pPersonnelNumber);
Console.WriteLine($"AFTER UPDATE NEW CONTEXT: {JsonConvert.SerializeObject(empAfterNew)}");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Error updating contract with Id {pPersonnelNumber} with Exception: {ex} ");
return false;
}
}
public static Resources CreateErpContext()
{
// Resource
string ODataEntityPath = ClientConfiguration.Default.UriString + "data";
Uri oDataUri = new Uri(ODataEntityPath, UriKind.Absolute);
Resources context = new Resources(oDataUri);
context.SendingRequest2 += new EventHandler<SendingRequest2EventArgs>(delegate (object sender, SendingRequest2EventArgs e)
{
var authenticationHeader = OAuthHelper.GetAuthenticationHeader(useWebAppAuthentication: true);
e.RequestMessage.SetHeader(OAuthHelper.OAuthHeader, authenticationHeader);
});
return context;
}
public static Employment GetContract(Resources context, string personnelNumber)
{
Employment emp = context.Employments.Where(x => x.PersonnelNumber == personnelNumber)
.OrderByDescending(x => x.EmploymentStartDate).FirstOrDefault();
return emp;
}
The output line:
Console.WriteLine($"AFTER UPDATE NEW CONTEXT {JsonConvert.SerializeObject(empAfterNew)}");
returns unexpected value for end date: "EmploymentEndDate":"2019-12-31T23:00:00+00:00".
And it is not updated in the system D365 for Finance and Opereations

Related

Record not adding to SQL Server despite data being passed correctly? (ASP.NET WEB API and Angular)

I am trying to add two records to two separate entities using one action result.
My data is passing just fine, and no catches are met (implying that the database is saving the necessary changes and returning the appropriate Ok).
My two entities are related, one entity is linked to the other through a Foreign Key. The entities are created in such a way that the entity dependent on the other (for its foreign key) is created after the other.
The first entity (Branch) is created and is stored just fine as expected on the SQL Server. However, the second is not, and I am not sure why?
Both entities are set to auto-increment their primary keys.
My action result (within my web API is split into two separate methods):
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Branch.Add(branch);
db.SaveChanges();
var latestBranchRecordReader = db.Branch.OrderByDescending(x => x.BranchId).First();
var latestBranchRecord = latestBranchRecordReader.BranchId;
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
CreateBranchAddress(branchAddress);
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
public ActionResult CreateBranchAddress(BranchAddress newBranchAddress)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
db.BranchAddress.Add(newBranchAddress);
db.SaveChanges();
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
Both db.SaveChanges() are hit when debugging, so I do not understand why nothing is being added to the database for the BranchAddress entity.
The data coming through newBranch of type BranchesVM is also correct (I get no POST 400 errors).
Are there any other possible reasons as to why it may not be adding?
Does it need to have 2 ActionResult? If it's possible to have just one, you could try this:
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Add(branch);
db.SaveChanges();
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
db.Add(branchAddress);
db.SaveChanges();
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
You might also want to wrap it using transaction to make sure both query executed successfully, otherwise it will rollback.
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
//START TRANSACTION
using (var transSQL = db.Database.BeginTransaction())
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Add(branch);
db.SaveChanges();
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
db.Add(branchAddress);
db.SaveChanges();
//SQL Commit
transSQL.commit();
return Ok();
}
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
I realized that I did not post enough information in my question to get an appropriate answer. However, I changed my catch to output an inner exception:
catch (Exception e)
{
return BadRequest(e.InnerException);
}
This allowed me to see what exactly was causing the error, that being one of the records exceeding its maximum size as set out in the table.

Is it bad practice to add an entry to a table like so using Entity Framework?

I'm new to Entity Framework and was wondering if my code is considered bad practice and whether or not it could lead to deadlock.
var context = new DatabaseEntities();
var demoTable = new DemoTable();
var newEntry = context.demoTable.Create();
newEntry.Message = "Test message";
newEntry.Timestamp = DateTime.Now;
if (addEntryToTable(context, demoTable, newLine))
{
Console.WriteLine("Table updated.");
}
my function :
private bool addEntryToTable (DatabaseEntities context, System.Data.Entity.DbSet demoTable, Object newLine)
{
try
{
demoTable.Add(newLine);
context.SaveChanges();
return true;
}
catch
{
demoTable.Remove(newLine);
context.SaveChanges();
return false;
}
}

Cosmos Insert works only once

I'm hoping someone can help me with this issue. I have two methods that insert a document into a Cosmos DB. One works each and every time I run it. The other gets as far as CreateDatabaseIfNotExistsAsync and kicks out of the method and returns to the calling method. The ItemReponse objects Status property says "WaitingForActivation".
I will say that this logic did work once and now (with no changes) it does not. Below is the code that dies. First the Unit test:
public void AddPerson()
{
var logger = Mock.Of<ILogger<Person>>();
var appset = Mock.Of<IAppSettings>();
appset.DatabaseName = "PersonsDB";
appset.ContainerName = "PersonsContainer";
appset.EndpointUrl = "https://localhost:8081";
appset.AuthorizationKey = "somesecretkey";
Person person = new Person()
{
id = Guid.NewGuid().ToString(),
Address_line_1 = "1932 Alakea St",
Address_line_2 = "",
Birth_Date = "2020-01-01",
Gender = "m",
Patient_First_Name = "John",
Patient_Last_Name = "Ridley",
Hospital_Code = "Queens Med",
Rec_ID = "1111111111",
VER_CRM_ID = "22222222",
VER_EMAIL = "JR#noemail.com",
};
PersonCosmos personCosmos = new PersonCosmos(logger, appset);
var myresult = personCosmos.PutPersonData(person);
Assert.True(myresult.Exception == null, "Row added");
}
Let me add that I am running the Cosmos Emulator hence the localhost url. Now for the code that dies.
public async Task<ItemResponse<Person>> PutPersonData(Person persondata)
{
this.cosmosClient = new CosmosClient(EndpointUri, PrimaryKey);
this.database = await this.cosmosClient.CreateDatabaseIfNotExistsAsync(DatabaseName);
this.container = await this.database.CreateContainerIfNotExistsAsync(ContainerName, "/Hospital_Code");
ItemResponse<Person> PutResponse = null;
try
{
// Read the item to see if it exists.
PutResponse = await this.container.ReadItemAsync<Person>(persondata.id.ToString(), new PartitionKey(persondata.Hospital_Code ));
return PutResponse;
}
catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
// Create an item in the container representing the Appointment. Note we provide the value of the partition key for this item, which is "Hospital_Code"
PutResponse = await this.container.CreateItemAsync<Person>(persondata, new PartitionKey(persondata.Hospital_Code));
return PutResponse;
}
catch (Exception ex)
{
// Create an item in the container representing the object. Note we provide the value of the partition key for this item, which is "Hospital_Code"
_logger.LogError($"Error in {0} at {1} " + ex.Message, System.Reflection.MethodBase.GetCurrentMethod().Name, DateTime.UtcNow.ToLongTimeString());
return PutResponse;
}
}
You are mixing sync and async in your code here.
Use await on this line.
var myresult = **await** personCosmos.PutPersonData(person);
and also this calling function should be async as well
public void AddPerson()

Unable to find entity while under a transaction

I'm having an issue with this data layer method
public async Task<(bool HasFailed, IList<string> ErrorMessages)> ApproveBillCancelRequest(IEnumerable<string[]> billsToApprove,
string userId, string operationId)
{
var callerInfo = Shared.CommonAcross.Helper.GetCaller();
InfoScope($"{LayerName} -> {callerInfo.MethodName} -> Started", operationId, userId);
var errorMessages = new List<string>();
using (var context = new FocusConnection())
{
var transaction = context.Database.BeginTransaction();
try
{
foreach (var billToApprove in billsToApprove)
{
var targetBillNumber = billToApprove[0];
var targetPayModeId = int.Parse(billToApprove[1]);
var entityBill = context.BILL_INFO_CANCEL_REQUESTS
.SingleOrDefault(where =>
where.BILL_NUMBER == targetBillNumber &&
where.PAY_MODE_ID == targetPayModeId);
if (entityBill == null)
{
errorMessages.Add($"Bill #{billToApprove[0]}, payment #{billToApprove[1]} was not found for cancel approval");
continue;
}
entityBill.BILL_INFO.LAST_MODIFIED_BY = userId;
entityBill.BILL_INFO.STAMP_DATE = DateTime.Now;
entityBill.BILL_INFO.INPUT_STATUS = 0;
var cancelledBill = new BILL_INFO_CANCELED
{
BILL_NUMBER = entityBill.BILL_NUMBER,
PAY_MODE_ID = entityBill.PAY_MODE_ID,
CASHIER_ID = entityBill.CASHIER_ID,
CANCELED_DATE = entityBill.CANCEL_REQUEST_DATE,
CANCEL_REQUESTED_BY = entityBill.CANCEL_REQUESTED_BY,
CANCEL_APPROVED_BY = userId,
REMARKS = entityBill.CANCELATION_REASON
};
// Add cancelled bill
context.BILL_INFO_CANCELEDS.Add(cancelledBill);
// Remove cancellation request
context.BILL_INFO_CANCEL_REQUESTS.Remove(context.BILL_INFO_CANCEL_REQUESTS.Single(where =>
where.BILL_NUMBER == cancelledBill.BILL_NUMBER && where.PAY_MODE_ID == cancelledBill.PAY_MODE_ID));
await context.SaveChangesAsync();
}
transaction.Commit();
}
catch (Exception exp)
{
transaction?.Rollback();
ErrorScope($"{LayerName} -> {callerInfo.MethodName} -> Exception [{exp.Message}]", exp, operationId, userId);
errorMessages.Add($"{LayerName} -> {callerInfo.MethodName} -> Exception [{exp.Message}]");
return (true, errorMessages);
}
}
return (errorMessages.Any(), errorMessages);
}
The issue is that in the ForEach loop, the first record is retrieved properly and processed. But the remaining records are never found (entityBill == null) but they are in the database. I believe it has something to do with the running transaction.
Can anyone help out?
This was a hard one to crack. It turns out that while String.Splitting() on the UI layer, there was an extra space being appended to the first index of the array within the List of arrays. So I fixed that by:
var targetBillNumber = billToApprove[0].Trim();
var targetPayModeId = int.Parse(billToApprove[1].Trim());

Can I do an UpsertRequest that checks if the name of the entity exists?

In C#, I'm writing a program that transfers data from an old Microsoft Dynamics CRM system to a new one.
For most of the entities, I can use the UpsertRequest. Though, for contacts and accounts, there are already records in the new environment. As I don't want to have doubles, I would like to have the UpsertRequest check on the "Name" field in case of accounts and on "Fullname" in case of contacts.
Would that be possible? I (searched a lot and) cannot find examples on this. If not, what would be the best way to proceed?
Thanks for any feedback.
for such case I would make a plugin on message create of contact and account such like this one :
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService organizationService = serviceFactory.CreateOrganizationService(context.UserId);
Entity TargetEntity = new Entity();
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
TargetEntity = (Entity)context.InputParameters["Target"];
QueryExpression queryDuplicateDetect = new QueryExpression(TargetEntity.LogicalName);
if (TargetEntity.LogicalName == "account" && TargetEntity.Attributes.Contains("name"))
{
queryDuplicateDetect.ColumnSet = new ColumnSet(new string[] { "name" });
queryDuplicateDetect.Criteria.AddCondition(new ConditionExpression("name", ConditionOperator.Equal, TargetEntity["name"].ToString()));
}
else if (TargetEntity.LogicalName == "contact" && TargetEntity.Attributes.Contains("fullname"))
{
queryDuplicateDetect.ColumnSet = new ColumnSet(new string[] { "fullname" });
queryDuplicateDetect.Criteria.AddCondition(new ConditionExpression("fullname", ConditionOperator.Equal, TargetEntity["fullname"].ToString()));
}
try
{
EntityCollection resultsColl = organizationService.RetrieveMultiple(queryDuplicateDetect);
if (resultsColl.Entities.Count > 0)
{
foreach (Entity e in resultsColl.Entities)
{
tracingService.Trace("Record Found with ID {0}", e.Id);
}
//log results in some entity for more info
throw new InvalidPluginExecutionException("Duplicate detected.");
}
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message);
}
}
}
and on the create side i'll use simple try catch to skip existing records
Entity x = new Entity("account");
x["name"] = "mohamed0";
Entity y = new Entity("contact");
y["fullname"] = "mohamed";
Entity z = new Entity("contact");
z["fullname"] = "mohamed";
try
{
var r = _orgService.Create(x);
r = _orgService.Create(y);
r = _orgService.Create(z);
}
catch (Exception e)
{
throw;
}
hope this will help.

Categories

Resources