Odata - New Hire Detection - c#

I need to find New Hires that have been entered into Success Factors and will be starting in the future via OData.
The following C# based query is what I'm using:
DateTime tomorrow = DateTime.UtcNow.AddDays(1);
var newHires = EmpEmployment
.Expand("userNav")
.Where (e => e.startDate >= tomorrow);
The above query returns staff who have a startDate in the future however the User expansion does not find any matching entries and is null.
The issue appears to be that the User table is only populated with entries for the person when the start date is reached. This is a problem because I want to create them in other systems in advance.
Does anyone know if this is normal or thoughts around how I can obtain details like firstname, lastname of future employees who have been entered in the system?

You are trying to use EmpEmployment entity from Employee Central. The required date could be retrieved from RCM (Recruitment Management).
There is no clear answer to your question because customizing depends on the project; you should ask functional consultant responsible for EC what the way to get New hires startDate.

Related

Can you use a dictionary with a let statement in Iqueryable with Entity framework core?

So what I am trying to do is write code that will pick an appropriate personnel based on the data that I have to submit a license to a district board. This code is obfuscated from my actual codebase so please bear with the student/class example.
The logic should say, if there was no teacher present, or teacher has no license then use the state school directors license. if we cant find that, use the national.
The fall back works fine when I exclude the state director from the query, it runs without problem. However, when I try to include this, it throws a 'System.InvalidOperationException'. I have verified that all students have a current address and an appropriate state exists in the dictionary.
It looks like my current syntax of using a dictionary in an iqueryable is invalid and EF doesnt know how to work with a dictionary!
var qry = from student in FilteredContext // type is IQueryable<Student>
let studentsAddress = student.Addresses.FirstOrDefault(x=>x.IsCurrent)
let instructor = lastClass == null ? : lastClass.Instructor
let stateSchoolDirector = _personnelContext.Context.FirstOrDefault(x=>x.ID == SchoolDirectors.ByState[currentAddress.State])
let nationalSchoolDirector = _personnelContext.Context.FirstOrDefault(x => x.ID == SchoolDirectors.NationalSchoolDirector)
let personnelForLicense = instructor ?? stateSchoolDirector ?? national
select personnelForLicense.License;
var results = qry.ToList();
So my question is, is there any way to represent a dictionary or something like it within iqueryable? If not, are there any reasonable work arounds? I am totally stuck here and despite seraching could not easily find anything like it on StackOverflow or google results. The closest suggestion I could find was telling me to convert to .AsEnumerable() which I fail to see how it will work with my let statements
Just for going full circle, the only option that I could think of was to convert the dictionary into an actual SQL table/entity on the context

LINQ query maps dates incorrectly

My query looks like so:
using (var ctx = new PCLvsCompContext())
{
var brokerId = broker?.Id;
var symbolId = company?.Id;
var result = (from t in ctx.TradeHistoryJoineds
where t.TradeDate >= fromDate
&& t.TradeDate <= toDate
&& (brokerId == null || t.BrokerId == brokerId)
&& (symbolId == null || t.SymbolId == symbolId)
select t).OrderBy(x => x.TradeDate).ThenBy(x => x.BrokerName).ToList();
return result;
}
As an example, I run this query with dates like fromDate March-01-2017 toDate March-31-2017. I then captured the generated sql in SQL profiler that this query produces and ran it in SQL management studio. The output was as expected where for each weekday, each company has some trades. The query is based off of a view which casts all dates to "datetime" so that excel can parse them as dates correctly. However, when I put a breakpoint at "return result" and inspect the dates, all but 2 of the dates are March-1-2017. This is incorrect, the query result in SQL manager shows trades for almost every weekday in March (which is correct).
What is going on here? Why is Linq losing its mind?
Although based on the results I cannot see exactly how you would end up with those results, it is very common that you could be dealing with a DateTime timezone issue. I suspect that perhaps you saved your dates to the database using a DateTime object from say DateTime.Now instead of DateTime.UtcNow. So at that point in time and based on the machine it was called on it would be based on the timezone and datelight savings of that machine.
A DateTime object should not be used as it can relate to the region of the SQL database, the region of the server making this LINQ call and so the two regions could be on different timezones.
Instead you should always use DateTimeOffset.
If you cannot do that for some reason, then double-check your dates toDate and fromDate and do:
var utcToDate = toDate.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'");
var utcFromDate = toDate.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'");
Which gives something like this if it was run on 3rd April 2018 at 22:56.
2018-04-03T22:56:57.740Z
You would then also need to make sure when you save any date to the SQL backing store that you do ToUniversalTime() firstly. You can check your SQL tables directly with a normal SQL query and they should be stored in the database as the same UTC string format as above, and then the comparison should be obvious to whether it is valid.
However I would strongly recommend changing all your DateTime calls and gets to DateTimeOffset. The only time to use DateTime in majority of cases is for the final display to a user.
Thank you all for your suggestions. For those who are familiar with linq, EF and views this may seem like a stupid oversight, but I will post my shame for others in case this happens to them since the cause and the resulting behavior are not immediately obviously linked (as can be seen by all the suggestions, none of which actually point to the answer).
When you are querying a view using linq and Entity Framework, you apparently must have a column called 'Id', otherwise Entity Framework can't distinguish any of the rows from one another and simply makes some sort of duplication that I can't quite decipher (all the rows were unique based on their data even without an Id column, so I can't quite figure out why this is necessary).
So by adding an the TradeId with an 'Id' alias to the view, then Entity Framework seemed to return to sanity and map the data as expected.

Updating a field through workflows, better approach?

I have been asked to create a view that includes entities that fit within a date range. So if the entity's new_date1 field is lesser than today, and its new_date2 field is greater than today, the entity should appear in the subgrid on the form.
Unfortunately, you can't do this with simple views as FetchXML doesn't support calculations and operators that could return today's date.
I have come up with the idea of creating an Active field on the entity, then have javascript rules set that field depending on the date range entered.
A view could then use the Active field for a filter criteria.
The problem is that if the entity's form is not opened in a while, the entity might become inactive (today's date is now beyond both date1 and date2 for example) but if the users are not opening the entity's form, the field won't update itself and the view will show inactive entities as active ones.
So I thought of have a scheduled workflow gather all entities that should be active, or inactive then this workflow launches a child workflows, that either sets the Active flag to yes or no.
Here's a bit of the code involved:
private void LaunchUpdateOpportunityWorkflow(IOrganizationService service, ITracingService tracingService, DataCollection<Entity> collection, bool active)
{
foreach (Entity entity in collection)
{
//launch a different workflow, depending on whether we want it active or inactive...
Guid wfId = (active) ? setActiveWorkflowId : setInactiveWorkflowId;
ExecuteWorkflowRequest execRequest = new ExecuteWorkflowRequest();
execRequest.WorkflowId = wfId;
execRequest.EntityId = (Guid)entity["opportunityid"];
try
{
CrmServiceExtensions.ExecuteWithRetry<ExecuteWorkflowResponse>(service, execRequest);
}
catch (Exception ex)
{
tracingService.Trace(string.Format("Error executing workflow for opportunity {0}: {1}", entity["opportunityid"], ex.Message));
}
}
}
The process of gathering the relevant DataCollection is done through simple RetrieveMultipleRequest requests.
The problem with that approach is that if the server reboots, someone has to go and start the workflow that runs the code above.
Is there better a approach to this ? I am using MS CRM 2016.
Adding to Jame's answer, if the filter criterias get complicated where it cannot be achieved using fetchxml, you can always use a plugin.
Register a plugin on "RetrieveMultiple" message.
var queryExpression = PluginExecutionContext.InputParameters["Query"];
if(queryExpression == null || !queryExpression.EntityName.equals("yourentityname", StringComparison.InvariantCultureIgnoreCase) return;
Add a condition which is unique to the advanced find, because there is no way to filter down on which advanced find is triggering the plugin on your entity, easiest way to achieve this would be to add an attribute and use it in the advanced find query.
Check for the condition, if found, the user is trying to run the advanced find you have set up:
if (queryExpression.Criteria == null || queryExpression.Criteria.Conditions == null ||
!queryExpression.Criteria.Conditions.Any()) return;
Find the matching condition, so you can remove it and add conditions you'd like to filter the data by:
var matchContidion = queryExpression.Criteria.Conditions.FirstOrDefault(c => c.AttributeName == "yourflagattribute");
if (matchContidion == null) return;
Remove the dummy match criteria and add your own criterias:
queryExpression.Criteria.Conditions.Remove(matchContidion);
queryExpression.Criteria.Conditions.Add(new ConditionExpression("new_date1", ConditionOperator.LessThan, DateTime.Now));
queryExpression.Criteria.Conditions.Add(new ConditionExpression("new_field2", ConditionOperator.Equals, "Some complex value which cannot be set using fetchxml")); //for example, based on certain values, you might want to call a webservice to get the filter value.
I think you can probably achieve this with FetchXML.
new_date1 field is lesser than today
This is older than 24 hours.
new_date2 field is greater than today
This is any date in the future, so assuming your dates are no further than 100 years in the future you can use Next X Years.
As Darren Lewis pointed out, older than 24 hours might not be yesterday depending on your definition of yesterday. In which case try using Last X Years.

Linq query and group by data from table which has one to many relationship?

i have 3 tables,
1 is user table which store all user data, another one is application table, the last is user education table,
the relationship between these tables :
an user can have many applications, and also can have many education background
the problem
here is i want to select data for monthly report from application table and i want to group by my data by each education level, based on each user highest education, like this :
user A - high school
user A - bachelor degree
user A - master degree
user B - high school
user C - bachelor degree
user D - high school
will result :
master degree : total 1
bachelor degree : total 1
high school : total 2
i have tried this :
var edu = CTX.user_applications.Where(a => a.applied_date >= new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1)
&& a.applied_date <= DateTime.Now
).GroupBy(g => (g.user_list.user_edus.OrderByDescending(o => o.edu_lvl_id).FirstOrDefault()))
.Select(x => new ReportObject
{
Key = x.Key.ToString(),
Count = x.Count().ToString()
}).ToList();
error :
A group by expression can only contain non-constant scalars that are comparable by the server. The expression with type 'System.Data.Linq.EntitySet`1[OTOKarir.Models.user_edu]' is not comparable
it does not work, it give me an error, there's must some trick to do it correctly, btw i am not really sure that it will give me the desired result, i am stug with this, need some guidance, any idea will be very great? thx.
You're ordering by an object: a user_edu. This should be a scalar instead. That can easily be accomplished by selecting its edu_lvl_id.
Showing only the essential parts, this is what you should do:
var edu = CTX.user_applications
.GroupBy(g => (g.user_list
.user_edus.OrderByDescending(o => o.edu_lvl_id)
.FirstOrDefault()
.edu_lvl_id)); // This is added
Side note: grouping in LINQ-to-SQL is very inefficient, because for each group a separate query is executed to populate the items in the group. You may consider fetching the data first an doing the grouping in memory (LINQ-to-objects). And then you can group by the object again.
Take a look at this answer: LINQ GroupBy on multiple ref-type fields; Custom EqualityComparer
It explains that GroupBy can only be used for types that implement the IEqualityComparer<T> interface.
In order to know whether your query is correct or not, we would need the DB structure/types of data on which you perform the query.

Detect customer profile changes

I'm trying to figure how to write a LINQ query that return a list of customers who changed their address on a given date
Cusomters
- Name (nvarchar)
- Address (nvarchar)
- CheckInDate (datetime)
I would first get a list of customers who checked in on a certain date then loop through each customer and get that customer's check ins to see if there are changes. This would result in numerous database queries. Is there a more efficient way to do this?
This will detect all customers who changed their addresses by yourDate.
var result = Customers.Where(c=>c.CheckInDate <= yourDate)
.GroupBy(c=>c.Name)
.Where(g=>g.GroupBy(c=>c.Address).Count() > 1)
.SelectMany(x=>x);

Categories

Resources