I am using LINQ to retrieve Account type entities from Microsoft Dynamics CRM Online. I am unable to filter the list for a particular formatted value. I have the correct value, but am receiving zero records.
I'm creating my connection like this:
var connection = new CrmConnection("CRMOnline");
connection.ProxyTypesEnabled = true;
CrmOrganizationServiceContext _context = new CrmOrganizationServiceContext(connection);
I've tried:
List<Account> items = _context.CreateQuery<Account>()
.Where( c => ((OptionSetValue)c["new_accreditationstatus"]).Equals(7))
.ToList();
and
List<Account> items = _context.CreateQuery<Account>()
.Where( c => c.GetFormattedAttributeValue("new_accreditationstatus") == "7"
.ToList();
and
List<Account> items = _context.CreateQuery<Account>()
.Where( c => c["new_accreditationstatus"] == "7"
.ToList();
The last on throws a System.Format exception.
Filters on normal properties, i.e. .Where(c => c.AccountNumber.StartsWith("2010")) work perfectly fine.
You can only get access to the _____Set entities when generating the early-bound CRM file (look into crmsvcutil.exe/Xrm.cs online) and creating an early-bound derivative of CrmOrganizationServiceContext (commonly called XrmServiceContext). You can see the available constructors in the early-bound file.
So if you know the (int) value of the OptionSetValue in advance (7, in this case), you can just use this value as one of the arguments in the Where clause, as you've stated elsewhere:
.Where( c => c.new_AccreditationStatus.Value == 7)
EDIT (try this):
var list = _context.AccountSet.Where(c =>
c.FormattedValues["new_accreditationstatus"] == "7").ToList();
Another great question, but unfortunately, I think this will represent another failure/"limitation" of the Linq provider, which doesn't mention anything about FormattedValues as one of the permitted uses of the Where clause, though it is permitted as an item in the Select clause.
The actual values for OptionSetValues are stored in the StringMap entity, and incidentally enough, you can access the StringMap entity via Linq. An example is as follows.
// This query gets one permissible value for this entity and field.
var actualValue = _context.CreateQuery("stringmap")
.Where(x => x.GetAttributeValue<string>("attributename") == "new_accreditationstatus")
.Where(x => x.GetAttributeValue<int>("value") == "7")
.Where(x => x.GetAttributeValue<int>("objecttypecode") == Account.EntityTypeCode)
.Select(x => x.GetAttributeValue<string>("value"))
.Single();
However, trying to build on this with a subquery and a version of your original query, as in the below, results in an exception, also below.
var actualValues = _context.CreateQuery("stringmap")
.Where(x => x.GetAttributeValue<string>("attributename") == "new_accreditationstatus")
.Where(x => x.GetAttributeValue<int>("objecttypecode") == Xrm.Account.EntityTypeCode);
// This (modified) query uses the StringMap values from the previous query in
// a subquery, linking to the int (attributevalue) value that
// new_accreditationstatus represents.
List<Account> items = _context.CreateQuery<Account>()
.Where(c => actualValues
.Where(x => x.GetAttributeValue<int>("attributevalue") == c.new_accreditationstatus.Value)
.Select(x => x.GetAttributeValue<string>("attributevalue"))
.Single() == "7")
.ToList();
...throws an exception.
Privilege Type Read not defined on entity 'StringMap'.
Which is of course frustrating, because somehow, Linq allows you to query the string map in the first query.
So you'll have to first query the StringMap entity for the AttributeValue that corresponds to "7", then use that value in a new query that references that value as follows:
var actualValue = _context.CreateQuery("stringmap")
.Where(x => x.GetAttributeValue<string>("attributename") == "new_accreditationstatus")
.Where(x => x.GetAttributeValue<int>("value") == "7")
.Where(x => x.GetAttributeValue<int>("objecttypecode") == Account.EntityTypeCode)
.Select(x => x.GetAttributeValue<string>("attributevalue"))
.Single();
List<Account> items = _context.CreateQuery<Account>()
.Where(c => c.new_accreditationstatus = new OptionSetValue(actualValue)
.ToList();
If I can ever find a way to do all of this in one query, I will definitely edit and repost.
Have you seen the crmsvcutil extension that will generate enumerations for optionsets?
Related
I am trying to generate a List<Object> using Where clause with properties from a different List<Object>. I know that I could use a .Include(), similar to a SQL join if I were using Entity Framework but I am not using Entity Framework so I don't think it would work. I have:
List<Problem> problems = MethodToCallDbAndGenerateList(); //ado.net
problems = problems.Where(x => x.Property1 == "value").ToList();
//remaining logic
List<Solved> solved = MethodToCallDb()
.Where(x => x.SolvedId == problems.ProblemId)
.ToList();
//error happens in Where(...problems.ProblemId);
//List<Problem> does not contain a definition for ProblemId
The error says the List<Problem> does not contain ProblemId but I do have that property in my class. So I am unsure of why I am getting that error.
How can I generate my List<Solved> based on filtered results from
.Where(x => x.SolvedId == problems.SolvedId);
Using LINQ to Objects, you can use the Enumerable.Join method to create a join between two List<T>s and just return the matching members:
List<Problem> problems = MethodToCallDbAndGenerateList()
.Where(x => x.Property1 == "value")
.ToList();
List<Solved> solved = MethodToCallDb()
.Join(problems, s => s.SolvedId, p => p.ProblemId, (s,p) => s)
.ToList();
However, if there are a lot of problems and solved, or if you frequently check the same list of problems, or if you are only creating problems to use in the join, you'd be better off creating a HashSet:
var problemIDs = problems.Select(p => p.ProblemId).ToHashSet();
List<Solved> solved = MethodToCallDb()
.Where(s => problemIDs.Contains(s.SolvedId))
.ToList();
NOTE: If you are only creating problems to use in the join, better to skip creating the List<Problem> and just do:
var problemIDs = MethodToCallDbAndGenerateList()
.Where(x => x.Property1 == "value")
.Select(p => p.ProblemId)
.ToHashSet();
I'm trying to find all customer codes where the customer has a status of "A" and whose code does not contain any letter using LINQ query.
var activeCustomers = Customers.Where(x => x.Status == "A" && x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code);
When I run this query in LinqPad I get the following error:
You'll need to do this as a two part query. First, you could get all the users who's status is "A":
var activeCustomers = Customers.Where(x => x.Status == "A").ToList();
After you've got those in-memory, you can create an additional filter for char.IsDigit:
var codes = activeCustomers.Where(x => x.Code.Any(n => !char.IsLetter(n)))
.Select(x => x.Code)
.ToArray();
As commenters have stated, IsLetter() cannot be translated to SQL. However, you could do the following, which will first retrieve all items with Status "A" from the database, then will apply your criteria after retrieval:
var activeCustomers = Customers.Where(x => x.Status == "A").AsEnumerable().Where(x => x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code);
You'll have to determine if it's acceptable (from a performance perspective) to retrieve all customers with "A" and then process.
The AsEnumerable() transitions your LINQ query to working not with IQueryable (which works with SQL) but with IEnumerable, which is used for plain LINQ to objects.
Since it is LINQ 2 SQL, there is no natural way to translate char.IsLetter to something SQL can understand. You can hydrate a query that retrieves your potential candidates and then apply an addition in-memory filter. This also solves the issue where LINQ 2 SQL has a preference for a string and you are dealing with chars
var activeCustomers = Customers.Where(x => x.Status == "A").ToList();
var filteredCustomers = activeCustomers.Where(x =>
x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code).ToList();
There are two performance hits here. First, you're retrieving all potential records, which isn't too desirable. Second, in your above code you were only interested in an enumerable collection of codes, which means our query is including far more data than we originally wanted.
You could tighten up the query by only returning back to columns necessary to apply your filtering:
var activeCustomers = Customers.Where(x => x.Status == "A")
Select(x => new Customer{ Status = x.Status, Code = x.Code }).ToList();
You still return more sets than you need, but your query includes fewer columns.
I'm trying to select one field last record in filtered database (this is different than last inserted record). I tried with following code in controller but instead of field value, i'm getting "true" or "false", depending on if there's results after filtering or not.
List<Pozicije> poz = new List<Pozicije>();
poz = db.Pozicijes.Where(p => p.grupa == grupa)
.OrderBy(p => p.sifra_pozicije).ToList();
string pos = poz.Select(p => p.sifra_pozicije.Contains(s)).LastOrDefault().ToString();
can someone point me how to get value i need instead?
Try this instead. I've combined both parts of your query into one.
var pos =
Convert.ToString(db.Pozicijes.Where(p => p.grupa == grupa
&& p.sifra_pozicije.Contains(s))
.OrderByDescending(p => p.sifra_pozicije)
.Select(p => p.sifra_pozicije)
.FirstOrDefault());
If it doesn't work, you may need to tell us what types s and sifra_pozicije are.
LastOrDefault is not supported with LINQ to Entities/LINQ TO SQL. You need to do OrderByDescending and then get First record. Like:
string pos = db.Pozicijes.Where(p => p.grupa == grupa && p.sifra_pozicije.Contains(s)))
.OrderByDescending(p=> p.sifra_pozicije)
.Select(r=> r.sifra_pozicije)
.First();
This code builds without problems but fails when executed:
var query =
_db.STEWARDSHIP
.OrderByDescending(r => r.VisitDate)
.Where(r => SiteId == null || r.SiteAssoc.Id == iSiteId)
.Where(r => r.MarkedForDeletion == false)
.Select(r => new SearchResults(r.Id,
r.SiteAssoc.Name,
r.VisitDate,
r.VisitTypeValAssoc.Description));
The error message is:
Only parameterless constructors and initializers are supported in LINQ to Entities.
What am I doing wrong here? I must pass info to the constructor to build the SearchResults objects. Is there a way to do this or do I have to make this a 2-step thing where I get the results and then iterate over the results to create the new objects?
UPDATE:
Changing the select to this fixes the issue:
.Select(r => new SearchResults(){Id = r.Id,
Name = r.SiteAssoc.Name,
VisitDate = r.VisitDate,
VisitType = r.VisitTypeValAssoc.Description});
Try this
.OrderByDescending(r => r.VisitDate)
.Where(r => SiteId == null || r.SiteAssoc.Id == iSiteId)
.Where(r => r.MarkedForDeletion == false)
.Select(r => new SearchResults()
{
ID = Ir.Id,
Name = r.SiteAssoc.Name,
VisitDate = r.VisitDate,
Description = r.VisitTypeValAssoc.Description
});
Entities can be created outside of queries and inserted into the data
store using a DataContext. You can then retrieve them using queries.
However, you can't create entities as part of a query.
See this post Explicit construction of entity type '###' in query is not allowed.
LINQ doesn't allow the explicit construction of objects within the select statements.
I'm have a SQL statement which I am trying to transform in a LINQ statement...
SELECT DISTINCT mc.*
FROM ManufractorCategories mc
WHERE mc.Active = 'true'
AND mc.Folder = 'false'
AND (mc.Id not in (SELECT Category_id FROM Manufractor_Category
WHERE Manufractor_id = 3));
That's my last, not working LINQ statement
(IQueryable<object>)db.ManufractorCategories
.Where(o => o.Active == active)
.Where(o => o.Folder == folder)
.Select(i => new { i.Id, i.Folder }).Except(db.Manufractor_Categories.Where(t => t.Manufractor_id == id).Select(t => new { t.Category_id })).Distinct();
I've tried the whole Sunday on that, but the Except statement won't work.
Thanks in advances for any help!
The Except method requires two sets of the same type - this means that you would have to select objects of type ManufractorCategory in the nested query as well as in the outer query - then it would select all categories that are in the first one and not in the second one.
An easier alternative is to use the Contains method to check whether the current ID is in a list of IDs that you want to filter. The following should work:
var q =
db.ManufractorCategories
.Where(o => o.Active == active)
.Where(o => o.Folder == folder)
.Select(i => new { i.Id, i.Folder })
.Where(o =>
!db.Manufractor_Categories
.Select(t => t.Manufractor_id)
.Contains(o.Id)
.Distinct();
And a simplified version using query syntax:
var q =
from o in db.ManufractorCategories
where o.Active == active && o.Folder == folder &&
db.Manufractor_Categories
.Select(t => t.Manufractor_id)
.Contains(o.Id)
select new { i.Id, i.Folder };
The Except statement is going to get a list of objects with the Category_id property. However, you're query has a result that contains objects with the Id and Folder properties. The query will most likely be unable to see where these objects are equal, and so, the Except clause won't take effect.