Sharepoint search fails when using DataKeynames - sharepoint

We have a Sharepoint site which uses search.
We get the following error:
Unable to validate data. at
System.Web.Configuration.MachineKeySection.EncryptOrDecryptData
(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length,
IVType ivType, Boolean useValidationSymAlgo)
at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
After a bit of testing we have found that the error occurs when we use DateKeyNames on a GridView control.
Not sure why there should be any combination between Search, this error and DataKeyNames.
Any ideas?
Update: Here is some code
[Guid("8891224e-e830-4ffa-afbd-f789583d8d14")]
public class TestErrorGridView : System.Web.UI.WebControls.WebParts.WebPart
{
Control ascxToAdd;
public TestErrorGridView()
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
}
protected override void CreateChildControls()
{
base.CreateChildControls();
Table test = new Table();
TestBindObject t1 = new TestBindObject() { ID = 1, Name = "Test" };
List<TestBindObject> l1 = new List<TestBindObject>();
l1.Add(t1);
SPGridView testGrid = new SPGridView() { AutoGenerateColumns = false};
BoundField header = new BoundField();
header.DataField = "ID";
BoundField name = new BoundField();
name.DataField = "Name";
testGrid.Columns.Add(header);
testGrid.Columns.Add(name);
testGrid.DataSource = l1;
testGrid.DataBind();
// If you comment out this line search works
testGrid.DataKeyNames = new string[] { "ID" };
this.Controls.Add(testGrid);
base.CreateChildControls();
SPGridView testGrid = new SPGridView() { AutoGenerateColumns = false, EnableViewState=false };
testGrid.DataKeyNames = new string[] { "testid" };
this.Controls.Add(testGrid);
}
}
public class TestBindObject
{
public int ID { get; set; }
public string Name { get; set; }
}
UPDATE
This error occurrs on the developer machines, so it is not realated to machine keys being different on different machines in a cluster.
One of the developers has MOSS installed, he does not get the error. The developers who have just WSS installed get the error.
UPDATE 2
Have added datasource to code

I'm going to throw out a guess here since the code where you set the GridView's datasource is missing as well, but here goes...
It probably has something to do with the order in which you are setting the GridView's properties. My guess is that you need to set it in this order:
Create your GridView
Set the GridView's datasource (so that it has data)
Set DataKeyNames
Call GridView.DataBind()
Step 3 and 4 are the steps I am not sure about. You may have to DataBind and then set DataKeyNames.

We eventually solved this by following the example in this link:
http://msdn.microsoft.com/en-us/library/bb466219.aspx
I think that is was binding to a data table rather than a list that made the difference.
The fact that Sharepoint grids do not allow autogenerated columns appears to be one of the factors leading to this problem.

Related

How can I restrict Site ID (Whse) selection to only the current branch?

While it is easy in thought to just limit records to INSite.branchID = AccessInfo.branchID, the need is a bit more complex. I thought that I found a simple solution when looking at the DAC for INTran to find:
#region SiteID
public abstract class siteID : PX.Data.BQL.BqlInt.Field<siteID> { }
protected Int32? _SiteID;
[IN.SiteAvail(typeof(INTran.inventoryID), typeof(INTran.subItemID))]
[PXDefault(typeof(INRegister.siteID))]
[PXForeignReference(typeof(FK.Site))]
[InterBranchRestrictor(typeof(Where<SameOrganizationBranch<INSite.branchID, Current<INRegister.branchID>>>))]
public virtual Int32? SiteID
{
get
{
return this._SiteID;
}
set
{
this._SiteID = value;
}
}
#endregion
which has an intriguing attribute for InterBranchRestrictor. After a little digging, I found this attribute actually is used rather widely in Acumatica, but it appears to be limited to only Report Graphs and enabling the feature for Inter-Branch Transactions. Easy enough, I enabled the feature and tried an Inventory Issue again. No luck. I still could select a site id for a different branch.
So far, I only have limited control by creating a graph extension on INIssueEntry to set and validate the site ID. But what I really want is to limit the selector to only site id's of the current branch.
protected void _(Events.FieldDefaulting<INTran.siteID> e)
{
PXResultset<INSite> Results = PXSelect<INSite, Where<INSite.branchID, Equal<Current<AccessInfo.branchID>>>>.Select(Base);
if (Results.Count == 1)
{
foreach (PXResult<INSite> result in Results)
{
INSite site = result;
e.NewValue = site.SiteID;
e.Cancel = true;
}
}
}
protected void _(Events.FieldVerifying<INTran.siteID> e)
{
int? siteID = (int?)e.NewValue;
INTran row = (INTran)e.Row;
INSite site = PXSelect<INSite, Where<INSite.siteID, Equal<Required<INSite.siteID>>,
And<INSite.branchID, Equal<Current<AccessInfo.branchID>>>>>.Select(Base, siteID);
if(siteID != null && site?.SiteID == null)
{
PXUIFieldAttribute.SetError<INTran.siteID>(e.Cache, row, "Invalid Warehouse for Branch");
}
}
I really want to leverage what it seems like
[InterBranchRestrictor(typeof(Where<SameOrganizationBranch<INSite.branchID, Current<INRegister.branchID>>>))]
does, but clearly I just don't understand what it does. Alternatively, if I could add a where clause to
[IN.SiteAvail(typeof(INTran.inventoryID), typeof(INTran.subItemID))]
then I could restrict the list to the current branch that way, but I'm struggling to make that work as well. (Problems around implementing the PXForeignReference attribute in the extension needed to override the field definition.)
How can I restrict (in a manner that can be replicated efficiently throughout Acumatica) branch specific records to only site ID's of the current branch?
The InterBranchRestrictorAttribute is checking the graph to be working from a Report or the Inter-Branch Transactions feature to be turned on in the IsReportOrInterBranchFeatureEnabled method, so you need to remove this from your implementation.
You can write your own PXResrictorAttribute in the way shown in the example below:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public class CustomRestrictorAttribute: PXRestrictorAttribute
{
public CustomRestrictorAttribute(Type where) : base(CustomRestrictorAttribute.EmptyWhere, "Restrictor Message.", Array.Empty<Type>())
{
this._interBranchWhere = where;
}
protected override BqlCommand WhereAnd(PXCache sender, PXSelectorAttribute selattr, Type Where)
{
return base.WhereAnd(sender, selattr, this._interBranchWhere);
}
private static readonly Type EmptyWhere = typeof(Where<True, Equal<True>>);
protected Type _interBranchWhere;
}
And apply it to the DAC field like below:
[SiteAvail(typeof(SOLine.inventoryID), typeof(SOLine.subItemID))]
[PXParent(typeof(Select<SOOrderSite, Where<SOOrderSite.orderType, Equal<Current<SOLine.orderType>>, And<SOOrderSite.orderNbr, Equal<Current<SOLine.orderNbr>>, And<SOOrderSite.siteID, Equal<Current2<SOLine.siteID>>>>>>), LeaveChildren = true, ParentCreate = true)]
[PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIRequired(typeof(IIf<Where<SOLine.lineType, NotEqual<SOLineType.miscCharge>>, True, False>))]
[InterBranchRestrictor(typeof(Where2<SameOrganizationBranch<INSite.branchID, Current<SOOrder.branchID>>,
Or<Current<SOOrder.behavior>, Equal<SOBehavior.qT>>>))]
[CustomInterBranchRestrictor(typeof(Where<INSite.branchID,Equal<Current<SOOrder.branchID>>>))]
protected virtual void SOLine_SiteID_CacheAttached(PXCache cache)
{
}

VaryByParam fails if a param is a list

I've got this action in MVC
[OutputCache(Duration = 1200, VaryByParam = "*")]
public ActionResult FilterArea( string listType, List<int> designersID, int currPage = 1 )
{
// Code removed
}
that fails to present the correct HTML with url like
http://example.com/en-US/women/clothing?designersID=158
http://example.com/en-US/women/clothing?designersID=158&designersID=13
Is this a know bug of OutputCache in .NET cause cannot recognize VaryByParam with a list param or am I missing something?
I too had the same issue in MVC3 and I believe it's still the same case in MVC5.
Here is the setup I had.
Request
POST, Content-Type:application/json, passing in an array of string as the parameter
{ "options": ["option1", "option2"] }
Controller Method
[OutputCache(Duration = 3600, Location = OutputCacheLocation.Any, VaryByParam = "options")]
public ActionResult GetOptionValues(List<string> options)
I tried every option possible with OutputCache and it just wasn't caching for me. Binding worked fine for the actual method to work. My biggest suspicion was that OutputCache wasn't creating unique cache keys so I even pulled its code out of System.Web.MVC.OutputCache to verify. I've verified that it properly builds unique keys even when a List<string> is passed in. Something else is buggy in there but wasn't worth spending more effort.
OutputCacheAttribute.GetUniqueIdFromActionParameters(filterContext,
OutputCacheAttribute.SplitVaryByParam(this.VaryByParam);
Workaround
I ended up creating my own OutputCache attribute following another SO post. Much easier to use and I can go enjoy the rest of the day.
Controller Method
[MyOutputCache(Duration=3600)]
public ActionResult GetOptionValues(Options options)
Custom Request class
I've inherited from List<string> so I can call the overriden .ToString() method in MyOutputcache class to give me a unique cache key string. This approach alone has resolved similar issues for others but not for me.
[DataContract(Name = "Options", Namespace = "")]
public class Options: List<string>
{
public override string ToString()
{
var optionsString= new StringBuilder();
foreach (var option in this)
{
optionsString.Append(option);
}
return optionsString.ToString();
}
}
Custom OutputCache class
public class MyOutputCache : ActionFilterAttribute
{
private string _cachedKey;
public int Duration { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.Url != null)
{
var path = filterContext.HttpContext.Request.Url.PathAndQuery;
var attributeNames = filterContext.ActionParameters["Options"] as AttributeNames;
if (attributeNames != null) _cachedKey = "MYOUTPUTCACHE:" + path + attributeNames;
}
if (filterContext.HttpContext.Cache[_cachedKey] != null)
{
filterContext.Result = (ActionResult) filterContext.HttpContext.Cache[_cachedKey];
}
else
{
base.OnActionExecuting(filterContext);
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Cache.Add(_cachedKey, filterContext.Result, null,
DateTime.Now.AddSeconds(Duration), System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default, null);
base.OnActionExecuted(filterContext);
}
}

Add new property in subsonic partial class

I added a new column(DateCreated) in a table(Activity) in sqlserver.
and I am using subsonic 2.0, how can I add this column as property in subsonic partial class, so that I can insert and update the value of "DateCreated" Column.
DateCreated will be provided by the user from the GUI.
following is the code I m using but it insert NULL & retrive NULL from the database.
public partial class ActivityInscription
{
public struct Columns
{
public static string IsInMixedList = #"IsInMixedList";
}
public bool? IsInMixedList
{
get;
set;
}
}
Please any one help me to resolve this issue.
If you added the column to the database then just rebuild the DAL. It will pick up the new column and add it to the subsonic DAL. There is no reason for a Partial class in your case. This will work providing that the table "Activity" is part of your subsonic list (includeTableList) of tables in the config file.
Following is the solution, I used and it is working:
public partial class Activity
{
public DateTime? DateCreated
{
get;
set;
}
protected override void BeforeInsert()
{
TableSchema.Table tblSchema = Schema;
TableSchema.TableColumn ccDateCreated = new TableSchema.TableColumn(tblSchema);
ccrDateCreated.ColumnName = "DateCreated";
ccDateCreated.DataType = DbType.DateTime;
ccDateCreated.MaxLength = 0;
ccDateCreated.AutoIncrement = false;
ccDateCreated.IsNullable = false;
ccDateCreated.IsPrimaryKey = false;
ccDateCreated.IsForeignKey = false;
ccDateCreated.IsReadOnly = false;
ccDateCreated.DefaultSetting = #"";
ccDateCreated.ForeignKeyTableName = "";
if (!tblSchema.Columns.Contains("DateCreated"))
{
tblSchema.Columns.Add(ccDateCreated);
}
if (this.GetSchema().Columns.Contains("DateCreated"))
this.SetColumnValue(Columns.DateCreated, DateCreated);
base.BeforeInsert();
}
}
Now it is working fine & inserting the values that I provide from the GUI.

Retrieving values of ReadOnly fields from DynamicData DetailsView in Edit Mode on Updating using LinqDataSource

I have several tables in my database that have read-only fields that get set on Inserting and Updating, namely: AddDate (DateTime), AddUserName (string), LastModDate (DateTime), LastModUserName (string).
All of the tables that have these values have been set to inherit from the following interface:
public interface IUserTrackTable
{
string AddUserName { get; set; }
DateTime AddDate { get; set; }
string LastModUserName { get; set; }
DateTime LastModDate { get; set; }
}
As such, I have the following method on the Edit.aspx page:
protected void DetailsDataSource_Updating(object sender, LinqDataSourceUpdateEventArgs e)
{
IUserTrackTable newObject = e.NewObject as IUserTrackTable;
if (newObject != null)
{
newObject.LastModUserName = User.Identity.Name;
newObject.LastModDate = DateTime.Now;
}
}
However, by the time it hits this method, the e.OriginalObject has already lost the values for all four fields, so a ChangeConflictException gets thrown during the actual Update. I have tried adding the four column names to the DetailsView1.DataKeyNames array in the Init event handler:
protected void Page_Init(object sender, EventArgs e)
{
// other things happen before this
var readOnlyColumns = table.Columns.Where(c => c.Attributes.SingleOrDefaultOfType<ReadOnlyAttribute>(ReadOnlyAttribute.Default).IsReadOnly).Select(c => c.Name);
DetailsView1.DataKeyNames = DetailsView1.DataKeyNames.Union<string>(readOnlyColumns).ToArray<string>();
DetailsView1.RowsGenerator = new CustomFieldGenerator(table, PageTemplates.Edit, false);
// other things happen after this
}
I've tried making that code only happen on PostBack, and still nothing. I'm at a lose for how to get the values for all of the columns to make the round-trip.
The only thing the CustomFieldGenerator is handling the ReadOnlyAttribute, following the details on C# Bits.
UPDATE: After further investigation, the values make the round trip to the DetailsView_ItemUpdating event. All of the values are present in the e.OldValues dictionary. However, they are lost by the time it gets to the LinqDataSource_Updating event.
Obviously, there are the "solutions" of making those columns not participate in Concurrency Checks or other ways that involve hard-coding, but the ideal solution would dynamically add the appropriate information where needed so that this stays as a Dynamic solution.
i Drovani, I assume you want data auditing (see Steve Sheldon's A Method to Handle Audit Fields in LINQ to SQL), I would do this in the model in EF4 you can do it like this:
partial void OnContextCreated()
{
// Register the handler for the SavingChanges event.
this.SavingChanges += new EventHandler(context_SavingChanges);
}
private static void context_SavingChanges(object sender, EventArgs e)
{
// handle auditing
AuditingHelperUtility.ProcessAuditFields(objects.GetObjectStateEntries(EntityState.Added));
AuditingHelperUtility.ProcessAuditFields(objects.GetObjectStateEntries(EntityState.Modified), InsertMode: false);
}
internal static class AuditingHelperUtility
{
internal static void ProcessAuditFields(IEnumerable<Object> list, bool InsertMode = true)
{
foreach (var item in list)
{
IAuditable entity = item as IAuditable;
if (entity != null)
{
if (InsertMode)
{
entity.InsertedBy = GetUserId();
entity.InsertedOn = DateTime.Now;
}
entity.UpdatedBy = GetUserId();
entity.UpdatedOn = DateTime.Now;
}
}
}
}
Sadly this is not possible with EF v1

SharePoint 2007 (wss) search and Unable to validate data Exception

We have a problem with SharePoint's search box. Whenever we try to search for something we get:
Unable to validate data. at
System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean
fEncrypt, Byte[] buf, Byte[] modifier,
Int32 start, Int32 length, IVType
ivType, Boolean useValidationSymAlgo)
at
System.Web.UI.ObjectStateFormatter.Deserialize(String
inputString) exeption.
Does any one know the reason for this exception, or a way to work around it?
New entry:
I'm using a SPGridView where i use the datakeys property in a web part. The webpart works, but we found that using the datakeys property breaks seach in that if you try to use the search textbox and click the seach button it gets this exception:
Unable to validate data. at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo)
at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
This is what i have tryed to do:
Make the gridview not spgridview and set autogenerate true (works)
Remove the datakeynames (works)
Test with a empty gridvew (failes)
Test with a non-empty gridview (failes)
Change Machine Keys (failes)
Turn of view state on the gridvew (failes)
Move the gridview ti a ascx file (failes)
I can't seem to figure this one out. Have enyone got this error and been able to work around it?
EDit 10.09.2009
This is the last code I tested. I used a MSDN excample as referance. I have also tried without Data table MSDN Example
public class TestErrorGridView : System.Web.UI.WebControls.WebParts.WebPart
{
Control ascxToAdd;
protected DataTable PropertyCollection = new DataTable();
private DataColumn key;
public TestErrorGridView()
{
key = PropertyCollection.Columns.Add("ID", typeof(string));
PropertyCollection.Columns.Add("Name", typeof(string));
}
public void AddProperty(TestBindObject data)
{
DataRow newRow = PropertyCollection.Rows.Add();
newRow["ID "] = data.ID;
newRow["Name"] = data.Name;
}
public void BindGrid(SPGridView grid)
{
SPBoundField fldPropertyName = new SPBoundField();
fldPropertyName.HeaderText = "ID";
fldPropertyName.DataField = "ID";
grid.Columns.Add(fldPropertyName);
SPBoundField fldPropertyValue = new SPBoundField();
fldPropertyValue.HeaderText = "Name";
fldPropertyValue.DataField = "Name";
grid.Columns.Add(fldPropertyValue);
PropertyCollection.PrimaryKey = new DataColumn[] { key };
grid.DataSource = PropertyCollection.DefaultView;
grid.DataKeyNames = new string[] { key.ColumnName };
grid.DataBind();
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
}
protected override void CreateChildControls()
{
base.CreateChildControls();
TestBindObject t1 = new TestBindObject() { ID = 1, Name = "Test3" };
this.AddProperty(t1);
SPGridView testGrid = new SPGridView() { AutoGenerateColumns = false };
this.BindGrid(testGrid);
this.Controls.Add(testGrid);
}
}
[Serializable]
public class TestBindObject
{
public int ID { get; set; }
public string Name { get; set; }
}
From the error message I'd say that you are operating in a web-farm environment. Have you set the same machineKey in each of the SharePoint web.config files? See this link for a little more info.

Resources