Project Server 2013 Reassign Tasks and Update Timesheet CSOM - csom

I'm developing a small code that makes possible to read from database some values in order to do the Timesheets for all of employees of a corporation.
The data of the database comes from another app where Project Managers define all the task changes like reassign tasks.
The code works well when the task starts and ends with the same employee that was set from Project Manager at the start of the project tasks definition.
The issue is when Project Manager decides/needs to change the employee who is doing the task with other one for any reason. Then I got the problem that the Timesheet of the new employee doesn't have the Project Line in order to fill the Timesheet with my app.
So my question is:
- Is there any way to include the new lines to the new employee timesheet?
Thanks in advance.
EDIT:
I would like to add how I'm doing the fill of timesheet:
// I'm getting my period here --> Before I authenticate the user against Project Context
var myPeriod = projContext.TimeSheetPeriods.FirstOrDefault();
foreach (var l in myPeriod.TimeSheet.Lines)
{
for (int i = 0; i < TimesheetTaskId.Count; i++)
{
projContext.Load(projContext.Projects);
projContext.ExecuteQuery();
var proj = projContext.Projects.First(p => p.Id == Guid.Parse(projectid.ElementAt(i)));
projContext.ExecuteQuery();
if (l.Id == Guid.Parse(TimesheetTaskId.ElementAt(i)))
{
for (int x = 0; x < taskid.Count; x++)
{
draftProj = proj.CheckOut();
var plannedwork = l.Work.Where(w => w.Id == Guid.Parse(TimesheetTaskId.ElementAt(i))).FirstOrDefault();
TimeSheetWorkCreationInformation workCreation = new TimeSheetWorkCreationInformation
{
ActualWork = string.Format("{0}h", actualwork.ElementAt(x)), // Worked hours
Start = date.ElementAt(x),
End = date.ElementAt(x),
Comment = "From CSOM",
NonBillableOvertimeWork = "0",
NonBillableWork = "0",
OvertimeWork = "0",
PlannedWork = plannedwork == null ? "0h" : plannedwork.PlannedWork
};
l.Work.Add(workCreation);
myPeriod.TimeSheet.Update();
}
}
draftProj.CheckIn(true);
}
}
As you can see, during the foreach sentence I create an object type 'TimeSheetWorkCreationInformation' which allows me to fill the Timesheet if there are any work lines in it.
When we make the reassign, the timesheet of the new resource doesn't have the new work lines.
Remembering again my question:
- Is there any way to include the new lines to the new employee timesheet?
Thanks

Related

A better way to combine two Linq Queries into one in MVC5 Controller

in my MVC Application using EntityFramework I have a self referencing table: customers with customerParentID. For children with no emails I want to be able to channel correspondence to parents Email. This is the solution I have that works but I am hitting database 3 times and I want to combine the last 2 into one.
Controller Linq Query
useParentEmail is a bool which is true for children with no Email
Id is obtained by a parameter
var b = db.Lists.SingleOrDefault(a => a.LatestTxnId == Id);
var customerWithNoEmail = db.Customers.SingleOrDefault(a => a.CustomerID == b.CustomerID && a.useParentEmail);
if (customerWithNoEmail != null)
{
var customerParent = db.Customers.SingleOrDefault(a => a.CustomerID == customerWithNoEmail.ParentCustomerID);
customerParentEmail = customerParent.Email;
}
This query hits database twice is there a better way I can do this to hit database once? Any help will be appreciated.
Assuming you are using Entity Framework with the correct navigation properties in the model so that joins work automagically, something like this could work (I've used LinqPad and linq to objects for ease of example). To be honest although I used to be a heavy user of Entity Framework I haven't used it for a few years so I could be a little rusty.
var customers = new[]
{
new { CustomerID = 1, useParentEmail = true, email = "", ParentCustomerID = (int?)2 },
new { CustomerID = 2, useParentEmail = false, email = "parent#example.org", ParentCustomerID = (int?)null },
new { CustomerID = 3, useParentEmail = false, email = "child#example.org", ParentCustomerID = (int?)null }
};
int id = 1;
var emails = (from c in customers
join p in customers on c.ParentCustomerID equals p.CustomerID into childrenParents
from cp in childrenParents.DefaultIfEmpty()
where c.CustomerID == id
select new
{
c.CustomerID,
email = c.useParentEmail == true ? cp.email : c.email
}).SingleOrDefault();
emails.Dump();

EntityFramework 6 AsNoTracking and Projections

Given the following codes:
A.
using (var context = new testEntities())
{
context.tblAuthors.Select(p => new
{
Author_name = p.Author_name,
Id = p.Id,
country = p.country,
tblBooks = p.tblBooks.Select(z => new
{
Id = z.Id,
Auhthor_id = z.Auhthor_id,
Edition = z.Edition,
Price = z.Price
}).ToList()
}).ToList();
}
B.
using (var context = new testEntities())
{
context.tblAuthors.Include(p => p.tblBooks).AsNoTracking().ToList();
}
I ran both on a separate loop of a 100 iterations. The first code (A) gives me an average of 157ms execution time. The second code (B) gives me an average of 673ms execution time.
My questions are:
1 - Why does Entity Framework have this difference? I thought AsNoTracking() was supposed to perform the same as a Projection since the Context is not tracking the entities in both cases.
2 - If I want to get maximum performance should I be using projections for all read-only operations instead of AsNoTracking()?
Update 1: The amount of data that is being retrieved is the same. I am using all the available fields on the table. The query that is being generated by Entity Framework is the same for both codes.

Error while adding multiple items to an entity

for (int i = 0; i < skus.Count; i++)
{
sku item = new sku();
item = skus[i];
sku sku = CompanyDbContext.skus.Where(s => s.item_no == item.item_no).FirstOrDefault();
if (sku == null) // ok to insert [no duplicate item numbers]
{
CompanyDbContext.skus.Add(item);
}
}
CompanyDbContext.SaveChanges();
I'm getting
collection was modified enumeration operation may not execute
error. How can I fix this ?
As mentioned in the comments, this happens because you are modifying the collection which you are looping through as you're performing your work.
One option you have is to create a temporary collection and add your sku items to that, and finally add the contents of the temporary List<sku> to your CompanyDbContext
// Create a new temporary list
List<sku> tempSkus = new List<sku>();
for (int i = 0; i < skus.Count; i++)
{
// Let's assign item to skus[i] immediately, we don't need a new instance here when we're later re-pointing to an existing instance
sku item = skus[i];
// Use LINQ Any function to determine whether there are any existing SKU's already
bool existingSku = CompanyDbContext.skus.Any(s => s.item_no == item.item_no);
// There are no duplicates, let's add this sku item to our temporary List
if(!existingSku)
{
tempSkus.Add(item);
}
}
// Add the Range of tempSkus List to the CompanyDbContext
CompanyDbContext.skus.AddRange(tempSkus);
CompanyDbContext.SaveChanges();
Or if you prefer LINQ
// Create a new temporary list
List<sku> tempSkus = skus.Where(p => CompanyDbContext.skus.Any(s => s.item_no != p.item_no)).ToList();
// Add the Range of tempSkus List to the CompanyDbContext
CompanyDbContext.skus.AddRange(tempSkus);
CompanyDbContext.SaveChanges();
The problem is that you are modify the same thing that you are iterating. As best practice you should update your method something like this:
//get search predicat from List<sku> skus
var item_nos = skus.Select(s=>s.item_no).ToList();
//items already in repo
var addedItems = CompanyDbContext.skus.Where(s => item_nos.Contains(s.item_no)).ToList();
var newItems = skus.Except(addedItems).ToList();
foreach(var sku in newItems){
CompanyDbContext.skus.Add(item);
}
CompanyDbContext.SaveChanges();

load navigation properties with filter for Entity Framework 4.3

Few days back I put a question regarding mapping two classes Message and MessageStatusHistory using EF. The mapping is going fine but I am facing some problems with the navigation property StatusHistory in class Message that relates it to MessageStatusHistory objects. I am loading the messages for one user only and want to the statuses pertaining to that user only. Like I would want to show if the user has marked message as read/not-read and when. If I use default loading mechanism like following it loads all the history related to the message irrespective of the user:
IDbSet<Message> dbs = _repo.DbSet;
dbs.Include("StatusHistory").Where(x=>x.MessageIdentifier == msgIdentifier);
To filter history for one user only I tried following trick:
IDbSet<Message> dbs = _repo.DbSet;
var q = from m in dbs.Include("StatusHistory")
where m.MessageIdentifier == msgIdentifier
select new Message
{
MessageIdentifier = m.MessageIdentifier,
/*OTHER PROPERTIES*/
StatusHistory = m.StatusHistory
.Where(x => x.UserId == userId).ToList()
};
return q.ToList();//THROWING ERROR ON THIS LINE
I am getting the error:
The entity or complex type 'MyLib.Biz.Message' cannot be constructed in a LINQ
to Entities query.
I have tried by commenting StatusHistory = m.StatusHistory.Where(x => x.UserId == userId).ToList() also but it has not helped.
Please help me in getting Messages with filtered StatusHistory.
EDIT:- above is resolved with this code:
var q = from m in _repository.DBSet.Include("Histories")
where m.MessageIdentifier == id
select new {
m.Id,/*OTHER PROPERTIES*/
Histories = m.Histories.Where(x =>
x.SenderId == userId).ToList()
};
var lst = q.ToList();
return lst.Select(m => new Message{
Id = m.Id, MessageIdentifier = m.MessageIdentifier,
MessageText = m.MessageText, Replies = m.Replies,
ReplyTo = m.ReplyTo, Histories = m.Histories, SenderId =
m.SenderId, SenderName = m.SenderName, CreatedOn = m.CreatedOn
}).ToList();
But if I try to include replies to the message with:
from m in _repository.DBSet.Include("Replies").Include("Histories")
I am getting error on converting query to List with q.ToList() for Histories = m.Histories.Where(x=> x.SenderId == userId).ToList().
About your EDIT part: You cannot use ToList() in a projection, just leave it an IEnumerable<T> and convert to a List<T> when you construct the Message. You also don't need to create two list objects, you can switch from the LINQ to Entities query to LINQ to Objects (the second Select) by using AsEnumerable():
var list = (from m in _repository.DBSet
where m.MessageIdentifier == id
select new {
// ...
Histories = m.Histories.Where(x => x.SenderId == userId)
})
.AsEnumerable() // database query is executed here
.Select(m => new Message {
// ...
Histories = m.Histories.ToList(),
// ...
}).ToList();
return list;
Be aware that Include has no effect when you use a projection with select. You need to make the properties that you want to include part of the projection - as you already did with select new { Histories.....

How can I get a specific Neo4j node using Neo4jClient

Some pseudo-code of the model I'm working with:
User { int Id, string Username }
Activity { int Id, string Name }
Place { int Id, string Name }
Basically I have a bunch of Users and they belong to certain places (many to many relationship in RDBMS world). What I'd like to do now that I've created all of the nodes already is create the relationship between them. To do that I believe I need to get references to each node and then simply create the relationship between them.
Note: So far no relationships exist. It does look like in some of the examples they have added the User nodes with a relationship that points to the RootNode but I have no idea why. I'm not sure if I need to do that or not.
More pseudo-code:
var userRef = _graphClient...GetUserNodeWhereIdEquals(user.Id);
// or something like _graphClient.OutV<User>("[[id={0}]]", user.Id)
// or even just _graphClient.V<User>(id == user.Id)
var placeRef = _graphClient...GetPlaceNodeWhereIdEquals(place.Id);
_graphClient...CreateRelationshipBetween(userRef, placeRef, "belongs_to");
Unfortunately the documentation starts off pretty great then goes south when you get to relationships.
Update 3/29/12
Here's the code I have so far:
foreach (var a in _activityTasks.GetAll())
{
_graphClient.Create(a, new ActivityBelongsTo(_graphClient.RootNode));
}
foreach (var p in _placeTasks.GetAll().Take(1))
{
var placeNode = _graphClient.Create(p, new PlaceBelongsTo(_graphClient.RootNode));
foreach (var activity in p.Activities)
{
Activity activity1 = activity;
var activityNode = _graphClient.RootNode.In<Activity>(ActivityBelongsTo.TypeKey, a => a.Id == activity1.Id).SingleOrDefault();
_graphClient.CreateRelationship(placeNode, new PlaceHasActivity(activityNode.Reference));
}
}
The activity nodes are created fine. The place node is created fine. An error is now being thrown when trying to get the activityNode. It's a rather large stack trace so I'll try to paraphrase here:
Received an exception when executing the request.
The query was: g.v(p0).in(p1).filter{ it[p2] == p3
}.drop(p4).take(p5)._()
The exception was: Value cannot be null. Parameter name: key
System.ArgumentNullException: Value cannot be null.Parameter name: key
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue
value, Boolean add) ... The raw response body was: [ {
"outgoing_relationships" :
"http://localhost:7474/db/data/node/2/relationships/out", "data" : {
"Name" : "Aerobics", "Id" : 2 }, "all_typed_relationships" :
"http://localhost:7474/db/data/node/2/relationships/all/{-list|&|types}",
"traverse" :
"http://localhost:7474/db/data/node/2/traverse/{returnType}", "self"
: "http://localhost:7474/db/data/node/2", "property" :
"http://localhost:7474/db/data/node/2/properties/{key}",
"outgoing_typed_relationships" :
"http://localhost:7474/db/data/node/2/relationships/out/{-list|&|types}",
"properties" : "http://localhost:7474/db/data/node/2/properties",
"incoming_relationships" :
"http://localhost:7474/db/data/node/2/relationships/in", "extensions"
: { }, "create_relationship" :
"http://localhost:7474/db/data/node/2/relationships",
"paged_traverse" :
"http://localhost:7474/db/data/node/2/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" :
"http://localhost:7474/db/data/node/2/relationships/all",
"incoming_typed_relationships" :
"http://localhost:7474/db/data/node/2/relationships/in/{-list|&|types}"
} ]
Something to do when adding a item to a Dictionary when the key is null. Problem is, I don't see any nulls when I debug on my end, activity1 is there, RootNode is there, TypeKey is a const string.
I'm almost wondering if I should just keep the created nodes within a array or Dictionary myself and then just working with the NodeReference. That's what I'm going to try next.
Later that morning
This seems to load everything into the graph database fine:
var activityNodes = _activityTasks.GetAll().ToDictionary(a => a.Id, a => _graphClient.Create(a, new ActivityBelongsTo(_graphClient.RootNode)));
foreach (var p in _placeTasks.GetAll())
{
var placeNode = _graphClient.Create(p, new PlaceBelongsTo(_graphClient.RootNode));
foreach (var activity in p.Activities)
{
_graphClient.CreateRelationship(placeNode, new PlaceHasActivity(activityNodes[activity.Id]));
}
}
foreach (var u in _userTasks.GetAllUserGraph())
{
var userNode = _graphClient.Create(u, new UserBelongsTo(_graphClient.RootNode));
foreach(var activity in u.Activities)
{
_graphClient.CreateRelationship(userNode, new UserParticipatesIn(activityNodes[activity.Id]));
}
}
Now the problem is similar to what I had before. Now I want to get an activity that has a relationship to the RootNode:
Node<Activity> activity = _graphClient
.RootNode
.In<Activity>(ActivityBelongsTo.TypeKey, a => a.Id == 1)
.SingleOrDefault();
Throwing the key value can't be null exception again. I think I need to investigate the gremlin syntax more. I'm guessing the problem is there.
This afternoon
Started to experiment with Gremlin queries:
g.v(0).inE.filter{it.label=="ACTIVITY_BELONGS_TO"}.outV.filter{it.Id==1}.Name
works fine. I tried to replicate that using neo4jClient syntax:
_graphClient.RootNode.InE(ActivityBelongsTo.TypeKey).OutV(b => b.Id == 1).SingleOrDefault();
Same null exception, it spits out:
g.v(p0).inE.filter{ it[p1].equals(p2) }.outV.filter{ it[p3] == p4 }.drop(p5).take(p6)._()
which looks right to me, except for the end. Ran this though:
g.v(0).inE.filter{it.label=="ACTIVITY_BELONGS_TO"}.outV.filter{it.Id==1}.drop(0).take(1)._()
And that works fine. Something stinks here...maybe I should try the other library although I liked the de/serialization support. Sigh...
Thought maybe a raw query would work. Nope! This method no longer accepts a string and the required GremlinQuery I have no idea how to you. Grooooooooooooooooan.
var users = graphClient.ExecuteGetAllNodesGremlin<IsCustomer>("g.v(0).out('IsCustomer'){it.'Name' == 'BobTheBuilder'}");
Update 3/30/12
Created a new project, everything below works fine. Super confused why it will work here... :( Maybe version differences, I have no idea.
var client = new GraphClient(new Uri("http://localhost:7474/db/data"));
client.Connect();
client.Create(new User { Id = 1, Username = "joe" }, new UserBelongsTo(client.RootNode));
client.Create(new User { Id = 2, Username = "cloe" }, new UserBelongsTo(client.RootNode));
client.Create(new Activity { Id = 1, Name = "Bocce Ball" }, new ActivityBelongsTo(client.RootNode));
client.Create(new Activity { Id = 2, Name = "Programming" }, new ActivityBelongsTo(client.RootNode));
var user = client.RootNode.In<User>(UserBelongsTo.TypeKey, u=>u.Id == 1).SingleOrDefault();
var activity = client.RootNode.In<Activity>(ActivityBelongsTo.TypeKey, a=>a.Id == 1).SingleOrDefault();
client.CreateRelationship(user.Reference, new Plays(activity.Reference));
user = client.RootNode.In<User>(UserBelongsTo.TypeKey, u => u.Id == 1).SingleOrDefault();
activity = client.RootNode.In<Activity>(ActivityBelongsTo.TypeKey, a => a.Id == 1).SingleOrDefault();
I'm just getting started too. I would suggest you check out this blog:
http://romikoderbynew.com/2011/07/30/neo4jclient-primer/
Also, check http://frictionfree.org and its source code (in the about section) for more examples.
Creating relationships on existing - as I understand, this is possible. However, it appears to be easier to associate nodes as you create them. From the blog:
You can also create relationships between existing nodes.
graphClient.CreateRelationship(customerNodeReference, new
Speaks(languageNode.Reference));
RootNode - I believe you need to start a query from a node, I don't think you can do a
SELECT * FROM ... WHERE
Therefore, it would make sense that you need to attach nodes to the root node. This is an example from the FrictionFreeApp:
var node = graphClient.Create(
user,
new UserBelongsTo(rootNode));

Resources