LINQ Substring CRM 2011 - linq

I have a LINQ query that pulls down some information based on certain criteria in the Where clause from CRM 2011. But I want to add it so it only looks at the first 5 characters of the Zip instead of the whole Zip. I tried adding SubString. But if the value is null it will fail. How would you go about matching the first 5 characters of the Zip in the Where clause. This is my query.
var lQuery = (from a in gServiceContext.CreateQuery("account")
where (a["name"].Equals(lLead.AccountName) &&
a["address1_postalcode"].Equals(lLead.ZipCode) &&
a["address1_stateorprovince"].Equals(lLead.State)) ||
(a["address1_line1"].Equals(lLead.Address1) &&
a["address1_postalcode"].Equals(lLead.ZipCode) &&
a["address1_city"].Equals(lLead.City))
select new
{
Name = !a.Contains("name") ? string.Empty : a["name"],
City = !a.Contains("address1_city") ? string.Empty : a["address1_city"],
State = !a.Contains("address1_stateorprovince") ? string.Empty : a["address1_stateorprovince"],
Zip = !a.Contains("address1_postalcode") ? string.Empty : a["address1_postalcode"],
AccountId = !a.Contains("accountid") ? string.Empty : a["accountid"]
})
Thanks!

One of the other issues with String.Substring is that it throws if the string isn't long enough, i.e. you try to get the first 5 characters of a 4 character string. You can use a simple helper method like this:
where First(a["address1_postalcode"], 5) == lLead.ZipCode
(You might need to convert or cast a["address1_postalcode"] to string depending on what type it returns.)
public static string First(string s, int charcount)
{
if (s == null) return String.Empty;
return s.Substring(0, Math.Min(s.Length, charcount));
}

Related

Func<T, TResult> with OrderBy resulting in error with null fields

I've inherited some code, and also a bug.
The app is a C# MVC website using EF. The VIEW presents clients in a table (taken straight from a CLIENTS table in a SQL Server database).
List<Client> _clients = db.Client.ToList();
IEnumerable<Client> filteredClients;
The problem arises when the user clicks a header to sort by. The args passed to the controller indicate an index of the field to sort by (sortColumnIndex).
The original dev created a func to handle the translation from the index to the field.
Func<Client, string> orderingFunction = (c => sortColumnIndex == 1 ? c.Name.ToString()
: sortColumnIndex == 2 ? c.AccountExec.ToString()
: sortColumnIndex == 3 ? c.SalesforceLink.ToString()
: sortColumnIndex == 4 ? c.Location.ToString()
: sortColumnIndex == 5 ? c.PrimaryContact.ToString()
: sortColumnIndex == 6 ? c.AccountId.ToString()
: sortColumnIndex == 6 ? c.MongoClientId.ToString()
: ""); // index 0 is the hidden ClientId
The results of this are used in the OrderBy() clause.
filteredClients = filteredClients.OrderBy(orderingFunction);
When the field being sorted on has complete data (i.e. no NULL values), it works fine. As soon as a column has a NULL value, however, the resulting OrderBy throws a "Object reference not set to an instance of an object" error.
I'm afraid I'm not completely up to the task of deciphering the solution here; we still need to sort on the field selected by the user, even if all but one of the records have a NULL value. Is there any way to achieve this with the existing code structure, or is this better served by refactoring?
EDIT: full code up to the point of exception:
"param" is the argument that contains all of the filters and such.
List<Client> _clients = db.Client.ToList();
IEnumerable<Client> filteredClients;
//Check for filters. This is a search, and we can say it's empty for this purpose
if (!string.IsNullOrEmpty(param.sSearch))
{
var nameFilter = Convert.ToString(Request["bSearch_1"]); // Search string
var isNameSearchable = Convert.ToBoolean(Request["bSearchable_1"]); // Is column searchable? Optional
filteredClients = _clients.Where(x => x.Name.ToLower().Contains(param.sSearch.ToLower()));
}
else
{
filteredClients = _clients.OrderBy(x => x.Name);
}
// Sort Column
var isNameSortable = Convert.ToBoolean(Request["bSortable_1"]);
var sortColumnIndex = Convert.ToInt32(Request["iSortCol_0"]);
Func<Client, string> orderingFunction = (c => sortColumnIndex == 1 ? c.Name.ToString()
: sortColumnIndex == 2 ? c.AccountExec.ToString()
: sortColumnIndex == 3 ? c.SalesforceLink.ToString()
: sortColumnIndex == 4 ? c.Location.ToString()
: sortColumnIndex == 5 ? c.PrimaryContact.ToString()
: sortColumnIndex == 6 ? c.AccountId.ToString()
: ""); // index 0 is the hidden ClientId
// Sort Direction
var sortDirection = Request["sSortDir_0"]; // asc or desc
if (sortDirection == "asc")
{
//Results of this line generate exception
filteredClients = filteredClients.OrderBy(orderingFunction);
}
else
{
filteredClients = filteredClients.OrderByDescending(orderingFunction);
}
Well! Thanks to johnny 5's wondering why the c.AccountId.ToString() was in use, I pulled the .ToString() from the func<>, and that seemed to do the trick.
Thanks, johnny 5! Sometimes you just need another set of eyes!

LINQ to Entities does not recognize the method 'Int32 Min(Int32, Int32)'?

Im getting this error when I execute the following code, any Ideas how to fix it?
LINQ to Entities does not recognize the method 'Int32 Min(Int32, Int32)' method, and this method cannot be translated into a store expression.
result = items.ToList()
.Select(b => new BatchToWorkOnModel()
{
BatchID = b.Batch.ID,
SummaryNotes = b.Batch.Notes,
RowVersion = b.Batch.RowVersion,
Items = items
.Select(i => new ItemToWorkOnModel()
{
SupplierTitle = i.Title,
ItemID = i.ID,
BatchID = i.BatchID ?? 0,
ItemDate = i.PubDate,
// KB - Issue 276 - Return the correct Outlet name for each item
Outlet = i.Items_SupplierFields != null ? i.Items_SupplierFields.SupplierMediaChannel != null ? i.Items_SupplierFields.SupplierMediaChannel.Name : null : null,
Status = ((short)ItemStatus.Complete == i.StatusID ? "Done" : "Not done"),
NumberInBatch = i.NumInBatch,
Text = string.IsNullOrEmpty(i.Body) ? "" : i.Body.Substring(0, Math.Min(i.Body.Length, 50)) + (i.Body.Length < 50 ? "" : "..."),
IsRelevant = i.IsRelevant == 1,
PreviouslyCompleted = i.PreviouslyCompleted > 0 ? true : false
}).ToList()
})
.FirstOrDefault();
It seems Math.Min is not implemented by the EF query provider. You should be able to fix it by simply applying AsEnumerable on your items collection to do the expression using Linq to Objects instead;
Items = items.AsEnumerable().Select(i => new ItemToWorkOnModel()...
If you add a where condition to the item selection (seems a little strange to take all items in the whole table), you'll want to add it before AsEnumerable() to allow EF to do the filtering in the database.
Also, you only want the first result from the query, but you're fetching all of them using ToList() before cutting the list down to a single item. You may want to remove the ToList() so that EF/the underlying database can return only a single result;
result = items.Select(b => new BatchToWorkOnModel()...
You do not need Math.Min.
The line in question is:
Text = string.IsNullOrEmpty(i.Body)
? "" : i.Body.Substring(0, Math.Min(i.Body.Length, 50)) + (i.Body.Length < 50 ? "" : "...")
So what does this line return?
If i.Body is null or empty it returns an empty string. If it is 50 or more characters long it returns a substring of 50 characters and appends "...".
If the length is less than 50 it takes a substring with the length of the string and appends an empty string. But that's just the original string.
Text = string.IsNullOrEmpty(i.Body)
? "" : (i.Body.Length < 50 ? i.Body : i.Body.Substring(0, 50) + "...")

Comma delimited value Entity Framework in contains Statement

I have LINQ statement that has a comma delimited value.
I want to see if my Field matches any of the comma delimited values.
public string IdentifyProductSKU(string Serial)
{
int Len = Serial.Length;
var Split = from ModelSplitter in entities.Models
select ModelSplitter.m_validationMask.Split(',');
var Product = (from ModelI in entities.Models
where ModelI.m_validation == 0 &&
ModelI.m_validationLength == Len &&
ModelI.m_validationMask.Contains(Serial.Substring(ModelI.m_validationStart, ModelI.m_validationEnd))
select ModelI.m_name).SingleOrDefault();
return Product;
}
To explain the code: Every Model has got multiple identifying properties for eg. XX1,XX5,XX7 is all the same product. Now when I pass in a serial number I want to Identify the product based on the validation mask. For eg: XX511122441141 is ProductA and YY123414124 is ProductC. I Just want to split the in this query so in this line:
ModelI.m_validationMask.Contains(Serial.Substring(ModelI.m_validationStart, ModelI.m_validationEnd))
I want to Split the Validation mask To see if the serial contains any of the validation mask characters. Does this make sense?
This is how you split values into a list
var split = context.Packs.Select(u => u.m_validationMask).ToList();
List<String[]> list=new List<String[]>();
foreach (var name in split)
{
String[] str = name.Split(',');
list.Add(str);
}
Now I need to know how I can use that list in my Final EF Query:
int Len = Serial.Length;
var split = entities.Models.Select(u => u.m_validationMask).ToList();
List<String[]> list = new List<String[]>();
foreach (var name in split)
{
String[] str = name.Split(',');
list.Add(str);
}
var Product = (from ModelI in entities.Models
where ModelI.m_validation == 0 &&
ModelI.m_validationLength == Len &&
list.Contains(Serial.Substring(ModelI.m_validationStart, ModelI.m_validationEnd))
select ModelI.m_name).SingleOrDefault();
return Product;
I don't fully understand what you mean or what you are trying to do. But...
If your ModelSplitter.m_malicationMask can indead be split as you had demonstrated, then Split is a List then. What I don't understand is if you are trying to match the entire product A, or just the first three characters, you can modifiy your query
var Product = (from ModelI in entities.Models
where ModelI.m_validation == 0 &&
ModelI.m_validationLength == Len &&
ModelI.m_validationMask.Contains(Serial.Substring(ModelI.m_validationStart, ModelI.m_validationEnd))
let productId = ModelI.m_name.Substring(0, 3)
where split.Contains(productId)
select ModelI.m_name).SingleOrDefault();
Product should now be null if it does not match or an acutal product if it does.

Conditional Multiple Fields Searching and Filtering in LINQ

Assuming that we have the following table:
Person:
PersonID,
Name,
Age,
Gender
And we are providing a search function that allows users to search the table according to the name and/or the age.
The tricky part in writing the SQL ( or LINQ) query is that the users can choose to search for both field, or any one field, or no field. If he wants to search for all then he would just have to leave the textbox blank.
The logic to do this can be written as follows:
var p;
if(Name_TextBox=='')
{
p=from row in person
select row ;
}
else
{
p= from row in person
where row.Name=Name_TextBox
select row ;
}
// repeat the same for age
Now after a while the code gets very long and messy... How can I compress the above into a single query with no if-else?
Try code like this
string personName = txtPersonName.Text;
int personAge = Convert.ToInt32(txtAge.Text);
var opportunites = from p in this.DataContext.Persons
select new
{
p.PersonID,
p.Name,
p.Age,
p.Gender
};
if (personsID != 0)
opportunites = opportunites.Where(p => p.PersonID == personID);
if (personName != string.Empty)
opportunites = opportunites.Where(p => p.Name.StartsWith(personName));
if (personAge != 0)
opportunites = opportunites.Where(p => p.Age == personAge);
This will work fine. If personName is not given it will be not add to where, and if given then it will added.
One alternative which I have used in SQL which could be implemented in Linq too is
var p = from p in Person
where p.Name == Name_TextBox || Name_TextBox == String.Empty
select p;
(Note that your 'linq' is using SQL syntax, which won't compile. Also you can't declare a var as you are doing without directly assigning a value)
why not use the null coalescing operator? eg.
var products = from a in context.products
where a.ID == (productID ?? a.ID)
select a;
This works really well on my system

Linq Error: InvalidOperationException: Could not translate expression

Get value out of DateTime column
if null to return String.Empty
else
DateTime.ToShortDateString
What am I doing wrong => query produced below:
var queryable = from p in Products
select new {
selldate = p.SellEndDate == null
? string.Empty
: p.SellEndDate.Value.ToShortDateString() };
Error: InvalidOperationException: Could not translate expression 'Table(Product).Select(p => new <>f__AnonymousType01(selldate = IIF((p.SellEndDate = null), Invoke(value(System.Func1[System.String])), p.SellEndDate.Value.ToShortDateString())))' into SQL and could not treat it as a local expression.
Basically what's happening here is that LINQ to SQL is taking your entire query and trying to convert it into something that SQL Server can understand. The problem, though, is that SQL Server has no concept of DateTime.ToShortDateString, so the conversion to SQL fails.
You'll have to change your query so that it just selects SellEndDate (which will get it as a Nullable<DateTime>) and then when you use the results of that query you can do the conversion to string. For example:
var list = (from p in Products
select p.SellEndDate).ToList();
// calling ToList() above means we have the entire resultset in memory and
// no longer have to pass the query back to SQL Server
var stuff = from p in list select new
{
selldate = p.SellEndDate == null ?
string.Empty :
p.SellEndDate.Value.ToShortDateString()
};
ToShortDateString doesn't seem to have equivalent SQL translation.
Use ToString instead.
If the date time field allows nulls:
from order in repository.Order
select order.OrdShipDate == null ? "" : order.OrdShipDate.GetValueOrDefault(DateTime.Now).Month.ToString() + "/" + order.OrdShipDate.GetValueOrDefault(DateTime.Now).Day.ToString() + "/" + order.OrdShipDate.GetValueOrDefault(DateTime.Now).Year.ToString();
If the date time field doesn't allow nulls:
from order in repository.Order
select order.OrdShipDate.Month.ToString() + "/" + order.OrdShipDate.Day.ToString() + "/" + order.OrdShipDate.Year.ToString();

Resources