Using Linq to XML foreach to create muliple XElements - asp.net-mvc-4

I'm building an XML file dynamically using new XElement(), and midway through the file creation I need to iterate over a set of child records and create XElements for them. The problem I'm having is that I need to create more than 1 XElement per iteration. This is a my loop:
from t in trans.SalesTransactionLines
select new XElement("text", new XAttribute("lang", "en"), t.ItemName)
This works fine, but I need an additional 'position' XElement before each 'text' Element. This is the kind of thing I want, which doesn't work:
from t in trans.SalesTransactionLines
select new XElement("position",new XAttribute("x", "40"), new XAttribute("y", "420")),
new XElement("text", new XAttribute("lang", "en"), t.ItemName)
This is the result I'm looking for:
<position x="40" y="420" />
<text>Fender Classic Strat 70s Natural RN</text>
<position x="40" y="420" />
<text>Fender Classic 50s Tele White Blonde</text>

Use method based syntax and SelectMany method instead:
trans.SalesTransactionLines.SelectMany(x => new[] {
new XElement("position",
new XAttribute("x", "40"),
new XAttribute("y", 420")),
new XElement("text",
new XAttribute("lang", "en"),
x.ItemName)
})

Related

Search not working (as expected) for list-form PartyListForm - column loaded from mantle.party.PartyIdentification

I started by using PartyListForm in FindParty.xml. This list loads data related to parties, in my case with Supplier role. I added a new column with an ID from mantle.party.PartyIdentification, with specific partyIdTypeEnumId. The result is satisfactory, I have a list of Suppliers, with their names and respective IDs shown. The problem starts in the moment, when I want to let the user search through those IDs. It does not work. This is the definition of the column:
<field name="idValue">
<header-field title="Company ID" show-order-by="true">
<text-find size="30" hide-options="true"/>
</header-field>
<default-field>
<display text="${partyIdentification?.idValue?:'N/a'}" text-map="partyIdentification"/>
</default-field>
</field>
This is where the data (text-map="partyIdentification") comes from:
<row-actions>
<entity-find-one entity-name="mantle.party.PartyDetail" value-field="party"/>
<entity-find-one entity-name="mantle.party.PartyIdentification" value-field="partyIdentification">
<field-map field-name="partyId" from="partyId"/>
<field-map field-name="partyIdTypeEnumId" value="PtidICO"/>
</entity-find-one>
<entity-find-count entity-name="mantle.party.PartyInvoiceDetail" count-field="invCount">
<econdition field-name="partyId" operator="equals" from="partyId"/>
</entity-find-count>
</row-actions>
This is how it looks on the screen
#David's comment:
There is the original search commented out and my attempt:
<!--<service-call name="mantle.party.PartyServices.find#Party" in-map="context + [leadingWildcard:true, orderByField:'^organizationName', roleTypeId:'Supplier', pageSize:7]" out-map="context"/>-->
<service-call name="mantle.party.PartyServicesEnhancements.findEnhanced#Party" in-map="context + [leadingWildcard:true, orderByField:'^organizationName', roleTypeId:'Supplier', pageSize:7]" out-map="context"/>
I made a few changes by adding new components as a copy of existing ones, namely:
new view-entity with entity-name="FindPartyViewEnhanced" in package="mantle.party as copy of "FindPartyView" with these additions:
<member-entity entity-alias="IDNTF" entity-name="PartyIdentification" join-from-alias="PTY">
<key-map field-name="partyId" related="partyId" />
<entity-condition>
<econdition field-name="partyIdTypeEnumId" operator="equals" value="PtidICO"/>
</entity-condition>
</member-entity>
<alias entity-alias="IDNTF" name="idValue" field="idValue"/>
<alias entity-alias="IDNTF" name="partyIdTypeEnumId" field="partyIdTypeEnumId"/>
new service "findEnhanced" noun="Party" type="script" as a copy of find#Party service with new parameter added:
<parameter name="idValue"/>
new findPartyEnhanced.groovy (copy of findParty.groovy) with a single line added:
if (idValue) { ef.condition(ec.entity.conditionFactory.makeCondition("idValue", EntityCondition.LIKE, (leadingWildcard ? "%" : "") + idValue + "%").ignoreCase()) }
and finally, in the row actions of the screen, where the search is situated, this is what I ended up with:
<service-call name="mantle.party.PartyServicesEnhancements.findEnhanced#Party" in-map="context + [leadingWildcard:true, idValue:idValue, orderByField:'organizationName', roleTypeId:'Supplier', pageSize:7]" out-map="context"/>
Most probably, this is not the best solution, but it worked for me. Hopefully, it will help you as well.

Sitecore 7 Index treelist lucene

I have Sitecore items with a treelist property referring to other items (with different a template).
My goal is to find item A that contains item B in the treelist property using the ContentSearch api (lucene).
I've added the treelist property to my index:
<indexConfigurations>
<defaultLuceneIndexConfiguration type="Sitecore.ContentSearch.LuceneProvider.LuceneIndexConfiguration, Sitecore.ContentSearch.LuceneProvider">
<fieldMap type="Sitecore.ContentSearch.FieldMap, Sitecore.ContentSearch">
<fieldNames hint="raw:AddFieldByFieldName">
<field patch:before="field[0]" fieldName="TreelistProperty" storageType="YES" indexType="UNTOKENIZED" vectorType="NO" boost="1f" type="System.String"
settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
<analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
</field>
</fieldNames>
</fieldMap>
</defaultLuceneIndexConfiguration>
</indexConfigurations>
I would expect that lucene stores the treelist property as a concatenation of guids.
Assuming this is correct and my index is populated my query looks like this:
master = Sitecore.ContentSearch.ContentSearchManager.GetIndex("sitecore_master_index");
using (var context = master.CreateSearchContext())
{
var results = context.GetQueryable<SearchResultItem>()
.Where(x => x["TreelistProperty"].Contains("{456-41414-my-guid-here-1516}"))
.GetResults();
var hits = results.Hits.ToArray();
}
This returns nothing. Where did it go wrong?
You should normalize your guid, like this:
var master = Sitecore.ContentSearch.ContentSearchManager.GetIndex("sitecore_master_index");
using (var context = master.CreateSearchContext())
{
Sitecore.Data.ID myId = ID.Parse("{456-41414-my-guid-here-1516}");
string normalizedID = Sitecore.ContentSearch.Utilities.IdHelper.NormalizeGuid(myId );
var results = context.GetQueryable<SearchResultItem>()
.Where(x => x["TreelistProperty"].Contains(normalizedID))
.GetResults();
var hits = results.Hits.ToArray();
}
I think there is a typo in your index configuration, can you try
indexType="UN_TOKENIZED"
You can also investigate what values are in your lucene indexes using luke
http://www.sitecore.net/en-gb/learn/blogs/technical-blogs/getting-to-know-sitecore/posts/2013/06/using-luke-to-understand-sitecore-7-search.aspx
I think the guid values are stored without the braces and dashes by default. Try converting to ToShortId() before the comparison.

Yii: How to work with translate Yii::t() and hyperlinks

I have many lines similar to this in my code:
echo Yii::t('forms','Would you like to create a new item?');
where I want to hyperlink just around "create a new item", as an example.
Here are some alternatives that I've thought about:
Split the URL into 2 translated strings, surrounded by a hyperlink:
echo Yii::t('forms','Would you like to').' '.Yii::t('forms','create a new item').'?';
Use placeholders, as described in the Yii documentation ( http://www.yiiframework.com/doc/guide/1.1/en/topics.i18n Although hyperlinks aren't given as an explicit example):
echo Yii::t('forms','Would you like to {url}create a new item',array('{url}'=>"<a href='/new_item'>")).'</a>?';
There's probably an easier way to do this, but I've been unable to discover the preferred method...what's the best way to build translated strings that include URLs?
I suggest to you this solution:
echo Yii::t(
'forms',
'Would you like to {link:create}create a new item{/link}?',
array(
'{link:create}'=>'<a href="/new_item">',
'{/link}'=>'</a>',
)
);
The benefit is if you want put id, class, onclick and more anything in a tag you can do it. and so the translate string in clear.
Note that create in {link:create} is just a ideal string that pointer to hyperlink string.
Another advanced sample:
echo Yii::t(
'forms',
'Would you like to {link:create}create a new item{/link}? And you can {link:delete}delete the item{/link}.',
array(
'{link:create}'=>'<a href="/new_item" class="button">',
'{link:delete}'=>'<a href="#" id="item-21" onclick="delete(21);">',
'{/link}'=>'</a>',
)
);
The link may have different placement (beginning, middle or end) and label in the translated string depending on a target language. Therefore, you should use placeholder only for url:
echo Yii::t(
'forms',
'Would you like to create a new item?',
array('{url}' => '/new_item')
);
Use following if you have a dynamic uri:
echo Yii::t(
'forms',
'Would you like to create a new item?',
array(':url'=>'/new_item')
);
Or:
echo Yii::t(
'forms',
'Would you like to create a new item?',
);
Or if you want to pass other dynamic attributes other than the url, use the following:
echo Yii::t(
'forms',
'Would you like to <a :linkAttr>create a new item?</a>',
array('linkAttr'=>'href="/new_item" id="link-id" class="link-class"')
);
I think this is a better solution:
echo Yii::t(
'forms',
'Would you like to {action}?'
[
'action' => Html::a(
Yii::t('forms', 'create a new item'),
['controller/action']
)
]
);
Benefits of this solution
You can use helpers to generate your link
You can modify your html code without modifing the translations
Whoever will be doing translations doesn't need to know anything about html and they can't mess the html code.

Filtering results based on a distinct column or field value

I have an MVC 3 application running against an MS SQL 2008 database with a table named Documents. Documents are broken down by paragraph in the database. The Documents table has a DocText column containing the text of each paragraph, a DocTitle column containing the document title. My MVC 3 app has a search function that can search for a word or phrase in the DocText column or in the DocTitle column. Everything works fine except that if a particular document has the search word appearing in multiple paragraphs, my List returns multiple instances of that document. For example, if the user searches the word "Budget" and one of THE documents has the word "budget" in four different paragraphs, my returned list has that document listed four times.
What I want to achieve is to list each document that has the searched word. I only want to list the document by Title once, regardless of the number of times the search word appears in that document. The only column that is truly unique is the RecordID column, a primary key.
My controller:
public class SearchController : Controller
{
private GUICEEntities4 db = new GUICEEntities4();
//
// GET: /Search/
public ActionResult Index(string Keyword)
{
#region Keyword Search
if (!String.IsNullOrEmpty(Keyword)) {
var SearchDoc = db.Documents.Where(r => r.DocText.ToUpper().Contains(Keyword.ToUpper()) || r.Title.ToUpper().Contains(Keyword.ToUpper()) || r.DocNum.ToUpper().Contains(Keyword.ToUpper()));
ViewBag.CurrentKeyword = String.IsNullOrEmpty(Keyword) ? "" : Keyword;
return View(SearchDoc.ToList());
}
else{
return View();
}
#endregion
}
}
My View has the following:
#foreach (var item in Model) {
<tr>
<td>
<strong>AFI #Html.DisplayFor(modelItem => item.DocNum): #Html.DisplayFor(modelItem => item.Title)</strong>
<br />
#Html.DisplayFor(modelItem => item.DocSummary)
<br />
<span class="complianceitems">Number of compliance items:</span> (TBD)
</td>
<td>
<a href="/Documents/Index/#(Html.DisplayFor(modelItem => item.DocNum))">Checklist
Generator</a><br />
<a href="/UploadedDocs/#Html.DisplayFor(modelItem => item.DocFileName)" target="_blank">
Download PDF</a>
</td>
Any suggestions on how I can achieve my goal?
ADDED: Each document can be identified by the DocNum column which has a unique document number for that particular document. I've tried to iterate through the List to pull out each unqiue DocNum and then try to make that DocNum not appear again in the loop...but I was not successful.
The following SQL statement gives me the results I need. The statement assumes that the search word is "budget". I don't know how to get the same results using EF. Any suggestions?
SELECT DISTINCT DocNum, Title FROM Documents
WHERE
DocText LIKE '%budget%'
OR
Documents.Title LIKE '%budget%'
OR
DocNum LIKE '%budget%'
The issue here is in your EF query and not anything related to MVC. It's been a while since I've actively used EF but the simplest way would probably be to return just the RecordIds first.
var recordIds= db.Documents
.Where(r =>
r.DocText.ToUpper().Contains(Keyword.ToUpper()) ||
r.Title.ToUpper().Contains(Keyword.ToUpper()) ||
r.DocNum.ToUpper().Contains(Keyword.ToUpper()))
.Select(d => d.RecordId)
.Distinct();
I'm not exactly sure what you will do with each individual record from then on as there isn't enough information in your question. But this should help.
Update:
var foundDocs = new List<YourType>();
recordIds.ToList().ForEach(r => foundDocs.Add(db.TblDocLists.Single(l => l.TheDocNum == r)));
//I must point out that I'm not familiar with the latest versions of EF so this might be overkill.

linq to sql/xml - generate xml for linked tables

i have alot of tables with alot of columns and want to generate xml using linq without having to specify
the column names. here's a quick example:
users
---------------
user_id
name
email
user_addresses
---------------
address_id
user_id
city
state
this is the xml i want to generate with linq would look like
<user>
<name>john</name>
<email>john#dlsjkf.com</email>
<address>
<city>charleston</city>
<state>sc</state>
</address>
<address>
<city>charlotte</city>
<state>nc</state>
</address>
</user>
so i'm guessing the code would look something like this:
var userxml = new XElement("user",
from row in dc.Users where user.id == 5
select (what do i put here??)
);
i can do this for one table but can't figure out how to generate the xml for a linked table (like user_addresses).
any ideas?
ok found a way to get the xml i want, but i have to specify the related table names in the query...which is good enough for now i guess. here's the code:
XElement root = new XElement("root",
from row in dc.users
where row.user_id == 5
select new XElement("user",
row.AsXElements(),
new XElement("addresses",
from row2 in dc.user_addresses
where row2.user_id == 5
select new XElement("address", row2.AsXElements())
)
)
);
// used to generate xml tags/elements named after the table column names
public static IEnumerable<XElement> AsXElements(this object source)
{
if (source == null) throw new ArgumentNullException("source");
foreach (System.Reflection.PropertyInfo prop in source.GetType().GetProperties())
{
object value = prop.GetValue(source, null);
if (value != null)
{
bool isColumn = false;
foreach (object obj in prop.GetCustomAttributes(true))
{
System.Data.Linq.Mapping.ColumnAttribute attribute = obj as System.Data.Linq.Mapping.ColumnAttribute;
if (attribute != null)
{
isColumn = true;
break;
}
}
if (isColumn)
{
yield return new XElement(prop.Name, value);
}
}
}
}
You need to use a join. Here's one way:
var query = from user in dc.Users
from addr in dc.UserAddress
where user.Id == addr.UserId
select new XElement("user",
new XElement("name", user.Name),
new XElement("email", user.Email),
new XElement("address",
new XElement("city", addr.City),
new XElement("state", addr.State)));
foreach (var item in query)
Console.WriteLine(item);
i have alot of tables with alot of
columns and want to generate xml using
linq without having to specify the
column names.
Not quite sure how you want to achieve that. You need to state the column names that go into the XML. Even if you were to reflect over the field names, how would you filter the undesired fields out and structure them properly without specifying the column names? For example how would you setup the address part? You could get the fields by using this on your User and UserAddress classes: User.GetType().GetFields() and go through the Name of each field, but then what?