If else condition in LINQ to XML query in c# - linq

this is xml let say:
<.Sections>
<.SECTION ID ="4" NAME="GetStudents" CONTROL-TYPE="Button" LINK="WebForm2.aspx">
</SECTION>
<.SECTION ID="5" NAME="SelectStudent" CONTROL-TYPE="Drowpdown" METHOD ="selectList_MethodName">
</SECTION>
Observe this xml, I am generating the UI controls base on "CONTROL-TYPE" Attributes. but there are different attributes are there in both sections elements. as LINK and METHOD . I want to query like this , if section's CONTROL-TYPE=="Button" then get value of LINK attribute Else If CONTROL-TYPE=="Drowpdown" then get value of METHOD attribute Else If.
I am trying to write code in c# +ASP.net. how to achieve this? is there way get such data ?
NOTE: please don't go on dot inside tag as <.SECTION>. it is for this forum page understanding.

Well, the simplest way is either an explicit if/else:
string value;
string controlType = (string) element.Attribute("CONTROL-TYPE");
if (controlType == "Button")
{
value = (string) element.Attribute("LINK");
}
else if (controlType == "Dropdown")
{
value = (string) element.Attribute("METHOD");
}
else
{
// What do you want to happen if it's neither of these?
}
... or use the conditional operator if you're happy with a simple default value for other control types:
string controlType = (string) element.Attribute("CONTROL-TYPE");
string value = controlType == "Button" ? (string) element.Attribute("LINK")
: controlType == "Dropdown" ? (string) element.Attribute("METHOD")
: "default value";
EDIT: Within a query expression, there are two reasonable ways to do this. First, you could use the conditional operator and a let clause to fetch the control type just once:
var query =
from element in elements
let controlType = (string) element.Attribute("CONTROL-TYPE")
select new {
ID = (string) element.Attribute("ID"),
XYZ = controlType == "Button" ? (string) element.Attribute("LINK")
: controlType == "Dropdown" ? (string) element.Attribute("METHOD")
: "default value"
};
Alternatively - and preferrably, IMO - put this logic into a method, and then call the method from the select clause:
var query =
from element in elements
let controlType = (string) element.Attribute("CONTROL-TYPE")
select new {
ID = (string) element.Attribute("ID"),
XYZ = GetXyz(element);
};
...
private static void GetXyz(XElement element)
{
...
}

Related

Linq replace null/empty value with another value

In sql server I create views where I can check for null values and replace them with a default if I want (i.e. ISNULL(PrimaryPhone, 'No Primary #') AS PrimaryPhone. I used a lot of views in my asp.net web forms application, but I am now learning MVC 4 and want to start out right.
I have a model, controller and view set up for my client table. I would like to be able to replace null/empty values with ("Not Entered") or something like that.
I believe this needs to go in the controller, please correct me if I'm wrong.
I saw an example that uses hasvalue, but it is not available through intellisense.
How would I replace empty/null values without using DefaultValue in my model?
Thanks
var result = db.Clients.Select(x => new
{
CustomerID = x.CustomerID,
UserID = x.UserID,
FullName = x.FullName,
EmailAdd = x.emailadd.DefaultIfEmpty("No Email"),....
You can use the ?? operator to set a default value when something is null:
var result = db.Clients.Select(x => new
{
CustomerID = x.CustomerID,
UserID = x.UserID,
FullName = x.FullName,
EmailAdd = x.emailadd ?? "No Email", ...
If you need more control, for example checking for both null and empty, you can also use the ?: operator (also known as the "conditional operator" or "ternary operator"):
var result = db.Clients.Select(x => new
{
CustomerID = x.CustomerID,
UserID = x.UserID,
FullName = x.FullName,
EmailAdd = string.IsNullOrEmpty(x.emailadd) ? "No Email" : x.emailadd, ...

Sitecore 6 WFFM: ListField value?

I am building a complex WFFM user control that extends BaseUserControl. This control has multiple fields that get prepopulated based on some business logic. One of the fields is supposed to be a drop down which shows values from a series of Sitecore items. Here is the definition of my ListField property:
private string myListField;
[VisualProperty("My List Field:", 100),
VisualCategory("Appearance"), VisualFieldType(typeof(ListField))]
public string MyListField{
get { return myListField; }
set { myListField= value; }
}
When I debug this, the content of titleFieldList is a string that contains the following XML in URL encoded format:
%3Cquery%20t%3D%22root%22%20vf%3D%22__ID%22%20tf%3D%22Value%22%3E%3Cvalue%3E%7B814FC177-2750-48D6-B7B7-4EE87012C637%7D%3C%2Fvalue%3E%3C%2Fquery%3E
which, decode, is:
<query t="root" vf="__ID" tf="Value">
<value>{814FC177-2750-48D6-B7B7-4EE87012C637}</value>
</query>
I understand the meaning of this XML. It says that all the children of the item whose ID is that Guid are supposed to be used to populate my list, using the template field "__ID" for the value and the template field "value" for the text.
Can someone help me understand what am I supposed to do to bind an asp:DropDownList to this? Is this a particular sitecore object that has been serialized and encoded?
Is there an sc:control that can handle this?
Thanks!
** EDIT **
So I tried the following piece of code
string encodedQuery = TitleFieldList;
string query = HttpUtility.UrlDecode(encodedQuery);
XDocument xmlQuery = XDocument.Parse(query);
if (xmlQuery.Element("query") != null)
{
Dictionary<string, string> nodesDictionary = new Dictionary<string, string>();
string root = xmlQuery.Element("query").Element("value").Value;
string value = xmlQuery.Element("query").Attribute("vf").Value;
string text = xmlQuery.Element("query").Attribute("tf").Value;
Item rootItem = SitecoreUtility.GetItemWithoutSecurity(new ID(root));
ChildList childList = rootItem.GetChildren();
foreach (Item child in childList)
{
string theValue = (value == "__ID") ? child.ID.ToString() : child.Fields[value].ToString();
string theText = child.Fields[text].ToString();
nodesDictionary.Add(theText, theValue);
}
titleDropDownList.DataSource = nodesDictionary;
titleDropDownList.DataTextField = "key";
titleDropDownList.DataValueField = "value";
titleDropDownList.DataBind();
}
and it works. The dropdownlist is populated with the correct data coming from the fields that were selected in the editor. I just can't believe that there is no easier way to do this. Plus how am I supposed to honor the MultipleSelectedValueField and the EmptyChoiceField if present?
Try to change the return type of your property and add attribute TypeConverter. The type specified in TypeConverter is responsible for converting raw string value to a property's return type.
ListItemCollectionConverter - is a converter provided by WFFM
[VisualProperty("My List Field:", 100)]
[VisualCategory("Appearance")]
[VisualFieldType(typeof(ListField))]
[TypeConverter(typeof(Sitecore.Form.Web.UI.Controls.ListItemCollectionConverter.ListItemCollectionConverter))]
public Sitecore.Form.Web.UI.Controls.ListItemCollection MyListField{
get { return myListField; }
set { myListField= value; }
}

Trying to use [Description] data annotation attribute with existing code

SLIGHT UPDATE BELOW
I am trying to use the [Description] data annotation attribute with enums in order to display a friendly name. I've searched around a lot and cannot get anything implemented. Right now I have code that will display an enum as a string (using an extension), but I am not liking ThisIsAnEnum as an enum name (which is spaced out by the string extension) and it prohibits me from having longer names (which I need to maintain) such as for a radio button item. My goal is to have longer descriptions for radio button items without having to write really long enums. An extension/helper will probably be the right way to go, but I need to "fit" it into the code I am using, which is where I failed using the many examples out there.
The code I am using is generic, in that depending upon some logic either a radio button list, check box list, drop down list, select list or regular text boxes are displayed. For multi-item lists enum's are used, and the enum name is what is displayed (after using the string extension).
Here is the particular code that displays the enum:
public static IEnumerable<SelectListItem> GetItemsFromEnum<T>
(T selectedValue = default(T)) where T : struct
{
return from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = name.ProperCase(),
Value = enumValue,
Selected = enumValue.Equals(selectedValue)
};
}
ProperCase is the class that changes the enum to something readable.
I found something that almost worked:
public static string GetEnumDescription<TEnum>(TEnum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if ((attributes != null) && (attributes.Length > 0))
return attributes[0].Description;
else
return value.ToString();
}
in which case I changed code from Text = name.ProperCase(), to Text = name.GetEnumDescription(...) but if I put value in the parenthesis I get a "does not exist in the current context" message (which I tried fixing but just made the problem worse). If I leave it blank I get the "No overload for ... takes 0 arguments" (again, understandable - but I don't know how to fix). And if I put name in the parenthesis the code compiles but upon viewing the page I get the "Object reference not set..." error on this line:
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes
(typeof(DescriptionAttribute), false);
I've spent a lot of time on this and know that my stumbling block is the
Text = name.ProperCase(),
code. Any ideas/help? Thanks in advance.
UPDATE:
If I do:
Text = GetEnumDescription(selectedValue),
I actually DO get the [Description] text, however, it just displays for the first enum. So, if I have 5 enums all with different [Description]'s the code just repeats the [Description] for the first enum 5 times instead of displaying differently for each. I hope that makes sense and gets to narrow down the problem.
I'd recommend you the Display attribute:
public static IEnumerable<SelectListItem> GetItemsFromEnum<T>(T selectedValue = default(T)) where T : struct
{
return
from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = GetEnumDescription(name, typeof(T)),
Value = enumValue,
Selected = name == selectedValue.ToString()
};
}
public static string GetEnumDescription(string value, Type enumType)
{
var fi = enumType.GetField(value.ToString());
var display = fi
.GetCustomAttributes(typeof(DisplayAttribute), false)
.OfType<DisplayAttribute>()
.FirstOrDefault();
if (display != null)
{
return display.Name;
}
return value;
}
and then you could have:
public enum Foo
{
[Display(Name = "value 1")]
Value1,
Value2,
[Display(Name = "value 3")]
Value3
}
And now you could have:
var foo = Foo.Value2;
var values = GetItemsFromEnum(foo);
Also notice that I have modified the Selected clause in the LINQ expression as yours is not correct.
This being said, personally I would recommend you staying away from enums on your view models as they don't play nicely with what's built-in ASP.NET MVC and you will have to reinvent most of the things.

Only primitive types ('such as Int32, String, and Guid') are supported in this context when I try updating my viewmodel

I am having some trouble with a linq query I am trying to write.
I am trying to use the repository pattern without to much luck. Basically I have a list of transactions and a 2nd list which contains the description field that maps against a field in my case StoreItemID
public static IList<TransactionViewModel> All()
{
var result = (IList<TransactionViewModel>)HttpContext.Current.Session["Transactions"];
if (result == null)
{
var rewardTypes = BusinessItemRepository.GetItemTypes(StoreID);
HttpContext.Current.Session["Transactions"] =
result =
(from item in new MyEntities().TransactionEntries
select new TransactionViewModel()
{
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID).ItemDescription,
TransactionDate = item.PurchaseDate.Value,
TransactionAmount = item.TransactionAmount.Value,
}).ToList();
}
return result;
}
public static List<BusinessItemViewModel>GetItemTypes(int storeID)
{
var result = (List<BusinessItemViewModel>)HttpContext.Current.Session["ItemTypes"];
if (result == null)
{
HttpContext.Current.Session["ItemTypes"] = result =
(from items in new MyEntities().StoreItems
where items.IsDeleted == false && items.StoreID == storeID
select new BusinessItemViewModel()
{
ItemDescription = items.Description,
StoreID = items.StoreID,
StoreItemID = items.StoreItemID
}).ToList();
}
return result;
However I get this error
Unable to create a constant value of type 'MyMVC.ViewModels.BusinessItemViewModel'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
I know its this line of code as if I comment it out it works ok
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID).ItemDescription,
How can I map ItemDescription against my list of itemTypes?
Any help would be great :)
This line has a problem:
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID)
.ItemDescription,
Since you are using FirstOrDefault you will get null as default value for a reference type if there is no item that satifies the condition, then you'd get an exception when trying to access ItemDescription - either use First() if there always will be at least one match or check and define a default property value for ItemDescription to use if there is none:
ItemDescription = itemTypes.Any(r=>r.StoreItemID==item.StoreItemID)
? itemTypes.First(r=>r.StoreItemID==item.StoreItemID)
.ItemDescription
: "My Default",
If itemTypes is IEnumerable then it can't be used in your query (which is what the error message is telling you), because the query provider doesn't know what to do with it. So assuming the that itemTypes is based on a table in the same db as TransactionEntities, then you can use a join to achieve the same goal:
using (var entities = new MyEntities())
{
HttpContext.Current.Session["Transactions"] = result =
(from item in new entities.TransactionEntries
join itemType in entities.ItemTypes on item.StoreItemID equals itemType.StoreItemID
select new TransactionViewModel()
{
ItemDescription = itemType.ItemDescription,
TransactionDate = item.PurchaseDate.Value,
TransactionAmount = item.TransactionAmount.Value,
CustomerName = rewards.CardID//TODO: Get customer name
}).ToList();
}
I don't know the structure of your database, but hopefully you get the idea.
I had this error due a nullable integer in my LINQ query.
Adding a check within my query it solved my problem.
query with problem:
var x = entities.MyObjects.FirstOrDefault(s => s.Obj_Id.Equals(y.OBJ_ID));
query with problem solved:
var x = entities.MyObjects.FirstOrDefault(s => s.Obj_Id.HasValue && s.Obj_Id.Value.Equals(y.OBJ_ID));

SelectList Extension - couple expressions

So I got this crazy idea I could make make something cool work. I got tired of new selectlist(item, "blah", "blahblah") so I started writing an extension method (trying to get it more strongly typed) something like this ...
var selectList = projects.ToSelectList(p =>p.ProjectID, p =>p.ProjectName);
the extension method goes a little like this
public static SelectList ToSelectList<T>(this IEnumerable<T> item,
Expression<Func<T, string>> textName,
Expression<Func<T, string>> valueProperty)
{
//do cool stuff
return new SelectList(items, dataTextField, dataValueField);
}
What I need to get to is the reflection properties so I can grab the value and grab the name. Any ideas onhow I can do that? Any thoughts on doing this more better/easier? I've done this before but for the life of me I can't remember how I did it.
Edit this needed some clarification. I copied some code that was in-flight and not refined, so I've updated that code to reflect the more correct criteria.
I don't see why you have to use Reflection to do this.
public static SelectList ToSelectListItem<T>(this IEnumerable<T> items,
Func<T, string> textName, Func<T, string> valueProperty) {
{
return new SelectList(items
.Select(i => new SelectListItem {Text = textName(i), Value = valueProperty(i)}));
}
}
should would work, but I don't have the MVC dll on the current machine.
I originally tried your idea as well when I started with MVC but then I realized that some fields I wanted to format in a certain way and what about when an item is supposed to be selected by default. After handling all of that I realized it would be clearer to just write a one line LAMDA expression.
So now I usually just do a LAMDA expression to generate my SelectListItem collection.
<b>Screener: </b><%= Html.DropDownList("ScreenerOI", Model.Screeners.Select(p=>new SelectListItem() { Text = p.firstName + " " + p.lastName, Value = p.OI.ToString() })) %>
<b>Open Time: </b>
<%= Html.DropDownListFor(c => c.OpenTime, Model.HoursInDay.Select(p => new SelectListItem() { Text = p != null ? DateTime.Now.Date.Add(p.Value).ToString("h:mm tt") : "Clear Time", Value = p != null ? p.ToString() : "", Selected = Model.OpenTime == p }).ToList())%>
It seems like this is just as clear as to what is going on as what your idea is.
Another Example
<b>Screener Status: </b>
<%= Html.DropDownListFor(c => c.InfoStatusOI, Model.InfoStatuses.Select(p => new SelectListItem() { Text = p != null ? p.Status.ToString() : "", Value = p != null ? p.OI.ToString() : "", Selected = p != null && Model.InfoStatusOI == p.OI }).ToList())%>
This needs cleaned and tested for things like a zero count of the items, but here's the end result.
public static SelectList ToSelectList<T>(this IEnumerable<T> items, Expression<Func<T, object >> textName, Expression<Func<T, object >> valueProperty)
{
var dataTextField = textName.Body.ToString().Split('.')[1];
var dataValueField = valueProperty.Body.ToString().Split('.')[1];
return new SelectList(items, dataTextField, dataValueField);
}

Resources