I have an issue with string manipulation in c#. Please check the follwoing expression :
public static string UNID = ((Thread.CurrentPrincipal as ClaimsPrincipal).Identity as ClaimsIdentity)
.Claims.Single(c => c.ClaimType.Contains("nameidentifier")).Value.Substring( //issue is here
I want to point the value in the substring function for applying the indexOf function on it. I tried this keyword but not working :
public static string UNID = ((Thread.CurrentPrincipal as ClaimsPrincipal).Identity as ClaimsIdentity)
.Claims.Single(c => c.ClaimType.Contains("nameidentifier")).Value.Substring(this.IndexOf('/') + 1);
I know that we can do the same thing by breaking the expression into parts like :
var value = ((Thread.CurrentPrincipal as ClaimsPrincipal).Identity as ClaimsIdentity)
.Claims.Single(c => c.ClaimType.Contains("nameidentifier")).Value;
var UNID = value.Substring(value.IndexOf('/') + 1);
But if there is any solution for this just like i was trying with this keyword. Then please let me know ?
Personally I think having it as two separate lines is the best way to do this, but if you are dead set on one line you can use Split instead. The second parameter indicates that you only want to split on the first delimiter.
var UNID = ((Thread.CurrentPrincipal as ClaimsPrincipal).Identity as ClaimsIdentity)
.Claims.Single(c => c.ClaimType.Contains("nameidentifier"))
.Value.Split(new[] {'/'}, 2)[1];
This should work:
public static string UNID = ((Thread.CurrentPrincipal as ClaimsPrincipal).Identity as ClaimsIdentity).Claims
.Where(c => c.ClaimType.Contains("nameidentifier"))
.Select(c => c.Value.Substring(c.Value.IndexOf('/')+1))
.Single();
first select the requested claimtype
then convert that to the correct value-substring
and take the only (expected) value
Related
Problem
Background Story: I am rewriting all SQL queries of legacy system into LINQ.
The database is not as clean as I expect. As many of these SQL record contains spaces or different cases which treated as the same.
SELECT *
FROM fruit
WHERE name = #fruitname;
Provided #fruitname is apple, this query will match any record ends with apple, _apple, APPLE_ (where _ is a whitespace character).
However, This is the expected behavior in my use cases.
On the otherhand, LINQ string comparison is more precise. Which annoys me because such issues keep surfacing to me.
Setup
FruitTableAdapter fruitsAdapter = new FruitTableAdapter();
MyGardenDataSet.FruitDataTable fruitsTable = fruitsAdapter.GetData();
Approaches
// Issue 1: Does not match, '_apple' or 'APPLE_'
var fruits1 = fruitsTable.Where(row=>row.name == fruitname);
// Issue 2: String Comparison with case insensitive (does not match 'APPLE')
var fruits2 = fruitsTable.Where(
row=>row.nameEquals(fruitname, StringComparison.OrdinalIgnoreCase));
// Issue 3: Trailing space with case insensitive
var fruits2 = fruitsTable.Where(
row=>row.name.Trim().Equals(fruitname.Trim(),
StringComparison.OrdinalIgnoreCase));
I'm not sure but there could be many issues which SQL query are different from String Comparison.
Is there any SQL aware StringComparison? How can I achieve the same string comparison as SQL in LINQ?
Here's a nice String Extension method that builds on the solutions from a similiar question about casing StackOverflow
Keep in mind, we want to allow for NULL strings in our trim scenarios, so this extension will do a Case Insensitive compare on Trimmed strings after checking for null values
public static class StringExtension
{
// Trim strings and compare values without casing
public static bool SqlCompare(this string source, string value)
{
// Handle nulls before trimming
if (!string.IsNullOrEmpty(source))
source = source.Trim();
if (!string.IsNullOrEmpty(value))
value = value.Trim();
// Compare strings (case insensitive)
return string.Equals(source, value, StringComparison.CurrentCultureIgnoreCase);
}
}
Here's how to use the Extension in your LINQ statement:
(SysUserDisplayFavorites table is composed of char() fields with space filled results. These will get trimmed and compared (case insensitive) to the user provided values in displayFavorite object)
var defaultFavorite = _context.SysUserDisplayFavorites
.Where(x => x.UserId.SqlCompare(displayFavorite.UserId))
.Where(x => x.ModuleCode.SqlCompare(displayFavorite.ModuleCode))
.Where(x => x.ActivityCode.SqlCompare(displayFavorite.ActivityCode))
.Where(x => x.ActivityItemCode.SqlCompare(displayFavorite.ActivityItemCode))
.Where(x => x.IsDefault);
This is a very late answer.
You can use Regex to solve your problem
Here's what I have tried, hope it helps
I created a sample class
public class SampleTable
{
public string Name { get; set; }
public SampleTable(string name)
{
Name = name;
}
}
Populated sample data
List<SampleTable> sampleTblList = new List<SampleTable>();
sampleTblList.Add(new SampleTable(" Apple"));
sampleTblList.Add(new SampleTable(" APPLE"));
sampleTblList.Add(new SampleTable("Apple"));
sampleTblList.Add(new SampleTable("apple"));
sampleTblList.Add(new SampleTable("apple "));
sampleTblList.Add(new SampleTable("apmangple"));
Solution:-
string fruitName = "apple";
List<SampleTable> sortedSampleTblList = sampleTblList.Where(x =>
Regex.IsMatch(fruitName, x.Name, RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase)).ToList();
Output:-
string ans = String.Join(",", sortedSampleTblList.Select(x => x.Name.Replace(" ","_")).ToArray());
Console.Write(ans);
_Apple,_APPLE,Apple,apple,apple_
fruitsTable.Where(row => row.name.Trim().Equals(fruitname, StringComparison.OrdinalIgnoreCase)); should do what you need, but I'm confused because you've listed almost the same under Issue 3. Were you not realising it was working because you are reusing fruits2?
This little NUnit test is passing
[Test]
public void FruitTest()
{
var fruitsTable = new List<string> { " Apple", " APPLE", "Apple", "apple", "apple ", " apple", "APPLE " };
var fruitname = "apple ".Trim();
var fruits = fruitsTable.Where(row => row.Trim().Equals(fruitname, StringComparison.OrdinalIgnoreCase));
Assert.AreEqual(fruitsTable.Count(), fruits.Count());
}
If I want to find the exact match or the next nearest for a string.
Using SQL, I can do :
SELECT TOP 1 *
FROM table
WHERE Code >= #searchcode
ORDER BY Code
How might I achieve this using LINQ and a List of the records.
I was expecting to be able to do something like:
var find = ListDS.Where(c => c.Code >= searchcode).First();
but you can't compare strings that way.
Note that Code is an alpha string, letters, numbers, symbols, whatever..
Nearest means if you have a list containing "England", "France", "Spain", and you search for "France" then you get "France". If you search for "Germany" you get "Spain".
Here is a simple code may help you
List<string> ls = new List<string>();
ls.Add("ddd");
ls.Add("adb");
var vv = from p in ls where p.StartsWith("a") select p;
select all element with starting string "a"
If Code is an int this might work:
var find = ListDS.Where(c => c.Code >= searchcode).OrderBy(c => c.Code).First();
otherwise you need to convert it to one:
int code = int.Parse(searchcode);
var find = ListDS.Where(c => Convert.ToInt32(c.Code) >= code).OrderBy(c => Convert.ToInt32(c.Code)).First();
Try this solution:
class Something
{
public string Code;
public Something(string code)
{
this.Code = code;
}
}
class Program
{
static void Main(string[] args)
{
List<Something> ListDS = new List<Something>();
ListDS.Add(new Something("test1"));
ListDS.Add(new Something("searchword1"));
ListDS.Add(new Something("test2"));
ListDS.Add(new Something("searchword2"));
string searchcode = "searchword";
var find = ListDS.First(x => x.Code.Contains(searchcode));
Console.WriteLine(find.Code);
Console.ReadKey();
}
}
I replaced your >= with .Contains. You can also add the action into First, no need for Where.
It will not find the "nearest", just the first word containg your search parameters.
You could compare string in C#, it will use alphabetically order:
var find = ListDS.Where(c => c.Code.CompareTo(searchcode) >= 0)
.OrderBy(c => c) // get closer one, need to order
.First();
See the CompareTo docs.
Note that with this method, "10" > "2".
I am trying to use SqlFunctions.DatePart with a string which I change depending on parameters, however I can't seem to get it to work.
My code:
String dateArg = #"week";
var results = db.Orders
.GroupBy(x => new
{
Completed = SqlFunctions.DatePart(#dateArg, x.Completed)
})
.Select(x=>x.Sum(y => y.Total))
.ToList();
Fails with the following error:
The DATEPART argument to the 'SqlServer.DATEPART' function must be a literal string.
Well, #dateArg is not a literal string, it's as simple as that. "week" is, though.
If you need to vary the kind of DATEPART to group on, you'll need to split out the cases ("year", "month", "week", I'm guessing those are all that's needed). This is clumsy, but it's a result of the way compiling the expression tree down to a query operates -- just as in T-SQL itself, the first argument to DATEPART cannot be a parameter (they might as well have created separate methods DatePartYear, DatePartMonth etcetera, which would have made the restriction more obvious, and you should think of the calls like that).
I know this is an old question, but maybe this will help someone. Just use an inline if statement as follows:
string datePart = "week";
var results = db.Table.GroupBy(item =>
(datePart == "quarter") ? SqlFunctions.DatePart("quarter", item.Date) :
(datePart == "month") ? SqlFunctions.DatePart("month", item.Date) :
SqlFunctions.DatePart("week", item.Date));
var results = db.Orders
.GroupBy(x => new
{
Completed = SqlFunctions.DatePart("week", x.Completed)
})
.Select(x=>x.Sum(y => y.Total))
.ToList();
I try to filter my items according to unknown number of filters.
//item.statusId is nullable int
//statusIds is a string
{...
var statusIds = Convert.ToString(items["StatusId"]);//.Split(';');
results = mMaMDBEntities.MamConfigurations.Where(item =>
FilterByStatusId(statusIds, item.StatusId)).ToList();
}
return results;
}
private bool FilterByStatusId(string statusIds, int? statusId)
{
return statusIds.Contains(statusId.ToString());
}
But I get this error:
LINQ to Entities does not recognize the method 'Boolean FilterByStatusId(System.String, System.Nullable1[System.Int32])' method, and this method cannot be translated into a store expression.`
Any idea how to re-write it?
If statusIds is an array then you can do:
results = mMaMDBEntities.MamConfigurations
.Where(item => statusIds.Contain(item.StatusID)).ToList();
Some what similar to SQL Select * from table where ID in (1,2,3)
EDIT:
From your code it appears you have a string with semicolon separated values. You can try the following to get an array of int and later use that in your LINQ expression.
var str = Convert.ToString(items["StatusId"]);//.Split(';');
// string str = "1;2;3;4;5"; //similar to this.
int temp;
int[] statusIds = str.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => int.TryParse(r, out temp) ? temp : 0)
.ToArray();
then later you can use the int array in your expression like:
results = mMaMDBEntities.MamConfigurations
.Where(item => statusIds.Contain(item.StatusID)).ToList();
Why not insert the predicate statement directly into the where clause?
like this:
results = mMaMDBEntities.MamConfigurations.Where(item => statusIds.Contains(item.StatusId).ToList();
you might need to convert the string array resulting from the Split to a List or IEnumerable to make this work.
The exception is pretty self-explanatory, the method cannot be converted to a SQL statement in the form you wrote it, but if you write it like above, you should obtain the same result and it will work.
Have a string like A=B&C=D&E=F, how to remove C=D part and get the string like A=B&E=F?
Either just replace it away:
input.Replace("&C=D", "");
or use one of the solutions form your previous question, remove it from the data structure and join it back together.
Using my code:
var input = "A=B&C=D&E=F";
var output = input
.Split(new string[] {"&"}, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Split('=', 2))
.ToDictionary(d => d[0], d => d[1]);
output.Remove("C");
output.Select(kvp => kvp.Key + "=" + kvp.Value)
.Aggregate("", (s, t) => s + t + "&").TrimRight("&");
using System.Web; // for HttpUtility
NameValueCollection values = HttpUtility.ParseQueryString("A=B&C=D&E=F");
values.Remove("C");
values.ToString(); // "A=B&E=F"
I think you need to give a clearer example to make sure it's something for the situation, but something like this should do that:
var testString = "A=B&C=D&E=F"
var stringArray = testString.Split('&');
stringArray.Remove("C=D");
var output = String.Join("&", stringArray);
Something like that should work, and should be pretty dynamic
You can either split() and manually join (depending how the data looks like) or simly use string.Replace(,string.empty)
Split it on the & separator, exclude the C=D part by some mechanism, then join the remaining two? The String class provides the methods you'd need for that, including splitting, joining and substring matching.
string xyz = "A=B&C=D&E=F";
string output = xyz.Replace("&C=D","");
Output: A=B&E=F