ASP.NET GetFullHtmlFieldId not returning valid id - asp.net-mvc-2

In MVC2 template, I normally use this.ViewData.TemplateInfo.GetFullHtmlFieldId(fieldName)
to generate the id field of an html element. This has worked in most of the cases.
However this method does not actually return valid id field, it merely prefix fieldName with ViewData.TemplateInfo.HtmlFieldPrefix, this is causing problems for me when rendering collections which has [] in the HtmlFieldPrefix.
I have been manually converting those characters to _ where I find the need, but this seems to be not elegant (repeated code), has anyone found a good way to generate id field properly?

Can you be more specific about the kind of problems do you have?
For example, there is an elegant approach to editing variable length list with validation support provided. While it doesn't use templates still remains DRY with partial views.
While the ids are inconsistent - the names are OK and only problem I encountered is that using jquery.infieldlabel it appeared that label's for attribute (generated by GetFullHtmlFieldId inside LabelFor helper) didn't match id of the appropriate TextBoxFor input. So I created LabelForCollectionItem helper method that just uses the same method for id generation as the TextBox - TagBuilder.GenerateId(fullName)
Maybe the code doesn't correspond to your need but hope it will help somebody since I found your question among the first searching for solution to my problem.
public static class LabelExtensions
{
/// <summary>
/// Generates Label with "for" attribute corresponding to the id rendered by input (e.g. TextBoxFor),
/// for the case when input is a collection item (full name contains []).
/// GetFullHtmlFieldId works incorrect inside Html.BeginCollectionItem due to brackets presense.
/// This method copies TextBox's id generation.
/// </summary>
public static MvcHtmlString LabelForCollectionItem<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression,
string labelText = null, object htmlAttributes = null) where TModel : class
{
var tag = new TagBuilder("label");
tag.MergeAttributes(new RouteValueDictionary(htmlAttributes)); // to convert an object into an IDictionary
// set inner text
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string innerText = labelText ?? GetDefaultLabelText(html, expression, htmlFieldName);
if (string.IsNullOrEmpty(innerText))
{
return MvcHtmlString.Empty;
}
tag.SetInnerText(innerText);
// set for attribute
string forId = GenerateTextBoxId(tag, html, htmlFieldName);
tag.Attributes.Add("for", forId);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
/// <summary>
/// Extracted from System.Web.Mvc.Html.InputExtensions
/// </summary>
private static string GenerateTextBoxId<TModel>(TagBuilder tagBuilder, HtmlHelper<TModel> html, string htmlFieldName)
{
string fullName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName);
tagBuilder.GenerateId(fullName);
string forId = tagBuilder.Attributes["id"];
tagBuilder.Attributes.Remove("id");
return forId;
}
/// <summary>
/// Extracted from System.Web.Mvc.Html.LabelExtensions
/// </summary>
private static string GetDefaultLabelText<TModel, TValue>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string htmlFieldName)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
return labelText;
}
}

Related

Constructor not defined error after setting date fields as null during object creation

I have created a wrapper class to create an Object and send it as a request to a third party system. It was working well. But after I added a two new arguments of the Datatype Date, I am getting the below error.
Constructor not defined: [SFDC_DataObject.CustomerAccountObject].<Constructor>(Id, String, Id, String, Id, String, Integer, NULL, String, String, Id, String, NULL, String, String, String, String)
The request that I am creating and sending is as below.
SFDC_DataObject.CustomerAccountObject cusAccObj = new SFDC_DataObject.CustomerAccountObject(o.AccountId, o.Customer_Name__c, o.Agency_Name__r.Id,o.Agency_Name_OB__c, o.Opportunity.OwnerId, o.Opportunity.Owner.FederationIdentifier, PrimarySalesSplitPercent, null, secSOSalesforceId.get(o.OpportunityId), secSOSalesforceEmail.get(o.OpportunityId), o.Opportunity.Customer_Success_Manage__r.Id, o.Opportunity.Customer_Success_Manage__r.FederationIdentifier, null, o.Billing_Email__c, o.Billing_Phone__c, o.Bill_To_Name__c, o.Billing_Notes__c);
My wrapper class for the same object is as below.
public class CustomerAccountObject {
public String sfCustomerId;
public String customerName;
public String sfAgencyId;
public String agencyName;
public String sfPrimarySalesOwnerId;
public String primarySalesOwnerEmail;
public Integer primarySalesOwnerPercentage;
public Date primarySalesOwnerEffectiveFrom;
public String sfSecondarySalesOwnerId;
public String secondarySalesOwnerEmail;
public Date secondarySalesOwnerEffectiveFrom;
public String sfAccountManagerId;
public String accountManagerEmail;
public String billingEmail;
public String billingPhone;
public String billingName;
public String billingNotes;
public CustomerAccountObject() {}
public CustomerAccountObject(String sfCustomerId, String customerName, String sfAgencyId, String agencyName, String sfPrimarySalesOwnerId, String primarySalesOwnerEmail, Integer primarySalesOwnerPercentage, Date primarySalesOwnerEffectiveFrom, String sfSecondarySalesOwnerId, String secondarySalesOwnerEmail, Date secondarySalesOwnerEffectiveFrom, String sfAccountManagerId, String accountManagerEmail, String billingEmail, String billingPhone, String billingName, String billingNotes) {
this.sfCustomerId = sfCustomerId;
this.customerName = customerName;
this.sfAgencyId = sfAgencyId;
this.agencyName = agencyName;
this.sfPrimarySalesOwnerId = sfPrimarySalesOwnerId;
this.primarySalesOwnerEmail = primarySalesOwnerEmail;
this.primarySalesOwnerPercentage = primarySalesOwnerPercentage;
this.primarySalesOwnerEffectiveFrom = primarySalesOwnerEffectiveFrom;
this.sfSecondarySalesOwnerId = sfSecondarySalesOwnerId;
this.secondarySalesOwnerEmail = secondarySalesOwnerEmail;
this.secondarySalesOwnerEffectiveFrom = secondarySalesOwnerEffectiveFrom;
this.sfAccountManagerId = sfAccountManagerId;
this.accountManagerEmail = accountManagerEmail;
this.billingEmail = billingEmail;
this.billingPhone = billingPhone;
this.billingName = billingName;
this.billingNotes = billingNotes;
}
}
I began getting the error after I added the null for the Date arguments I.e primarySalesOwnerEffectiveFrom and secondarySalesOwnerEffectiveFrom during the Object creation.
Can anyone please let me know what am I doing wrong here.
The order is wrong.
In c-tor definition you have
String sfCustomerId, String customerName, String sfAgencyId, String
agencyName, String sfPrimarySalesOwnerId, String
primarySalesOwnerEmail, Integer primarySalesOwnerPercentage, Date
primarySalesOwnerEffectiveFrom, String sfSecondarySalesOwnerId, String
secondarySalesOwnerEmail, Date secondarySalesOwnerEffectiveFrom + 6 more Strings
So
... Integer, Date, String, String, Date, ...
But the code that calls it goes
o.AccountId, o.Customer_Name__c,
o.Agency_Name__r.Id,o.Agency_Name_OB__c, o.Opportunity.OwnerId,
o.Opportunity.Owner.FederationIdentifier, PrimarySalesSplitPercent,
null, secSOSalesforceId.get(o.OpportunityId),
secSOSalesforceEmail.get(o.OpportunityId),
o.Opportunity.Customer_Success_Manage__r.Id,
o.Opportunity.Customer_Success_Manage__r.FederationIdentifier, null, +
4 strings
There are extra 2 strings before 2nd null. And only 4 strings after it. You need to inject that null just after secSOSalesforceEmail?
This will get only worse to maintain as time goes on. Consider making a simple constructor and making the properties public. You could then set them after constructor in normal call. And if you don't need dates you just don't write line that sets date fields instead of injecting null at right position.
Follow-up edit
Not sure if there's an official guide to that technique or a blog post. Tools like Apex-PMD complain when you make methods with too many arguments, rules like "Avoid long parameter lists".
One way would be to do something like this:
SFDC_DataObject.CustomerAccountObject cusAccObj = new SFDC_DataObject.CustomerAccountObject();
cusAccObj.sfCustomerId = o.AccountId;
cusAccObj.customerName = o.Customer_Name__c;
cusAccObj.sfAgencyId = o.Agency_Name__c;
cusAccObj.agencyName = o.Agency_Name_OB__c;
cusAccObj.sfPrimarySalesOwnerId = o.Opportunity.OwnerId;
cusAccObj.primarySalesOwnerEmail = o.Opportunity.Owner?.FederationIdentifier;
cusAccObj.primarySalesOwnerPercentage = PrimarySalesSplitPercent;
// cusAccObj.primarySalesOwnerEffectiveFrom = null; // just don't bother with the line?
cusAccObj.sfSecondarySalesOwnerId = secSOSalesforceId.get(o.OpportunityId);
// ..
That's not very object oriented, not very elegant but caller has full control on the mapping. Problem will be if you need to map new field and this has been copy-pasted into 10 places. You'll have to update them all (which will be easier than adding N-th parameter to long call but still)
Another way would be to create a baseline constructor that takes whole Order object (it's an Order, right?), it'd map the fields internally. Then if needed - you specify some extra fields after constructor. Or maybe make few constructors?
public CustomerAccountObject(){
// I'm parameterless, I'm doing nothing! I'm just here if somebody needs a really custom field mapping or JSON deserialisations need a parameterless one
}
public CustomerAccountObject(Order o){
// I can map all fields from Order! Want to map new field? Just chuck it in here!
sfCustomerId = o.AccountId;
// ...
}
public CustomerAccountObject(Order o, Map<Id, String> secSOSalesforceId, Map<Id, String> secSOSalesforceEmail){
// I can do everything above and few more fields too!
this(o);
sfSecondarySalesOwnerId = secSOSalesforceId.get(o.OpportunityId);
secondarySalesOwnerEmail = secSOSalesforceEmail.get(o.OpportunityId);
}
You have bit of code reuse, the Order fields mapping is defined in just 1 place, just 1 line to change in future. You don't have an orgy of this everywhere anymore. And then your call if you really need the last constructor or you'll call the one that just takes Order o and then set the 2 extra fields after it finishes.

create a new document from word template with multiple pages using documentformat.openxml

I have a Microsoft Word template with some content controls. It contains a table of contents and some extra information.
On the page, I have set some content controls where I would like to inject a new text from a RESTful service. If the RESTful service returns an array of data (objects), I need to replicate the information on each page based on the Word template.
Any idea on how can I do that using the Open XML SDK (DocumentFormat.OpenXml)?
Edit:
I found this post here, which is great, but I don't know how can I apply the array of data to multiple pages from the same template.
So, how can I create multiple pages from the same template in a new document? The data comes in as an array.
The sample code below (which is unit-tested and works) does what you are trying to achieve. It is based on the following interpretation of the question and assumptions:
"Control place holders" means "Rich Text content controls", which are called block-level structured document tags (SDTs) in Open XML lingo and are thus represented by the SdtBlock class in the Open XML SDK.
The content controls have tags, meaning the relevant w:sdt elements have grandchild elements like <w:tag="tagValue" />. Those tags are used to link the data received from the REST service to the content controls.
The data is provided as a Dictionary<string, string>, mapping tag values to the content control text (data).
The general approach is to perform a pure functional transformation of the main document part of your WordprocessingDocument. The void WriteContentControls(WordprocessingDocument) method wraps the outermost pure functional transformation object TransformDocument(OpenXmlElement). The latter uses an inner pure functional transformation object TransformSdtBlock(OpenXmlElement, string).
public class ContentControlWriter
{
private readonly IDictionary<string, string> _contentMap;
/// <summary>
/// Initializes a new ContentControlWriter instance.
/// </summary>
/// <param name="contentMap">The mapping of content control tags to content control texts.
/// </param>
public ContentControlWriter(IDictionary<string, string> contentMap)
{
_contentMap = contentMap;
}
/// <summary>
/// Transforms the given WordprocessingDocument by setting the content
/// of relevant block-level content controls.
/// </summary>
/// <param name="wordDocument">The WordprocessingDocument to be transformed.</param>
public void WriteContentControls(WordprocessingDocument wordDocument)
{
MainDocumentPart part = wordDocument.MainDocumentPart;
part.Document = (Document) TransformDocument(part.Document);
}
private object TransformDocument(OpenXmlElement element)
{
if (element is SdtBlock sdt)
{
string tagValue = GetTagValue(sdt);
if (_contentMap.TryGetValue(tagValue, out string text))
{
return TransformSdtBlock(sdt, text);
}
}
return Transform(element, TransformDocument);
}
private static object TransformSdtBlock(OpenXmlElement element, string text)
{
return element is SdtContentBlock
? new SdtContentBlock(new Paragraph(new Run(new Text(text))))
: Transform(element, e => TransformSdtBlock(e, text));
}
private static string GetTagValue(SdtElement sdt) => sdt
.Descendants<Tag>()
.Select(tag => tag.Val.Value)
.FirstOrDefault();
private static T Transform<T>(T element, Func<OpenXmlElement, object> transformation)
where T : OpenXmlElement
{
var transformedElement = (T) element.CloneNode(false);
transformedElement.Append(element.Elements().Select(e => (OpenXmlElement) transformation(e)));
return transformedElement;
}
}
This should provide enough input for implementing your specific solution even if the details vary (e.g., regarding how you map your array of data to the specific content controls). Further, if you don't use block-level structured document tags (SdtBlock, Rich Text content controls) but but rather inline-level structured document tags (SdtRun, plain text content controls), the principle is the same. Instead of the Paragraph instances (w:p elements) contained in SdtContentBlock instances, you would have Run instances (w:r elements) contained in SdtContentRun instances.
Update 2019-11-23: My CodeSnippets GitHub repository contains the code for the ContentControlWriter and AltChunkAssemblyTests classes. The latter shows how the ContentControlWriter class can be used.

what is the guid-like id associated with a dynamic proxy in Entity Framework?

I'm trying to figure out an exception, and wondering how the id for an Entity object is generated:
System.Data.Entity.DynamicProxies.TaskInstance_EFB25059687D16F3AB6ABF93C582495406916AC2CC28E7E312CB6B50EC3CF7A5.get_TaskLogs()
TaskInstance is the Entity here. It has the same identifier every time the exception happens. Just wondering how it works underneath.
Somehwere in EF's code base concerning proxy type generation, there is this method ...
// <summary>
// Creates an SHA256 hash of a description of all the metadata relevant to the creation of a proxy type
// for this entity type.
// </summary>
private string BuildEntityTypeHash()
{
using (var sha256HashAlgorithm = MetadataHelper.CreateSHA256HashAlgorithm())
{
var hash = sha256HashAlgorithm.ComputeHash(Encoding.ASCII.GetBytes(BuildEntityTypeDescription()));
// convert num bytes to num hex digits
var builder = new StringBuilder(hash.Length * 2);
foreach (var bite in hash)
{
builder.Append(bite.ToString("X2", CultureInfo.InvariantCulture));
}
return builder.ToString();
}
}
... where BuildEntityTypeDescription() is a method that builds a string from the type's navigation properties, key members etc.
So, basically, the proxy type name is a hashed representation of the type's meta data, so it will always be the same string as long as the type doesn't change.

How to specify Contains in UI Automation PropertyCondition

I am using UI Automation for GUI testing.
My window title contains the application name appended by a filename.
So, I want to specify Contains in my Name PropertyCondition.
I checked the overload but it is related to Ignoring the Case of the Name value.
Can anyone let me know how to specify Contains in my Name PropertyCondition?
Regards,
kvk938
I've tried solution of Max Young but could not wait for its completion. Probably my visual tree was too large, not sure. I've decided that it's my app and I should use knowledge of concrete element type I am searching for, in my case it was WPF TextBlock so I've made this:
public AutomationElement FindElementBySubstring(AutomationElement element, ControlType controlType, string searchTerm)
{
AutomationElementCollection textDescendants = element.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, controlType));
foreach (AutomationElement el in textDescendants)
{
if (el.Current.Name.Contains(searchTerm))
return el;
}
return null;
}
sample usage:
AutomationElement textElement = FindElementBySubstring(parentElement, ControlType.Text, "whatever");
and it worked fast.
As far as I know their is no way to do a contains while using the name property but you could do something like this.
/// <summary>
/// Returns the first automation element that is a child of the element you passed in and contains the string you passed in.
/// </summary>
public AutomationElement GetElementByName(AutomationElement aeElement, string sSearchTerm)
{
AutomationElement aeFirstChild = TreeWalker.RawViewWalker.GetFirstChild(aeElement);
AutomationElement aeSibling = null;
while ((aeSibling = TreeWalker.RawViewWalker.GetNextSibling(aeFirstChild)) != null)
{
if (aeSibling.Current.Name.Contains(sSearchTerm))
{
return aeSibling;
}
}
return aeSibling;
}
Then you would do this to get the desktop and pass the desktop with your string into the above method
/// <summary>
/// Finds the automation element for the desktop.
/// </summary>
/// <returns>Returns the automation element for the desktop.</returns>
public AutomationElement GetDesktop()
{
AutomationElement aeDesktop = AutomationElement.RootElement;
return aeDesktop;
}
Complete usage would look something like
AutomationElement oAutomationElement = GetElementByName(GetDesktop(), "Part of my apps name");

Form fields are reset on validation error

I have a rather complex form in the way that the number of form fields is flexibel. In short, the model object is a TLabel (TranslationLabel) that contains a Map of values (translations). Language here is an enum so the idea is that the number of fields (text areas) for which a translation is given depends on the values in this enum.
This is my form (simplified):
public class TranslationEditForm extends Form {
private final static List<Language> LANGUAGES = newArrayList(Language.values());
public TranslationEditForm(String id, final TranslationLabelView label) {
super(id, new CompoundPropertyModel<TranslationLabelView>(label));
ListView<Language> textAreas = new ListView<Language>("translationRepeater", LANGUAGES) {
#Override
protected void populateItem(final ListItem<Language> itemLang) {
//loop through the languages and create 1 textarea per language
itemLang.add(new Label("language", itemLang.getModelObject().toString()));
Model<String> textModel = new Model<String>() {
#Override
public String getObject() {
//return the value for current language
return label.getValue(itemLang.getModelObject());
}
#Override
public void setObject(String object) {
//set the value for current language
label.getTranslations().put(itemLang.getModelObject(), object);
}
};
itemLang.add(new TextArea<String>("value", textModel).setRequired(true));
}
};
//add the repeater containing a textarea per language to the form
this.add(textAreas);
}
}
Now, it works fine, 1 text area is created per language and its value is also set nicely; even more when changed the model gets updated as intended.
If you submit the form after emptying a text area (so originally there was a value) then of course there is a validation error (required). Normal (wicket) behaviour would be that the invalid field is still empty but for some reason the original value is reset and I don't understand why.
If I override onError like this:
#Override
protected void onError() {
this.updateFormComponentModels();
}
then it is fine, the value of the field is set to the submitted value (empty) instead of the original value.
Any idea what is causing this? What is wicket failing to do because the way I've set up the form (because with a simple form/model this is working fine as are the wicket examples)?
Posted as answer, so the question can be marked as solved:
ListView does recreate all its items at render time. This means that the validation will be broken. Have a look at API doc of the ListView
Calling setReuseItems() on the ListView solves this.
Regards,
Bert