I have created code which adds,deletes and modify objects added to the arraylist.when i select the remove method it shows the above error.how do i solve it.This code is where i am running everything.It has an instance of the member class which has all methods neccessary
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MedicalAid
{
class MedicalTest
{
//instance of MedicalTest class
public static MedicalTest medicalMember = new MedicalTest();
//array list to hold member objects
static List<Member> customer = new List<Member>();
//instance of Member class
static Member member = new Member();
//some important booleans
private bool isSubscribed;
private bool isDeducted;
private bool isToBeRemoved;
private bool isToBeAdded = true;
//add passed memebers to arraylist
public void addMembersToArrayList()
{
customer.Add(member1);
customer.Add(member2);
isToBeAdded = false;
}
//method to add member
public void AddMember(Member name)
{
customer.Add(name);
}
//method to remove a member
public void RemoveMember(String removeName) {
foreach (Member i in customer) {
if (isToBeRemoved)
{
if (i.GetName() == removeName)
{
Console.WriteLine("Found and Removed");
customer.Remove(i);
}
else{Console.WriteLine("Not Found");}
}
if(isSubscribed)
{
if (i.GetName() == removeName)
{
//if delete member is true,delete member
Console.WriteLine("Found and Transaction Made");
i.makeSubscription();
i.showMember();
}//closes isToBeDeleted
else { Console.WriteLine("Not Found"); }
}
if(isDeducted){
if (i.GetName() == removeName)
{
//if delete member is true,delete member
Console.WriteLine("Found and Transaction Made");
i.makeSubscription();
i.showMember();
}//closes isToBeDeleted
else
{
Console.WriteLine("Not Found");
}
}//closes deducted if
}
}
//method to iterate through customer and remove a member
public void ViewMembers()
{
//iterate throus the customer list and print details of any member availabe
if(customer.Capacity == 0){
Console.WriteLine("Medical Aid List is Empty");
}else{
foreach(Member i in customer){
i.showMember();
}
}
}
//create two objects with details
Member member1 = new Member("male",
"Z.N.A",
" 272 Area 3 D/Mvura Mutare",
"Premium",
"JAMES",
500.00,
"Dr Bvirakure",
"xx-xxxxx y XX",
//spouse
"xx/xx/1987",
"JOSEPHINE MANYORE",
"XX-XXXXX-XX",
//family doctor
"DANGAMVURA SHOPPING MALL",
"0773 0733 0734",
//dependent
"male",
"ANDREW BLESSING MANYORE",
"75-426820 Y 50",
//bank details
"ZABG",
"Herbet Chitepo",
"xxxxxxxxxxxxx",
"xxxxxxxxxxxxx",
"Mutare");
Member member2 = new Member("female",
"MINISTRY OF EDUCATION",
" 272 Area 3 D/Mvura Mutare",
"Premium",
"TAPIWA",
500.00,
"Dr Bvirakure",
"xx-xxxxx y XX",
//spouse
"xx/xx/1987",
"JAMES MANYORE",
"XX-XXXXX-XX",
//family doctor
"DANGAMVURA SHOPPING MALL",
"0773 0733 0734",
//dependent
"male",
"PORTIA TATENDA MANYORE",
"75-426820 Y 50",
//bank details
"ZB",
"Herbet Chitepo",
"xxxxxxxxxxxxx",
"xxxxxxxxxxxxx",
"Mutare");
//method to print saved members
static void Main(string[] args)
{
int option;
string options;
//add the members to the arraylist
if (medicalMember.isToBeAdded)
{
medicalMember.addMembersToArrayList();
}
do{
Console.Write("********Medical Aid*********\n"+
"1.To Add New Member\n"+
"2.To Edit Member Balance if he made a Subscription\n" +
"3.To Edit Member Balance if he received a Service\n" +
"4.To Delete Old Member\n" +
"5.To View Members\n"+
"6.To Exit\n");
options = Console.ReadLine();
option = Convert.ToInt32(options);
switch(option){
case 1: member.GetMember();
medicalMember.AddMember(member);
break;
case 2 : medicalMember.isSubscribed = true;
medicalMember.isDeducted = false;
medicalMember.isToBeRemoved = false;
Console.WriteLine("Enter Member Name who made a Subscription\n");
String memberToGetSer = Console.ReadLine();
medicalMember.RemoveMember(memberToGetSer);
break;
case 3 :medicalMember.isSubscribed = false;
medicalMember.isDeducted = true;
medicalMember.isToBeRemoved = false;
Console.WriteLine("Enter Member Name who received a Service\n");
String memberToGetSub = Console.ReadLine();
medicalMember.RemoveMember(memberToGetSub);
break;
case 4: medicalMember.isSubscribed = false;
medicalMember.isDeducted = false;
medicalMember.isToBeRemoved = true;
Console.WriteLine("Enter Member Name to remove");
String memberToRemove = Console.ReadLine();
medicalMember.RemoveMember(memberToRemove);
break;
case 5: medicalMember.ViewMembers();
break;
case 6: Console.WriteLine("******EXITING********");
Environment.Exit(0);
break;
}//closes switch
}while(option<=5);//closes while
}//closes main
}//closes class
}
You can't call Remove() while inside foreach loop (as long as it concerns the collection you are looping through)
use a for loop:
for (int i=0;i<customer.Count;i++)
{
......
}
From MSDN:
The foreach statement is used to iterate through the collection to get the information that you want,
but can not be used to add or remove items from the source collection to avoid unpredictable side effects.
If you need to add or remove items from the source collection, use a for loop.
See this for more details
just change this in remove Member:
public void RemoveMember(String removeName) {
for (int i=customer.Count - 1;i>=0;i--) {
if (isToBeRemoved)
{
if (customer[i].GetName() == removeName)
{
Console.WriteLine("Found and Removed");
customer.RemoveAt(i);
}
else{Console.WriteLine("Not Found");
}
}
}
You can't remove elements if you're reading forward through a collection as the enumerator would be invalidated. Try using the RemoveAll method, it will do what you want and simplify your code:
if (isToBeRemoved) // No need for a for loop.
{
customer.RemoveAll(elem => elem.GetName() == removeName);
}
Related
I have inherited an older customization to the Purchase Receipts / PO302000 screen that I'm trying to upgrade, and it had customization code to import Lot/Serial nbrs from an Excel spreadsheet. It all seems to work alright, except that at the end, it errors out when pressing a button as follows:
Base.Actions["LSPOReceiptLine_binLotSerial"].Press();
Here's the entire code:
public virtual void importAllocations()
{
try
{
if (Base.transactions.Current != null)
{
var siteid = Base.transactions.Current.SiteID;
if (Base.splits.Select().Count == 0)
{
if (this.NewRevisionPanel.AskExt() == WebDialogResult.OK)
{
const string PanelSessionKey = "ImportStatementProtoFile";
PX.SM.FileInfo info = PX.Common.PXContext.SessionTyped<PXSessionStatePXData>().FileInfo[PanelSessionKey] as PX.SM.FileInfo;
System.Web.HttpContext.Current.Session.Remove(PanelSessionKey);
if (info != null)
{
byte[] filedata = info.BinData;
using (NVExcelReader reader = new NVExcelReader())
{
Dictionary<UInt32, string[]> data = reader.loadWorksheet(filedata);
foreach (string[] textArray in data.Values)
{
if (textArray[0] != GetInventoryCD(Base.transactions.Current.InventoryID))
{
throw (new Exception("InventoryID in file does not match row Inventory ID"));
}
else
{
//Find the location ID based on the location CD provided by the Excel sheet...
INLocation inloc = PXSelect<INLocation,
Where<INLocation.locationCD, Equal<Required<INLocation.locationCD>>,
And<INLocation.siteID, Equal<Required<INLocation.siteID>>>>>.Select(Base
, textArray[1]
, Base.transactions.Current.SiteID);
Base.splits.Insert(new POReceiptLineSplit()
{
InventoryID = Base.transactions.Current.InventoryID,
LocationID = inloc.LocationID, //Convert.ToInt32(textArray[1]), //Base.transactions.Current.LocationID,
LotSerialNbr = textArray[2],
Qty = Decimal.Parse(textArray[3])
});
}
}
}
}
}
}
}
Base.Actions["LSPOReceiptLine_binLotSerial"].Press();
}
catch (FileFormatException fileFormat)
{
// Acuminator disable once PX1053 ConcatenationPriorLocalization [Justification]
throw new PXException(String.Format("Incorrect file format. File must be of type .xlsx", fileFormat.Message));
}
catch (Exception ex)
{
throw ex;
}
}
Now, there seems to be no such button - and I have no idea what it would be called now, or if it even still exists. I don't even really know what this action did.
Any ideas?
Thanks much...
That logic has been moved into the PX.Objects.PO.GraphExtensions.POReceiptEntryExt.POReceiptLineSplittingExtension. That action was doing the following in the PX.Objects.PO.LSPOReceiptLine
// PX.Objects.PO.LSPOReceiptLine
// Token: 0x0600446F RID: 17519 RVA: 0x000EE86C File Offset: 0x000ECA6C
public override IEnumerable BinLotSerial(PXAdapter adapter)
{
if (base.MasterCache.Current != null)
{
if (!this.IsLSEntryEnabled((POReceiptLine)base.MasterCache.Current))
{
throw new PXSetPropertyException("The Line Details dialog box cannot be opened because changing line details is not allowed for the selected item.");
}
this.View.AskExt(true);
}
return adapter.Get();
}
Now it is called ShowSplits and is part of the POReceiptLineSplittingExtension extension.
// PX.Objects.PO.GraphExtensions.POReceiptEntryExt.POReceiptLineSplittingExtension
// Token: 0x06005359 RID: 21337 RVA: 0x00138621 File Offset: 0x00136821
public override IEnumerable ShowSplits(PXAdapter adapter)
{
if (base.LineCurrent == null)
{
return adapter.Get();
}
if (!this.IsLSEntryEnabled(base.LineCurrent))
{
throw new PXSetPropertyException("The Line Details dialog box cannot be opened because changing line details is not allowed for the selected item.");
}
return base.ShowSplits(adapter);
}
Given the fact that ShowSplits is defined in the LineSplittingExtension originally it may be referred to as "LineSplittingExteions_ShowSplits" or "POReceiptLineSplittingExtension_ShowSplits". I would suggest including that POReceiptLineSplittingExtension as part of your extension and simply call the Base1.ShowSplits
I am working on customization to add some filters to the existing data view.
The Activity data view on the Employee Time Activities page.
I have added the Customer property to PMTimeActivity and OwnedFilter.
Now I need to modify the activity method to take into consideration the Customer filter.
The only way to do this is to override the method with one of the following scenarios:
without calling the base method and copying the code and adding the
filter part
with calling the base method and checking the condition on each
returned record.
The first scenario is making this part of customization very problematic because it will require to review this code every time the customization is being upgraded to any other build.
The second scenario is not good from a performance view.
Has anybody faced this issue and how can this be done in an acceptable way?
Below is the code of the activity method:
protected virtual IEnumerable activity()
{
List<object> args = new List<object>();
EmployeeActivitiesEntry.PMTimeActivityFilter filterRow = this.Filter.Current;
if (filterRow == null)
{
return null;
}
BqlCommand cmd = BqlCommand.CreateInstance(new Type[]
{
typeof(Select2<EPActivityApprove, LeftJoin<EPEarningType, On<EPEarningType.typeCD, Equal<PMTimeActivity.earningTypeID>>, LeftJoin<CRActivityLink, On<CRActivityLink.noteID, Equal<PMTimeActivity.refNoteID>>, LeftJoin<CRCase, On<CRCase.noteID, Equal<CRActivityLink.refNoteID>>, LeftJoin<ContractEx, On<CRCase.contractID, Equal<ContractEx.contractID>>>>>>, Where<EPActivityApprove.ownerID, Equal<Current<EmployeeActivitiesEntry.PMTimeActivityFilter.ownerID>>, And<EPActivityApprove.trackTime, Equal<True>, And<PMTimeActivity.isCorrected, Equal<False>>>>, OrderBy<Desc<EPActivityApprove.date>>>)
});
if (filterRow.ProjectID != null)
{
cmd = cmd.WhereAnd<Where<EPActivityApprove.projectID, Equal<Current<EmployeeActivitiesEntry.PMTimeActivityFilter.projectID>>>>();
}
if (filterRow.ProjectTaskID != null)
{
cmd = cmd.WhereAnd<Where<EPActivityApprove.projectTaskID, Equal<Current<EmployeeActivitiesEntry.PMTimeActivityFilter.projectTaskID>>>>();
}
if (filterRow.FromWeek != null || filterRow.TillWeek != null)
{
List<Type> cmdList = new List<Type>();
bool? includeReject = filterRow.IncludeReject;
bool flag = true;
if (includeReject.GetValueOrDefault() == flag & includeReject != null)
{
cmdList.Add(typeof(Where<, , >));
cmdList.Add(typeof(EPActivityApprove.approvalStatus));
cmdList.Add(typeof(Equal<ActivityStatusListAttribute.rejected>));
cmdList.Add(typeof(Or<>));
}
if (filterRow.FromWeek != null)
{
if (filterRow.TillWeek != null)
{
cmdList.Add(typeof(Where<, , >));
}
else
{
cmdList.Add(typeof(Where<, >));
}
cmdList.Add(typeof(EPActivityApprove.weekID));
cmdList.Add(typeof(GreaterEqual<Required<EmployeeActivitiesEntry.PMTimeActivityFilter.fromWeek>>));
args.Add(filterRow.FromWeek);
if (filterRow.TillWeek != null)
{
cmdList.Add(typeof(And<>));
}
}
if (filterRow.TillWeek != null)
{
cmdList.Add(typeof(Where<EPActivityApprove.weekID, LessEqual<Required<EmployeeActivitiesEntry.PMTimeActivityFilter.tillWeek>>>));
args.Add(filterRow.TillWeek);
}
cmd = cmd.WhereAnd(BqlCommand.Compose(cmdList.ToArray()));
}
return new PXView(this, false, cmd).SelectMultiBound(new object[]
{
this.Filter.Current
}, args.ToArray());
}
I would consider to try work with PXView, which was described by Sergey here.
In case of Employee activities time card it may look like this:
public class EmployeeExt : PXGraphExtension<EmployeeActivitiesEntry>
{
protected virtual IEnumerable activity()
{
var sel = new PXView(Base, true, Base.Activity.View.BqlSelect);
if(true)
{
sel.WhereAnd<Where<EPActivityApprove.projectID, Equal<Current<EmployeeActivitiesEntry.PMTimeActivityFilter.projectID>>>>();
}
int totalRow = 0;
int startRow = PXView.StartRow;
return sel.Select(PXView.Currents, PXView.Parameters,
PXView.Searches, PXView.SortColumns, PXView.Descendings,
PXView.Filters, ref startRow, PXView.MaximumRows, ref totalRow);
}
}
I am using the below code to check the phone status(if phone is up or down). When phone is down sends an alarm. However this doesn't show when 8800 series phones are down. Is there any other method to check the Phone register/unregister status?
#Override public void terminalChangedEvent(TermEv[] eventList) {
if ( eventList != null ) {
for (TermEv eventList1 : eventList) {
if (eventList1 instanceof CiscoTermInServiceEv){
if(terminalInService.test()==true){
LogSQL.removeLog(terminal.getName());
}
System.out.println(terminal.getName());
terminalInService.set();
return;
} else if (eventList1 instanceof CiscoTermOutOfServiceEv &&
terminalInService.test()==true) {
offline();
}
}
}
}
Second Question, I was not able to find the methods or documentation about "com.cisco.cti.util.Condition" class. What does Condition.set() and Condition.test() methods do?
Looks like you have the right general idea - JTAPI should work fine for 88xx models, assuming you have the correct device->user association, and user permissions (Standard CTI Enabled, and Standard CTI Allow Control of Phones supporting Connected Xfer and conf needed for 88xx).
Here is my version working on CUCM 11.5:
package com.mycompany.app;
import com.cisco.jtapi.extensions.*;
import java.util.*;
import javax.telephony.*;
import javax.telephony.events.*;
import javax.telephony.callcontrol.*;
import javax.telephony.callcontrol.events.*;
import com.cisco.cti.util.Condition;
public class DataTerm implements ProviderObserver, TerminalObserver {
public static final int OUT_OF_SERVICE = 0;
public static final int IN_SERVICE = 1;
private Address destAddress;
private CiscoTerminal observedTerminal;
private boolean addressInService;
private boolean terminalInService;
protected int state = OUT_OF_SERVICE;
Condition conditionInService = new Condition();
Provider provider;
public DataTerm(String[] args) {
try {
System.out.println("Initializing Jtapi");
String providerName = "ds-ucm115-1.cisco.com";
String login = "dstaudt";
String passwd = "password";
String dest = "2999";
JtapiPeer peer = JtapiPeerFactory.getJtapiPeer(null);
String providerString = providerName + ";login=" + login + ";passwd=" + passwd;
System.out.println("Opening " + providerString + "...\n");
provider = peer.getProvider(providerString);
provider.addObserver(this);
conditionInService.waitTrue();
this.destAddress = provider.getAddress(dest);
this.observedTerminal = (CiscoTerminal) destAddress.getTerminals()[0];
try {
if (destAddress != null) {
System.out.println("Adding Terminal Observer to Terminal" + observedTerminal.getName());
observedTerminal.addObserver(this);
}
} catch (Exception e) {
}
} catch (Exception e) {
System.out.println("Caught exception " + e);
}
}
public void terminalChangedEvent(TermEv[] events) {
for (int i = 0; i < events.length; i++) {
Terminal terminal = events[i].getTerminal();
switch (events[i].getID()) {
case CiscoTermInServiceEv.ID:
System.out.println("Received " + events[i] + "for " + terminal.getName());
terminalInService = true;
break;
case CiscoTermOutOfServiceEv.ID:
System.out.println("Received " + events[i] + "for " + terminal.getName());
terminalInService = false;
if (state != OUT_OF_SERVICE) { // you only want to notify when you had notified earlier that you are IN_SERVICE
state = OUT_OF_SERVICE;
}
break;
}
}
}
public void providerChangedEvent(ProvEv[] eventList) {
if (eventList != null) {
for (int i = 0; i < eventList.length; i++) {
if (eventList[i] instanceof ProvInServiceEv) {
conditionInService.set();
}
}
}
}
}
The "com.cisco.cti.util.Condition" seems to be based on this pattern:
public interface Condition
Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.
Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to "wait") until notified by another thread that some state condition may now be true. Because access to this shared state information occurs in different threads, it must be protected, so a lock of some form is associated with the condition. The key property that waiting for a condition provides is that it atomically releases the associated lock and suspends the current thread, just like Object.wait.
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html
I am trying to get multithreading more unraveled in my head. I made these three classes.
A global variable class
public partial class globes
{
public bool[] sets = new bool[] { false, false, false };
public bool boolChanged = false;
public string tmpStr = string.Empty;
public int gcount = 0;
public bool intChanged = false;
public Random r = new Random();
public bool gDone = false;
public bool first = true;
}
Drop in point
class Driver
{
static void Main(string[] args)
{
Console.WriteLine("start");
globes g = new globes();
Thread[] threads = new Thread[6];
ParameterizedThreadStart[] pts = new ParameterizedThreadStart[6];
lockMe _lockme = new lockMe();
for (int b = 0; b < 3; b++)
{
pts[b] = new ParameterizedThreadStart(_lockme.paramThreadStarter);
threads[b] = new Thread(pts[b]);
threads[b].Name = string.Format("{0}", b);
threads[b].Start(b);
}
}
}
And then my threading class
class lockMe
{
#region Fields
private string[] words = new string[] {"string0", "string1", "string2", "string3"};
private globes g = new globes();
private object myKey = new object();
private string[] name = new string[] { String.Empty, String.Empty, String.Empty };
#endregion
#region methods
// first called for all threads
private void setName(Int16 i)
{
Monitor.Enter(myKey);
{
try
{
name[i] = string.Format("{0}:{1}", Thread.CurrentThread.Name, g.r.Next(100, 500).ToString());
}
finally
{
Monitor.PulseAll(myKey);
Monitor.Exit(myKey);
}
}
}
// thread 1
private void changeBool(Int16 a)
{
Monitor.Enter(myKey);
{
try
{
int i = getBools();
//Thread.Sleep(3000);
if (g.gcount > 5) { g.gDone = true; return; }
if (i == 3) resets();
else { for (int x = 0; x <= i; i++) { g.sets[x] = true; } }
Console.WriteLine("Thread {0} ran through changeBool()\n", name[a]);
}
finally
{
Monitor.PulseAll(myKey);
Monitor.Exit(myKey);
}
}
}
// thread 2
private void changeInt(Int16 i)
{
Monitor.Enter(myKey);
{
try
{
g.gcount++;
//Thread.Sleep(g.r.Next(1000, 3000));
Console.WriteLine("Thread {0}: Count is now at {1}\n", name[i], g.gcount);
}
finally
{
Monitor.PulseAll(myKey);
Monitor.Exit(myKey);
}
}
}
// thread 3
private void printString(Int16 i)
{
Monitor.Enter(myKey);
{
try
{
Console.WriteLine("...incoming...");
//Thread.Sleep(g.r.Next(1500, 2500));
Console.WriteLine("Thread {0} printing...{1}\n", name[i], words[g.r.Next(0, 3)]);
}
finally
{
Monitor.PulseAll(myKey);
Monitor.Exit(myKey);
}
}
}
// not locked- called from within a locked peice
private int getBools()
{
if ((g.sets[0] == false) && (g.sets[1] == false) && (g.sets[2] == false)) return 0;
else if ((g.sets[0] == true) && (g.sets[1] == false) && (g.sets[2] == false)) return 1;
else if ((g.sets[2] == true) && (g.sets[3] == false)) return 2;
else if ((g.sets[0] == true) && (g.sets[1] == true) && (g.sets[2] == true)) return 3;
else return 99;
}
// should not need locks- called within locked statement
private void resets()
{
if (g.first) { Console.WriteLine("FIRST!!"); g.first = false; }
else Console.WriteLine("Cycle has reset...");
}
private bool getStatus()
{
bool x = false;
Monitor.Enter(myKey);
{
try
{
x = g.gDone;
}
finally
{
Monitor.PulseAll(myKey);
Monitor.Exit(myKey);
}
}
return x;
}
#endregion
#region Constructors
public void paramThreadStarter(object starter)
{
Int16 i = Convert.ToInt16(starter);
setName(i);
do
{
switch (i)
{
default: throw new Exception();
case 0:
changeBool(i);
break;
case 1:
changeInt(i);
break;
case 2:
printString(i);
break;
}
} while (!getStatus());
Console.WriteLine("fin");
Console.ReadLine();
}
#endregion
}
So I have a few questions. The first- is it better to have my global class set like this? Or should I be using a static class with properties and altering them that way? Next question is, when this runs, at random one of the threads will run, pulse/exit the lock, and then step right back in (sometimes like 5-10 times before the next thread picks up the lock). Why does this happen?
Each thread is given a certain amount of CPU time, I doubt that one particular thread is getting more actual CPU time over the others if you are locking all the calls in the same fashion and the thread priorities are the same among the threads.
Regarding how you use your global class, it doesn't really matter. The way you are using it wouldn't change it one way or the other. Your use of globals was to test thread safety, so when multiple threads are trying to change shared properties all that matters is that you enforce thread safety.
Pulse might be a better option knowing that only one thread can actually enter, pulseAll is appropriate when you lock something because you have a task to do, once that task is complete and won't lock the very next time. In your scenario you lock every time so doing a pulseAll is just going to waste cpu because you know that it will be locked for the next request.
Common example of when to use static classes and why you must make them thread safe:
public static class StoreManager
{
private static Dictionary<string,DataStore> _cache = new Dictionary<string,DataStore>(StringComparer.OrdinalIgnoreCase);
private static object _syncRoot = new object();
public static DataStore Get(string storeName)
{
//this method will look for the cached DataStore, if it doesn't
//find it in cache it will load from DB.
//The thread safety issue scenario to imagine is, what if 2 or more requests for
//the same storename come in? You must make sure that only 1 thread goes to the
//the DB and all the rest wait...
//check to see if a DataStore for storeName is in the dictionary
if ( _cache.ContainsKey( storeName) == false )
{
//only threads requesting unknown DataStores enter here...
//now serialize access so only 1 thread at a time can do this...
lock(_syncRoot)
{
if (_cache.ContainsKey(storeName) == false )
{
//only 1 thread will ever create a DataStore for storeName
DataStore ds = DataStoreManager.Get(storeName); //some code here goes to DB and gets a DataStore
_cache.Add(storeName,ds);
}
}
}
return _cache[storeName];
}
}
What's really important to see is that the Get method only single threads the call when there is no DataStore for the storeName.
Double-Check-Lock:
You can see the first lock() happens after an if, so imagine 3 threads simultaneously run the if ( _cache.ContainsKey(storeName) .., now all 3 threads enter the if. Now we lock so that only 1 thread can enter, now we do the same exact if statement, only the very first thread that gets here will actually pass this if statement and get the DataStore. Once the first thread .Add's the DataStore and exits the lock the other 2 threads will fail the second check (double check).
From that point on any request for that storeName will get the cached instance.
So we single threaded our application only in the spots that required it.
Im new with plugin. my problem is, When the case is created, i need to update the case id into ledger. What connect this two is the leadid. in my case i rename lead as outbound call.
this is my code. I dont know whether it is correct or not. Hope you guys can help me with this because it gives me error. I manage to register it. no problem to build and register but when the case is created, it gives me error.
using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Client;
using System.Net;
using System.Web.Services;
/*
* Purpose: 1) To update case number into lejar
*
* Triggered upon CREATE message by record in Case form.
*/
namespace UpdateLejar
{
public class UpdateLejar : IPlugin
{
/*public void printLogFile(String exMessage, String eventMessage, String pluginFile)
{
DateTime date = DateTime.Today;
String fileName = date.ToString("yyyyMdd");
String timestamp = DateTime.Now.ToString();
string path = #"C:\CRM Integration\PLUGIN\UpdateLejar\Log\" + fileName;
//open if file exist, check file..
if (File.Exists(path))
{
//if exist, append
using (StreamWriter sw = File.AppendText(path))
{
sw.Write(timestamp + " ");
sw.WriteLine(pluginFile + eventMessage + " event: " + exMessage);
sw.WriteLine();
}
}
else
{
//if no exist, create new file
using (StreamWriter sw = File.CreateText(path))
{
sw.Write(timestamp + " ");
sw.WriteLine(pluginFile + eventMessage + " event: " + exMessage);
sw.WriteLine();
}
}
}*/
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 service = serviceFactory.CreateOrganizationService(context.UserId);
//for update and create event
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parmameters.
Entity targetEntity = (Entity)context.InputParameters["Target"];
// Verify that the entity represents a connection.
if (targetEntity.LogicalName != "incident")
{
return;
}
else
{
try
{
//triggered upon create message
if (context.MessageName == "Create")
{
Guid recordid = new Guid(context.OutputParameters["incidentid"].ToString());
EntityReference app_inc_id = new EntityReference();
app_inc_id = targetEntity.GetAttributeValue<EntityReference>("new_outboundcalllid");
Entity member = service.Retrieve("new_lejer", ((EntityReference)targetEntity["new_outboundcallid"]).Id, new ColumnSet(true));
//DateTime createdon = targetEntity.GetAttributeValue<DateTime>("createdon");
if (app_inc_id != null)
{
if (targetEntity.Attributes.Contains("new_outboundcallid") == member.Attributes.Contains("new_outboundcalllistid_lejer"))
{
member["new_ringkasanlejarid"] = targetEntity.Attributes["incidentid"].ToString();
service.Update(member);
}
}
}
tracingService.Trace("Lejar updated.");
}
catch (FaultException<OrganizationServiceFault> ex)
{
//printLogFile(ex.Message, context.MessageName, "UpdateLejar plug-in. ");
throw new InvalidPluginExecutionException("An error occurred in UpdateLejar plug-in.", ex);
}
catch (Exception ex)
{
//printLogFile(ex.Message, context.MessageName, "UpdateLejar plug-in. ");
tracingService.Trace("UpdateLejar: {0}", ex.ToString());
throw;
}
}
}
}
}
}
Please check,
is that entity containing the attributes or not.
check it and try:
if (targetEntity.Contains("new_outboundcallid"))
((EntityReference)targetEntity["new_outboundcallid"]).Id
member["new_ringkasanlejarid"] = targetEntity.Attributes["incidentid"].ToString();
What is new_ringkasanlejarid's type? You're setting a string to it. If new_ringkasanlejarid is an entity reference, this might be causing problems.
You might want to share the error details or trace log, all we can do is assume what the problem is at the moment.