Using an HTML actionlink to sort a list - html

I have a list of possible entries stored in a database. I sort and display these entries into a new view depending on an entry put into a textbox, or from clicking on an html actionlink. The main idea of this can be seen below.
public ActionResult Index(string sortOrder, string searchString, int? sortType)
{
if (!String.IsNullOrEmpty(searchString))
{
if (sortType == 1)
{
//Sort the results strictly based on the HTML header options
applications = applications.Where(s => s.Business.Equals(searchString));
}
else
{
//sort based on the input of the form
applications = applications.Where(s => s.Business.Contains(searchString)
|| s.ApName.Contains(searchString));
}
}
return View(applications.ToList());
}
The first part of this if statement is activated by clicking a link like so,
<li>#Html.ActionLink("CITY", "index", "app", new {SearchString = "city", sortType=1}, null)</li>
Although my code is working, I'm just wondering if there is a more "proper" way of doing what I coded, without having to pass a sortType variable to determine whether to sort based on the form or the actionlink. I'm sure what I have done is quite a bad way of doing it, but I'm very new to this.

I'm not sure if this is what you are after because your question keeps talking about sorting but your code is talking about filtering. You could try to specify default values so the Where statements short-circuit when those criteria are not populated. Ex:
public ActionResult Index(string city = null, string business = null){
var model = applications
.Where(x => city == null || x.City.Contains(city))
.Where(x => business == null || x.Business == business);
return View(model);
}

Related

ASP.NET razor incorrectly works multiselect dropdownlist

There is a drop-down list with a multiselect
<select asp-for="UsersListForAssignTo" id="selectUser" class="form-control" asp-items="#ViewBag.UsersList" multiple="multiple"></select>
I prepare a list, so that some values were selected by default
List<string> allUsers = await db.Users.Select(u => u.UserName).ToListAsync();
List<string> usersAssignedTo = await db.MasterAssigmentTables.Where(mat => mat.TableId == id && mat.TableName == typeof(TestCaseDesign).Name).Include(mat => mat.User).Select(u => u.User.UserName).ToListAsync();
List<SelectListItem> userListAssigned = new List<SelectListItem>();
foreach (string user in allUsers)
{
if (usersAssignedTo.Contains(user))
{
userListAssigned.Add(new SelectListItem { Text = user, Selected = true });
}
else
{
userListAssigned.Add(new SelectListItem { Text = user, Selected = false });
}
}
ViewBag.UsersList = userListAssigned;
The value is passed to the model
public List<string> UsersListForAssignTo { get; set; }
The problem is that when I use "asp-for" all values become unselected
If I remove the binding to the model - then everything is ok
How to get around this problem?
The Selected value of a SelectListItem is meaningless in this context. The only thing that matters is what's in ModelState. That object is composed of values from the request, anything in ViewData (including ViewBag), and finally values on the model passed into the view.
Long and short, if you want some options initially selected, they need to be present in the list you are binding to. Set UsersListForAssignTo to the list of values you want initially selected before returning the view, and you'll be fine.

How to get the document using view in couchbase

I have a requirement wherein I have get the document from couchbase.
Following in the Map function that I am using for the same -
function (doc, meta) {
if (meta.type == "json" && doc!=null) {
emit(doc);
}
}
There is no reduce function. Also following is my java code to get the document -
List<URI> hosts = Arrays.asList(
new URI("http://<some DNS with port>/pools")
);
// Name of the Bucket to connect to
String bucket = "Test-Sessions";
// Password of the bucket (empty) string if none
String password = "";
//System.setProperty("viewmode", "development");
// Connect to the Cluster
CouchbaseClient client = new CouchbaseClient(hosts, bucket, password);
String designDoc = "sessions";
String viewName = "by_test";
View view = client.getView(designDoc, viewName);
Query query = new Query();
query.setIncludeDocs(true);
query.setKey(String.valueOf(122));
ViewResponse result = client.query(view, query);
Object object = null;
for(ViewRow row : result) {
if(null != row) {
object = row.getDocument();
}// deal with the document/data
}
System.out.println("Object" + object);
And the data that I have in couchbase is key - "122" and value - "true". But for some reason , I do not get any rows in the ViewResponse. What is going wrong can anyone help?
I don't understand what you are trying to achieve here, you are using a view to get a document by it's key? Key == 122? Why can't you just do client.get(122) ?
If you just need a list of all the keys in your bucket (of which you can use to pull back all documents via include docs) then make your function like so:
function (doc, meta) {
if (meta.type == "json") {
emit();
}
}
The key of the document is always emitted as an ID (viewRow.getId()). You don't need to emit the document, try to emit as little data as possible to keep view sizes small.
If you are needing to manipulate all the documents in your bucket be careful as the size grows, perhaps you'd need to look at pagination to cycle through the results. http://tugdualgrall.blogspot.com.es/
Also once you have the ViewResponse loop over it like so:
for(ViewRow row : result) {
row.getDocument(); // deal with the document/data
}
You don't need to be doing checks for null on the rows.

Recursive search for sites using Client Object Model using Site Id (GUID)

Using the Client Object Model I am looking for the most efficient way to search a SharePoint server and determine if a specific subsite exists given its unique ID (GUID). We are storing the GUID in our external system because we need to get back to the site and the GUID is the only property that can not change. I know that CAML can be used to search for data within a specific site. However, I haven't been able to find an API that will do this for subsites. I am forced to do a recursive search and use a for loop. In my case, we could have thousands of sites nested on our server.
This logic does one level -- but is not efficient when thousands of subsites exists.
public bool SiteExists(ClientContext context, string myGuid)
{
Web oWebsite = context.Web;
context.Load(oWebsite, website => website.Webs, website => website.Title, website => website.Description, website => website.Id);
context.ExecuteQuery();
for (int i = 0; i != oWebsite.Webs.Count; i++)
{
if (String.Compare(oWebsite.Webs[i].Id.ToString(), myGuid, true) == 0)
{
return true;
}
}
return false;
}
public bool SiteExists(ClientContext context, string myGuid) {
Guid id = new Guid(myGuid);
Site site = context.Site;
Web foundWeb = site.OpenWebById(id);
context.Load(foundWeb);
context.ExecuteQuery();
if(foundWeb != null) {
return true;
}
return false;
}

Entity Framework Code First - Orphaning solutions?

I have searched around for a good amount of time trying to find a satisfactory solution to the orphan issue common in EF.
One of the simplest forms of orphaning is the clearing of a collection of entities. The relationship between the entities is removed but the child entities still remain in the database.
My requirements: -
The clearing of the collection occurs in the domain and I want to simply be able to call clear and no more.
Any logic to figure out whether the relationship between the parent and child has been broken resulting in the delete needs to be encapsulated within the repository / DbContext.
I don't want to have to 'dirty' the domain with anything additional in order to solve this problem. This includes back references.
I suspect that this can't be solved as I have have spent considerable time looking for solutions but I ask out of hope!
Areas I have looked at are the ChangeTracker and any possible events which I can hook into, similar to AssociationChanged event which has popped up in various places. Something, somewhere in the DbContext must know that this relationship has been broken. How to access it, that is the question?
Thanks.
Can you try the following solution? Mb it fits your needs. The DeleteOrphans extension method must be called between DetectChanges and SaveChanges methods.
public static class DbContextExtensions
{
private static readonly ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>> s_navPropMappings = new ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>>();
public static void DeleteOrphans( this DbContext source )
{
var context = ((IObjectContextAdapter)source).ObjectContext;
foreach (var entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
{
var entityType = entry.EntitySet.ElementType as EntityType;
if (entityType == null)
continue;
var navPropMap = s_navPropMappings.GetOrAdd(entityType, CreateNavigationPropertyMap);
var props = entry.GetModifiedProperties().ToArray();
foreach (var prop in props)
{
NavigationProperty navProp;
if (!navPropMap.TryGetValue(prop, out navProp))
continue;
var related = entry.RelationshipManager.GetRelatedEnd(navProp.RelationshipType.FullName, navProp.ToEndMember.Name);
var enumerator = related.GetEnumerator();
if (enumerator.MoveNext() && enumerator.Current != null)
continue;
entry.Delete();
break;
}
}
}
private static ReadOnlyDictionary<string, NavigationProperty> CreateNavigationPropertyMap( EntityType type )
{
var result = type.NavigationProperties
.Where(v => v.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
.Where(v => v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One || (v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne && v.FromEndMember.GetEntityType() == v.ToEndMember.GetEntityType()))
.Select(v => new { NavigationProperty = v, DependentProperties = v.GetDependentProperties().Take(2).ToArray() })
.Where(v => v.DependentProperties.Length == 1)
.ToDictionary(v => v.DependentProperties[0].Name, v => v.NavigationProperty);
return new ReadOnlyDictionary<string, NavigationProperty>(result);
}
}

Local sequence cannot be used in LINQ to SQL implementation

I'm getting an error, see below, when I try to generate a list of the class MappedItem. In short the code example below tries to find products by category, date range and SKU. The requirement I have is that the user should be able to enter a comma separated list of SKUs and the search is to find any product whos SKU starts with one of the SKUs entered by the user. When I run the code, I get.
Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.
The abbreviated sequence is this:
Convert the comma separated string of SKUs into a list of strings.
string sku = TextSKU.Text;
List<string> skuList = sku.Split(new char[] { ',' }).ToList();
Define elsewhere in the code the class that will accept the search results.
public class MappedItem
{
public string ItemDescription { get; set; }
public int ItemCount { get; set; }
public MappedItem()
{
}
public MappedItem(string itemDescription, int itemCount)
{
ItemDescription = itemDescription;
ItemCount = itemCount;
}
}
Here's the query that I generate my results from
List<MappedItem> widgetItems = (from c1 in db.CCRCodes
join pac in db.widgetAssignedCodes on c1.code_id equals pac.code_id
join ph in db.widgetHistories on pac.history_id equals ph.history_id
where ph.contact_dt.Value.Date >= startDate && ph.contact_dt.Value.Date <= endDate &&
(string.IsNullOrEmpty(baanCatFam) || ph.baan_cat_family_code == baanCatFam) &&
(string.IsNullOrEmpty(baanCat) || ph.baan_cat_code == baanCat) &&
(string.IsNullOrEmpty(baanSubCat) || (ph.baan_sub_cat_code == baanSubCat)) &&
(string.IsNullOrEmpty(sku) || skuList.All(sl => ph.product_mod.StartsWith(sl)))
group c1 by c1.code_desc into ct
select new MappedItem
{
ItemDescription = ct.Key.ToUpper(),
ItemCount = ct.Count()
}).OrderByDescending(m => m.ItemCount)
.ToList();
I believe that the culprit is the line of code that I've extracted and displayed below.
skuList.All(sl => ph.product_mod.StartsWith(sl))
This identifies all skus that start with an element from the skuList which is derived from a comma delimited lists of skus entered by the user. My question is, what causes this error, and given the code examples, what do I do to get around them.
First - logically you want Any, not All.
Second, this is a poor way to build up a query filter. All of those operations are sent into the database, while the information to determine which filters should be applied is already local. The explicit joins are also bad (association properties could be used instead).
IQueryable<WidgetHistory> query = db.widgetHistories
.Where(ph => ph.contact_dt.Value.Date >= startDate
&& ph.contact_dt.Value.Date <= endDate);
if (!string.IsNullOrEmpty(baanCatFam))
{
query = query.Where(ph => ph.baan_cat_family_code == baanCatFam);
}
if (!string.IsNullOrEmpty(baanCat))
{
query = query.Where(ph => ph.baan_cat_code == baanCat);
}
if (!string.IsNullOrEmpty(baanSubCat))
{
query = query.Where(ph => ph.baan_sub_cat_code == baanSubCat);
}
//TODO sku filtering here.
List<MappedItem> widgetItems =
from ph in query
let c1 = ph.widgetAssignedCode.CCRCode
group c1 by c1.code_desc into g
select new MappedItem
{
ItemDescription = g.Key.ToUpper(),
ItemCount = g.Count()
}).OrderByDescending(m => m.ItemCount)
.ToList();
Third: the answer to your question.
what causes this error
LinqToSql's query provider cannot translate your local collection into sql. There's only a limitted set of scenarios where it can translate... .Where(ph => idList.Contains(ph.Id)) is translated into an IN clause with 1 parameter per int in idList.
To get around this limitation, you need to convert the local collection into an expression. Start by tranforming each item in the collection into a filtering expression:
List<Expression<Func<WidgetHistory, bool>>> skuFilters =
skuList.Select<string, Expression<Func<WidgetHistory, bool>>>(skuItem =>
ph => ph.ProductMod.StartsWith(skuItem)
).ToList();
Next, a helper method:
public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(
this IEnumerable<Expression<Func<T, bool>>> filters)
{
Expression<Func<T, bool>> firstFilter = filters.FirstOrDefault();
if (firstFilter == null)
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
var body = firstFilter.Body;
var param = firstFilter.Parameters.ToArray();
foreach (var nextFilter in filters.Skip(1))
{
var nextBody = Expression.Invoke(nextFilter, param);
body = Expression.OrElse(body, nextBody);
}
Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
return result;
}
And now putting it all together:
if (skuFilters.Any()) //this part goes into where it says "TODO"
{
Expression<Func<WidgetHistory, bool>> theSkuFilter = skuFilters.OrTheseFiltersTogether()
query = query.Where(theSkuFilter);
}