1

Does anyone know how to make a Linq query that gets all the birthdays of today? The code below doesn't work :

var getBirthdays = 
    orgContext.CreateQuery<Contact>()
              .Where(c => c.BirthDate != null 
                          && c.BirthDate.Value.Month == DateTime.Now.Month).ToList();

I get an error like this:

"Invalid 'where' condition. An entity member is invoking an invalid property or method."

Thanks in advance!

Click Ok
  • 8,700
  • 18
  • 70
  • 106
Freeetje
  • 523
  • 8
  • 27
  • 1
    Is BirthDate.Value a DateTime? – Ryan Bennett Apr 13 '12 at 14:33
  • Yes it is, but the problem is that we can't access it through LINQ in CRM 2011.. 100% sure that the Birthdate.Value is adatetim. – Freeetje Apr 13 '12 at 14:41
  • 1
    So its failing as in .Value is not an accessable property? What is the actual error? What in particular is not working? – Ryan Bennett Apr 13 '12 at 14:43
  • What kind of error do you get? – Omar Apr 13 '12 at 14:45
  • Sorry for not posting the error => "Invalid 'where' condition. An entity member is invoking an invalid property or method." The thing is that he has to check for ex. all the persons who has a birthday on 13/04 without the year! – Freeetje Apr 13 '12 at 14:52
  • 1
    Not related to the problem, but don't forget to factor in the people born on Feb 29th. – DCoder Apr 13 '12 at 15:28

5 Answers5

4

Anytime a vendor writes a four part blog series on how to do something as simple as finding a birthday (as Microsoft did in 2007), you have to know this won't be simple. So far as I can tell, this hasn't updated since then.

So you have limited options:

  1. Make new fields called something like new_birthmonth and new_birthday that's updated every time a contact is created or updated via a plugin, and then query on those int fields.
  2. Using Dynamic Linq, construct an OR clause in your WHERE clause that checks to see if the birthday falls in a reasonable range of years (say, 140 for the long-livers) (code below).
List<string> birthdays = new List<string>(); //will contain list of OR clauses

//makes sure no CRM unsupported dates are passed (less than 1/1/1900)
for (int i = Math.Min(140, DateTime.Today.Year - 1900); i > -1; i--) 
{
    //adds a different date per year
    birthdays.Add
    (
        string.Format
        (
            //DateTimes are stored in UTC
            "BirthDate = DateTime.Parse(\"{0}\")",
            DateTime.Today.ToUniversalTime().AddYears(-i)
        )
    );
}

//completes the correct dynamic linq OR clause
string birthdayList = string.Join(" OR ", birthdays);

var getBirthdays = orgContext.CreateQuery<Xrm.Contact>()
    .Where(c => c.BirthDate != null)
    .Where(birthdayList)
    .ToList();
Community
  • 1
  • 1
Peter Majeed
  • 5,304
  • 2
  • 32
  • 57
  • The LINQ provider can be utterly infuriating. My favourites: ToLower() and Count() give the same NotImplementedException as well – glosrob Apr 13 '12 at 18:55
  • @glosrob: Agreed: you should always have complete access to *your* data. Oh well - at least the next update rollup sounds nice. – Peter Majeed Apr 13 '12 at 19:24
  • Will this issue be solved in the next update rollup of CRM? I'll try it out and let you know if I made it work! – Freeetje Apr 16 '12 at 13:39
  • I tried to add the "BirthDayList" in my query and I'm getting the follwoing : 'System.Linq.IQueryable' does not contain a definition for 'Where' and the best extension method overload 'System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)' has some invalid arguments – Freeetje Apr 16 '12 at 13:55
  • @fredericdemeilliez: No, I don't think this issue will be fixed in the next issue of the CRM, unless they change their query language. – Peter Majeed Apr 16 '12 at 17:37
  • @fredericdemeilliez: And regarding your compile error, it is most likely because you are not including a reference to `System.Linq.Dynamic` in your `using` declarations. Once referenced, it will work. – Peter Majeed Apr 16 '12 at 17:38
  • Thx for all the help! I'll test it out with the System.Linq.Dynamic.. It's handy to know why it didn't work! – Freeetje Apr 17 '12 at 11:15
2

I solved my problem based on the example of "Peter Majeed" and using "LinqKit"!

var predicate = PredicateBuilder.False<Contact>();
for (int i = Math.Min(140, DateTime.Today.Year - 1900); i > -1; i--)
{
    DateTime cleanDateTime = new DateTime(DateTime.Today.AddYears(-i).Year, DateTime.Today.AddYears(-1).Month, DateTime.Today.AddYears(-i).Day);
    predicate = predicate.Or(p => p.BirthDate == cleanDateTime.ToUniversalTime());
}
var getBirthdays = (from c in orgContext.CreateQuery<Contact>().AsExpandable().Where(predicate)
                     select c).ToList();

The above query gave me the correct result! Thx to all who helped me!

Freeetje
  • 523
  • 8
  • 27
  • 2
    As a courtesy to all those that took the time to help and their answers were actually useful, please consider upvoting their answers. – Peter Majeed Apr 16 '12 at 17:39
  • When trying this, I get this error: An exception of type 'System.ServiceModel.FaultException`1' occurred in Microsoft.Xrm.Sdk.dll but was not handled in user code. Additional information: Generic SQL error. – Costin_T Nov 21 '16 at 09:11
1

If c.BirthDate is nullable, you have to convert it to a datetime first:

var getBirthdays = orgContext.CreateQuery<Contact>()
                             .Where(c => c.BirthDate != null && 
                                     (Convert.ToDateTime(c.BirthDate).Month == 
                                        DateTime.Now.Month) && 
                                      Convert.ToDateTime(c.BirthDate).Day == 
                                        DateTime.Now.Day))
                             .ToList();
Steve Mallory
  • 4,245
  • 1
  • 28
  • 31
1

You could fetch this info with a Query, if that is possible in your situation?

//set up the condition + filter
var ce = new Microsoft.Xrm.Sdk.Query.ConditionExpression();
ce.Operator = Microsoft.Xrm.Sdk.Query.ConditionOperator.LastXDays;
ce.AttributeName = "birthdate";
ce.Values.Add(30);

var fe = new Microsoft.Xrm.Sdk.Query.FilterExpression();
fe.AddCondition(ce);

//build query
var query = new Microsoft.Xrm.Sdk.Query.QueryExpression();
query.EntityName = "contact";
query.Criteria.AddFilter(fe);

//get results
var results = CrmHelperV5.OrgProxy.RetrieveMultiple(query);

//if you want early bound entities, convert here.
var contacts = new List<Contact>();
foreach(var result in results.Entities)
{
    contacts.Add(result.ToEntity<Contact>());
}

You may want to investigate the other operators for the filters + conditions

glosrob
  • 6,631
  • 4
  • 43
  • 73
1

You can use QueryExpression (it works for Microsoft CRM Plugin)

public EntityCollection getBirthdateList(IOrganizationService orgsService)
    {
        List<string> birthdays = new List<string>(); 

        //makes sure no CRM unsupported dates are passed (less than 1/1/1900)
        for (int i = Math.Min(140, DateTime.Today.Year - 1930); i > -1; i--)
        {
            //adds a different date per year
            birthdays.Add
            (
                DateTime.Now.AddYears(-i).ToString("yyyy-MM-dd")
            ); 
        }

       
        // Instantiate QueryExpression 
        var query = new QueryExpression("contact");

        // Define filter QEquote.Criteria
        var queryfilter = new FilterExpression();
        query.Criteria.AddFilter(queryfilter);

        // Define filter 
        queryfilter.FilterOperator = LogicalOperator.Or;
        queryfilter.AddCondition("birthdate",ConditionOperator.In,birthdays.ToArray());
        return orgsService.RetrieveMultiple(query); ;
    }