I need to override SaveChanges() so that I can update a column (NewClub.LastActivityDate) when certain entities get modified. The problem is that many times the table (NewClub) that has LastActivityDate is not in the entity set being presented to SaveChanges() so I need a way to update NewClub.LastActivityDate if it's in the entity set or not.
Here is what I have so far.
Thanks for looking
public override int SaveChanges()
{
var saveSuccess = false;
var newClubPrimaryKeyId = 0;
ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext;
List<ObjectStateEntry> objectStateEntryList =
ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Unchanged // need this to get to NewClub when it's not the table being updated
| EntityState.Modified
| EntityState.Deleted).ToList();
foreach (ObjectStateEntry entry in objectStateEntryList)
{
//Skip over relationships
if (entry.IsRelationship) continue;
//Make sure the entity is a member of the NCO schema: NewClub[...]
if (SecurityHelper.IsValidNewClubTableName(entry.EntitySet.Name))
{
entityInfoStr += "Entity Name: " + entry.EntitySet.Name + " Entity State: " + entry.State;
var nc = entry.Entity as NewClub;
var ncp = entry.Entity as NewClubProspect;
if (nc != null) { newClubPrimaryKeyId = nc.Id; }
else if (ncp != null){newClubPrimaryKeyId = ncp.NewClubId;}
//... More comparisons here....
//---------------------------------------------------------------
//Update NewClub.LastActivityDate where Id == newClubPrimaryKeyId
//----------------------------------------------------------------
//This does not work
string q = #"UPDATE NewClub SET LastActivityDate=" + DateTime.Now + " WHERE Id=" +
newClubPrimaryKeyId;
var t = ctx.CreateQuery<NewClub>(q);
}
}
try
{
saveSuccess = base.SaveChanges() > 0;
}
catch (Exception e)
{
string ex = e.ToString();
}
return saveSuccess ? 1 : 0;
}
You don't need to manually write a SQL query for this, the entry object gives you access to the current and last state of your object.
Try something like this
foreach( var entry in objectStateEntryList)
{
var lastTime = entry.Property("LastActivityDate");
if(lastTime != null)
{
lastTime.CurrentValue = DateTime.Now;
lastTime.IsModified = true; // be careful with this, throws exception if object is added not modified
}
}
You may also want to check out EFHooks https://github.com/visoft/EFHooks
Related
After inserting the first record, the count shows 0 even after the insert. I can see the the record inserted as soon as SaveContext()
is executed. So looks like userChangeRequestApprovalRepository isnt refreshed with the newly inserted data.
Is it appropriate to do count + 1 like the statement below instead
userChangeRequestApprovalRepository.Where(x => x.UserChangeRequestId == userChangeRequest.Id).Count() + 1;
Code
InsertUserChangeRequestApproval(userChangeRequest);
SaveContext();
var numberOfAprovals = userChangeRequestApprovalRepository.Where(x => x.UserChangeRequestId == userChangeRequest.Id).Count();
insert method
private void InsertUserChangeRequestApproval(UserChangeRequest userChangeRequest)
{
UserChangeRequestApproval userChangeRequestApproval = new UserChangeRequestApproval()
{
UserChangeRequestId = userChangeRequest.Id,
ApprovedByAuthUserId = userChangeRequest.ChangedByAuthUserId,
ApprovedDateTime = DateTime.Now,
IsActive = true
};
UserChangeRequestApprovalRepository.Insert(userChangeRequestApproval);
}
public virtual void Insert(TEntity entity)
{
_dbSet.Add(entity);
}
SaveContext method
public int SaveContext()
{
return _context.SaveChanges();
}
The code for the whole method
public IdentityResult ApproveUserChangeRequest(UserChangeRequest userChangeRequest, int approvedByAuthUserId, string authApplicationName)
{
var userChangeRequestRepository = UserChangeRequestRepository.GetAllAsList();
var userChangeRequestApprovalRepository = UserChangeRequestApprovalRepository.GetAllAsList();
var appSettingRepository = AppSettingRepository.GetAllAsList();
var clientCompanyContactRepository = ClientCompanyContactRepository.GetAllAsList();
var applicationUserRepo = ApplicationUserRepo.GetAllAsList();
// int approvedByAuthUserID = GetApprovedByUserId(authApplicationName, approvedByAuthUserName);
// Check if UserChangeRequest is still Pending
bool isUserChangeRequestPending = userChangeRequestRepository.Any(x => x.Id == userChangeRequest.Id && x.ChangeStatus == "Pending");
if (isUserChangeRequestPending && approvedByAuthUserId > 0)
{
// Inserting record in the UserChangeRequestApproval table
InsertUserChangeRequestApproval(userChangeRequest);
SaveContext();
using (var userTransaction = Context.Database.BeginTransaction())
{
using (var securityTransaction = _securityContext.Database.BeginTransaction())
{
try
{
//Get the Number of approval required for Internal and External Users
int? internalApprovalsRequired = GetApprovals("InternalUserChangeRequestApprovalsRequired", appSettingRepository);
int? externalApprovalsRequired = GetApprovals("ExternalUserChangeRequestApprovalsRequired", appSettingRepository);
//Get the name of the application the auth user belongs to
var authUserApplicationName = GetApplicationName(userChangeRequest.AuthUserId);
//Get the Number of approvals for the request
var numberOfAprovals = userChangeRequestApprovalRepository.Where(x => x.UserChangeRequestId == userChangeRequest.Id).Count();
//If the number of approvals is equal or greater than the Approvals required then Update AppUser or Contact details
if ((authUserApplicationName == "ArgentexTrader" && numberOfAprovals >= internalApprovalsRequired) || (authUserApplicationName == "ArgentexClient" && numberOfAprovals >= externalApprovalsRequired))
{
//Updating the clientcontact table
UpdateClientContact(userChangeRequest, clientCompanyContactRepository);
//Updating the auth user table
UpdateAuthUser(userChangeRequest);
//Updating the IdentityDB user table
UpdateIdentityDBUser(userChangeRequest, applicationUserRepo);
//Updating the UserChangeRequest table
userChangeRequest.ChangeStatus = "Approved";
UserChangeRequestRepository.Update(userChangeRequest);
SaveContext();
userTransaction.Commit();
securityTransaction.Commit();
return IdentityResult.Success;
}
}
catch (Exception ex)
{
userTransaction.Rollback();
securityTransaction.Rollback();
_logger.Error(ex);
return IdentityResult.Failed(new IdentityError { Description = ex.Message });
}
}
}
}
return null;
}
I've spent hours on here looking for an answer to my problem so I'm sorry if this is a duplicate query. If so, please point me in the right direction. If not, see below, I've been tasked with the following:
Create a function that can, dynamically, check the database to see if a value is present for a certain object.
Basically, the user clicks a button which moves an item to another Stage. The next stage may/may not have requirements before being able to enter that stage. If requirements are present, there are two data points sent to me:
ReflectionClass (string) [I will either use the DbSet string value OR the Class string value here]
ReflectionProperty (string)
With these two pieces of data I need to check the database (via DbSet preferably, using LINQ or Reflection).
The ReflectionClass variable can refer to one of 5 tables in the database. So, essentially, I need a way to say: db.[DbSet].Any(w=>w.ID == PipelineID && w.[ReflectionProperty] != null) or something like this...
I've tried using ExpressionTrees but I can't get the Func<T,bool> to work with a Type variable. See my code below, this will work, but I know there is a more efficient way to do this using Reflection, I just don't know how.
public async Task<Feedback> vStageReqFields(Guid NextStageID, Guid PipelineID)
{
Feedback f = new Feedback();
bool Valid = true;
string InvalidList = string.Empty;
List<Domain.Pipeline.Pipeline> pipelines = new List<Domain.Pipeline.Pipeline>();
List<Domain.Pipeline.Billing> billings = new List<Domain.Pipeline.Billing>();
try
{
//Get a list of Validations needed before advancing Stage...
var validations = db.PipelineStageRequiredFields.Any(a=>a.StageID == NextStageID) ? db.PipelineStageRequiredFields.Where(w=>w.StageID == NextStageID).ToList() : null;
//If Validations exist, start validatiing...
if(validations != null && validations.Any())
{
Type objectType = (from asm in AppDomain.CurrentDomain.GetAssemblies()
from type in asm.GetTypes()
where type.IsClass && type.FullName == "Domain.Pipeline.Pipeline" // static class to find Assembly info, all v.ReflectionClasses come from the same Assembly...
select type).SingleOrDefault();
foreach (var v in validations)
{
if(Utility.HasProperty(objectType, v.RefelectionProperty))//Check to see if ReflectionsClass has ReflectionProperty
{
//Switch Statement for Reflection Class to Check Property Value...
switch (v.RefelectionClass)
{
case "Domain.Pipeline.Pipeline":
pipelines = GetAllMembers(db, "Pipelines").OfType<Domain.Pipeline.Pipeline>().Where(w => w.ID == PipelineID).ToList(); //Get all Pipeline Objects...
if (pipelines.Any())
{
var model = pipelines.FirstOrDefault();
var value = model.GetType().GetProperty(v.RefelectionProperty).GetValue(model, null); //Check if Required ReflectionProperty has a value...
if (value == null)
{
Valid = false;
if (string.IsNullOrEmpty(InvalidList))
{
InvalidList = "The following fields are required: " + v.RefelectionProperty;
}
else
{
InvalidList = InvalidList + ", " + v.RefelectionProperty;
}
}
}
else
{
f.Success = false;
f.Type = FeedbackType.Error;
f.SuccessMsg = "Error: Could not find a Pipeline with this ID: '" + PipelineID.ToString() + "'";
}
break;
case "Domain.Pipeline.Billing":
billings = GetAllMembers(db, "Billings").OfType<Domain.Pipeline.Billing>().Where(w => w.PipelineID == PipelineID).OrderByDescending(o => o.EffectiveDate).ToList();
if (billings.Any())
{
var model = billings.FirstOrDefault();
var value = model.GetType().GetProperty(v.RefelectionProperty).GetValue(model, null);
if (value == null)
{
Valid = false;
if (string.IsNullOrEmpty(InvalidList))
{
InvalidList = "The following fields are required: " + v.RefelectionProperty;
}
else
{
InvalidList = InvalidList + ", " + v.RefelectionProperty;
}
}
}
else
{
f.Success = false;
f.Type = FeedbackType.Error;
f.SuccessMsg = "Error: Could not find a Pipeline with this ID: '" + PipelineID.ToString() + "'";
}
break;
default:
f.Success = false;
f.Type = FeedbackType.Error;
f.SuccessMsg = "Error: Could not find any Data in the " + v.RefelectionClass + " table for this Pipeline: '" + PipelineID.ToString() + "'";
break;
}
}
else
{
f.Success = false;
f.Type = FeedbackType.Error;
f.SuccessMsg = "The " + v.RefelectionClass + " does not have a Property of " + v.RefelectionProperty;
}
}
}
//No Validations Exist, User can proceed...
else
{
f.Success = true;
f.Type = FeedbackType.Success;
f.SuccessMsg = "Success! There are no required fields for the next stage.";
}
}
catch(Exception ex)
{
f.Success = false;
f.Type = FeedbackType.Error;
f.SuccessMsg = ITool.GetExceptionDetails(ex);
}
return f;
}
Here is the other function:
static IEnumerable GetAllMembers(DbContext DB, string dbSetName)
{
var model = DB.GetType().GetProperty(dbSetName);
return (IEnumerable)model.GetValue(DB);
}
Consider the following refactoring to remove repeated code and allow it to be more DRY (Don't Repeat Yourself).
public Feedback vStageReqFields(Guid NextStageID, Guid PipelineID) {
Feedback feedback = new Feedback();
List<string> required = new List<string>();
List<string> errors = new List<string>();
StringBuilder msg = new StringBuilder();
try {
//Get a list of Validations needed before advancing Stage...
var validations = db.PipelineStageRequiredFields.Where(w => w.StageID == NextStageID);
//If Validations exist, start validatiing...
if (validations.Any()) {
foreach (var field in validations) {
var className = field.RefelectionClass;
object model = null;
//Switch Statement for Reflection Class to Check Property Value...
switch (className) {
case "Domain.Pipeline.Pipeline":
model = db.Pipelines.FirstOrDefault(p => p.ID == PipelineID);
break;
case "Domain.Pipeline.Billing":
model = db.Billings.Where(w => w.PipelineID == PipelineID).OrderByDescending(o => o.EffectiveDate).FirstOrDefault();
break;
default:
errors.Add("Could not find any Data in the " + className + " table for this Pipeline: '" + PipelineID.ToString() + "'");
continue;
}
validate(field, PipelineID, model, required, errors);
}
if (required.Any()) {
msg.Append("The following fields are required: ")
.AppendLine(string.Join(", ", required));
}
if (errors.Any()) {
msg.AppendLine(string.Join(Environment.NewLine, errors));
}
}
if (msg.Length > 0) {
feedback.Success = false;
feedback.Type = FeedbackType.Error;
feedback.SuccessMsg = msg.ToString();
} else {
feedback.Success = true;
feedback.Type = FeedbackType.Success;
feedback.SuccessMsg = "Success! There are no required fields for the next stage.";
}
} catch (Exception ex) {
feedback.Success = false;
feedback.Type = FeedbackType.Error;
feedback.SuccessMsg = ITool.GetExceptionDetails(ex);
}
return feedback;
}
Use strong typing to find the desired object model.
The supporting validate method looks like this
private void validate(PipelineStageRequiredField field, Guid PipelineID, object model, List<string> required, List<string> errors) {
if (model != null) {
var propertyName = field.RefelectionProperty;
var objectType = model.GetType();
var propertyInfo = getProperty(objectType, propertyName);
if (propertyInfo != null) {
var isValidModel = buildRequiredFieldCheckDelegate(objectType, propertyInfo);
if (!(bool)isValidModel.DynamicInvoke(model)) {
required.Add(propertyInfo.Name);
}
} else {
errors.Add("The " + field.RefelectionClass + " does not have a Property of " + propertyName);
}
} else {
errors.Add("Error: Could not find a " + field.RefelectionClass + " with this Pipeline: '" + PipelineID.ToString() + "'");
}
}
private static PropertyInfo getProperty(Type type, string propertyName) {
return type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
}
Where an expression can be built to check that the required field has a value
private Delegate buildRequiredFieldCheckDelegate(Type type, PropertyInfo propertyInfo) {
//Func<T, bool>
var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(bool));
var prpertyDefaultValue = getDefaultValue(propertyInfo.PropertyType);
// p => p.Property != default(typeof(TProperty))
// p =>
var parameter = Expression.Parameter(type, "p");
// p => p.Property
var property = Expression.Property(parameter, propertyInfo);
// default(TProperty);
var defaultValue = Expression.Constant(prpertyDefaultValue);
// p => p.Property != default(TProperty)
var body = Expression.NotEqual(property, defaultValue);
// Func<T, bool> = T p => p.Property != default(TProperty)
var lambda = Expression.Lambda(delegateType, body, parameter);
return lambda.Compile();
}
private static object getDefaultValue(Type type) {
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
This question already has answers here:
DbUpdateException: Which field is causing "String or binary data would be truncated"
(4 answers)
Closed 12 months ago.
How do you determine which column is the culprit when you have 80(+/-) columns to choose from? Using .Net Core (netcoreapp2.2) and EF Core 2.2.4.
Picked up some existing code and there was an attempt to track columns that failed. However, it does not work. I've looked at dozens of examples here and elsewhere and have not found a way to do this in EF Core 2.x.
public int GetColumnMaxLength(string table, EntityEntry entityEntry)
{
// Just a rough to get the right data - always returns 0 for the moment...
int result = 0;
var modelContext = entityEntry.Context;
var entityType = modelContext.Model.FindEntityType(table); // THIS IS ALWAYS NULL!
if (entityType != null)
{
// Table info
var tableName = entityType.Relational().TableName;
var tableSchema = entityType.Relational().Schema;
// Column info
foreach (var property in entityType.GetProperties())
{
var columnName = property.Relational().ColumnName;
var columnType = property.Relational().ColumnType;
var isFixedLength = property.Relational().IsFixedLength;
};
}
return result;
}
The above code is being called by this catch portion of a try/catch around the db.SaveAsync(); statement.
catch (Exception ex)
{
// -----------------------------------------
// no idea what this was really trying to
// do as it barfs out all columns...
// -----------------------------------------
var dataInfo = new DataInfo();
var strLargeValues = new List<Tuple<int, string, string, string>>();
foreach (var entityEntry in _db.ChangeTracker.Entries().Where(et => et.State != EntityState.Unchanged))
{
// -----------------------------------------
// try to get the column info for all
// columns on this table...
// -----------------------------------------
dataInfo.GetColumnMaxLength("Subscription", entityEntry);
foreach (var entry in entityEntry.CurrentValues.Properties)
{
var value = entry.PropertyInfo.GetValue(entityEntry.Entity);
if (value is string s)
{
strLargeValues.Add(Tuple.Create(s.Length, s, entry.Name, entityEntry.Entity.GetType().Name));
}
}
}
var l = strLargeValues.OrderByDescending(v => v.Item1).ToArray();
foreach (var x in l.Take(100))
{
Trace.WriteLine(x.Item4 + " - " + x.Item3 + " - " + x.Item1 + ": " + x.Item2);
}
throw;
}
So, the crux of the question is: How do I get the SQL column definition from EF Core?
I want to be able to log the specific table and column when incomingData.Length > targetColumnDefinition.Length
FINAL SOLUTION:
public override int SaveChanges()
{
using (LogContext.PushProperty("DbContext:Override:Save", nameof(SaveChanges)))
{
try
{
return base.SaveChanges();
}
catch (Exception ex)
{
var errorMessage = String.Empty;
var token = Environment.NewLine;
foreach (var entityEntry in this.ChangeTracker.Entries().Where(et => et.State != EntityState.Unchanged))
{
foreach (var entry in entityEntry.CurrentValues.Properties)
{
var result = entityEntry.GetDatabaseDefinition(entry.Name);
var value = entry.PropertyInfo.GetValue(entityEntry.Entity);
if (result.IsFixedLength && value.ToLength() > result.MaxLength)
{
errorMessage = $"{errorMessage}{token}ERROR!! <<< {result.TableName}.{result.ColumnName} {result.ColumnType.ToUpper()} :: {entry.Name}({value.ToLength()}) = {value} >>>";
Log.Warning("Cannot save data to SQL column {TableName}.{ColumnName}! Max length is {LengthTarget} and you are trying to save something that is {LengthSource}. Column definition is {ColumnType}"
, result.TableName
, result.ColumnName
, result.MaxLength
, value.ToLength()
, result.ColumnType);
}
}
}
throw new Exception(errorMessage, ex);
}
}
}
On .NET Core 3.1 and EFCore 5.0.2 this logging works with no additional extension methods needed:
try
{
await context.SaveChangesAsync();
}
catch(Exception ex)
{
foreach (var entityEntry in context.ChangeTracker.Entries().Where(et => et.State != EntityState.Unchanged))
{
foreach (var entry in entityEntry.CurrentValues.Properties)
{
var prop = entityEntry.Property(entry.Name).Metadata;
var value = entry.PropertyInfo?.GetValue(entityEntry.Entity);
var valueLength = value?.ToString()?.Length;
var typemapping = prop.GetTypeMapping();
var typeSize = ((Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping) typemapping).Size;
if (typeSize.HasValue && valueLength > typeSize.Value)
{
Log.Error( $"Truncation will occur: {entityEntry.Metadata.GetTableName()}.{prop.GetColumnName()} {prop.GetColumnType()} :: {entry.Name}({valueLength}) = {value}");
}
}
}
throw ex;
}
As mentioned in the comments, you need the full name and this can be read from the metadata.
public int GetColumnMaxLength(EntityEntry entityEntry)
{
int result = 0;
var table = entityEntry.Metadata.Model.FindEntityType(entityEntry.Metadata.ClrType);
// Column info
foreach (var property in table.GetProperties())
{
var maxLength = property.GetMaxLength();
// For sql info, e.g. ColumnType = nvarchar(255):
var sqlInfo = property.SqlServer();
};
return result;
}
Context is not saving to the database no matter what i do it will insert a new record fine but not save. This is using sql server and the user had permissions ot update data have already checked this
private void btnOk_Click(object sender, EventArgs e)
{
SourceContext SourceDal = new SourceContext();
Appointment _appointment = new Appointment();
int errorCount = 0;
Patient _patient = new Patient();
_patient = SourceDal.getPatientByPatientId(txtPatientId.Text);
_patient.SSN = txtSSN.Text;
_patient.FirstName = txtPatientFirstName.Text;
_patient.LastName = txtPatientLastName.Text;
_patient.Middle = txtPatientMiddle.Text;
_patient.AddressOne = txtPatientAddressOne.Text;
_patient.City = txtPatientCity.Text;
_patient.State = txtPatientState.Text;
_patient.ZipCode = txtPatientZip.Text;
_patient.HomePhone = txtPatientHomePhone.Text;
_patient.WorkPhone = txtPatientWorkPhone.Text;
_patient.CellPhone = txtPatientCellPhone.Text;
if (rBtnHomePhone.Checked == true)
_patient.ApptPhone = txtPatientHomePhone.Text;
if (rBtnHomePhone.Checked == true)
_patient.ApptPhone = txtPatientHomePhone.Text;
if (rBtnWorkPhone.Checked == true)
_patient.ApptPhone = txtPatientWorkPhone.Text;
_patient.BirthDate = dtBirthDate.DateTime;
_patient.emailAddress = txtPatientEmail.Text;
_patient.Race = (int)dpRace.SelectedValue;
_patient.Ethnicity = (int)dpEthnicity.SelectedValue;
_patient.Language = (int)dpLanguages.SelectedValue;
_patient.AlertNote = txtPatientNotes.Text;
if (dpGender.Text == "")
{
dpGender.Focus();
errorCount = 1;
lblGenderRequired.Text = "* Gender is required.";
}
else
{
errorCount = 0;
lblGenderRequired.Visible = false;
}
_patient.Gender = dpGender.Text.Substring(0, 1);
_patient.PatientID = txtPatientId.Text;
txtPatientFirstName.Text = _patient.FirstName;
txtPatientLastName.Text = _patient.LastName;
// IF ITS SAVE NEW GO AHEAD ADD IT TO THE CONTEXT.
SourceDal.AddToPatient(_patient);
}
Add to paitent has the following
public void AddToPatient(Patient newPatient)
{
using (var myContext = new SMBASchedulerEntities(this.Connectionstring))
{
myContext.Patients.Add(newPatient);
if (newPatient.ID == 0)
{
myContext.Entry(newPatient).State = EntityState.Added;
}
else
{
myContext.Entry(newPatient).State = EntityState.Modified;
}
try
{
myContext.SaveChanges();
}
catch (DbEntityValidationException ex)
{
foreach (var entityValidationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in entityValidationErrors.ValidationErrors)
{
Console.Write("Property: " + validationError.PropertyName + " Error: " + validationError.ErrorMessage);
}
}
}
}
}
It adds in the record fine but it just wont save the current record no matter what i do even though all the details are correct. But when i reload the form and the application the update is not there the email address is not saved no are any the other updates.
I suspect I'm not familiar with that entity framework, as I'm unfamiliar with the some of that syntax, but you should be able to use something like this:
public void AddToPatient(Patient newPatient)
{
SMBASchedulerEntities dbContext = new SMBASchedulerEntities();
if (newPatient.ID.ToString() != "0")
{//Update the record
Patient updatePatient = dbContext.Patients.Single(p => p.ID == newPatient.ID);
updatePatient.FirstName = newPatient.FirstName;
updatePatient.LastName = newPatient.LastName;
...
...
dbContext.SubmitChanges();
}
else
{//Insert a new record
Patient insertPatient = new Patient();
insertPatient.FirstName = newPatient.FirstName;
insertPatient.LastName = newPatient.LastName;
...
...
dbContext.Patients.InsertOnSubmit(insertPatient);
dbContext.SubmitChanges();
}
}
To put this another way, check to see if you need to insert or update a new patient first, before inserting it every time.
I have the following code which creates a Task in Salesforce and then tracks a user's browsing history and stores it in SalesForce. Currently, it displays each and every page the user has browsed as an individual entry. I want to group all those entries together in the Browsing_History__c object instead of task being created every time a user visits a page.
Any help would be appreciated..I am not familiar with SF very much. :)
private void CreateTaskInSF(string id, string type, string details, string description)
{
// if there's a similar Event in the past 2 hours, don't add it
QueryResult qr = null;
try // get events from past 2 hours
{
qr = Binding.query("Select Details__c from Task WHERE WhoId='" + id + "' and Type__c='" + type + "' and CreatedDate > " + DateTime.UtcNow.AddHours(-2).ToString("s") + "Z");
}
catch (Exception e)
{
return;
}
bool logged = false;
if (qr != null) // if there are Tasks in past 2 hours
{
sforce.sObject[] browsing = qr.records;
if (browsing != null)
{
// iterate through events to make sure the new Task isn't logged
for (int i = 0; i < browsing.Length; i++)
{
Task currTask = (Task)browsing[i];
if (currTask.Details__c == details)
{
if (description != "") // is there a description to check for?
{
string oldTaskDescription = "";
if (currTask.Description != null)
oldTaskDescription = currTask.Description;
if (oldTaskDescription == description) // if there is a description match
logged = true;
}
else
logged = true; // there's no description, so check only on details field
}
}
}
}
if (logged == true)
{
return; // if Activity is already logged, don't log it again
}
else if (type == "Browsing")
{
QueryResult browsingQuery = null;
try // get events from past 2 hours
{
browsingQuery = Binding.query("Select Web_Browsing__c from Task WHERE WhoId='" + id + "' and Subject='" + type + "' and Details__c='" + details + "' and CreatedDate > " + DateTime.UtcNow.AddHours(-2).ToString("s") + "Z");
}
catch
{
}
Boolean createNewBrowsing = false;
if (browsingQuery != null) // if there are Tasks in past 2 hours
{
sforce.sObject[] webBrowsing = browsingQuery.records;
if (webBrowsing != null)
{
//find correct object and update Browsing_History__c
//Binding.update
}
else
{
createNewBrowsing = true;
}
}
else
{
createNewBrowsing = true;
}
if (createNewBrowsing)
{
Web_Browsing__c newTask = new Web_Browsing__c();
newTask.Lead__c = id;
newTask.Browsing_History_255__c = details;
newTask.Type__c = type;
newTask.Browsing_History__c = details;
newTask.CreatedDate = DateTime.Now;
//if(type == "Browsing") newTask. = details;
//SaveResult[] createResult = Binding.create(new sObject[] { newTask });
try
{
SaveResult[] createResult = Binding.create(new sObject[] { newTask });
}
catch (Exception e)
{
return;
}
}
}
else
{
// if this new Activity isn't logged, then create a new Activity Task
sforce.Task newTask = new sforce.Task();
newTask.WhoId = id;
newTask.Subject = type;
newTask.Details__c = details;
if (description != "") newTask.Description = description;
newTask.Status = "Completed";
newTask.Priority = "Normal";
newTask.ActivityDate = DateTime.Now;
newTask.ActivityDateSpecified = true;
// insert it
try
{
SaveResult[] createResult = Binding.create(new sforce.sObject[] { newTask });
}
catch (Exception e)
{
return;
}
}
}
You'll need to update your query to ask for the browsing history object and update the code to create a browsing history object instead of a task.
If you haven't already, review the Web Services API docs, it has examples for querying and creating in java/c#.