How do I write this in Neo4jClient - c#

Thanks to Dave Bennett, I have a great Neo4j query that provides the results I need. I need to get this to work in Neo4JClient. Here's my working Neo4j query:
`// Composite Tile with related users
match (t:Tile{summary:"Test 1"})<-[:ASSIGNED_TO]-(u:User)
with {summary: t.summary,
id: t.id,
sprint_id: t.sprint_id,
size: t.size,
percent_done: t.percent_done,
color: t.color_id,
description: t.description,
queue_id: t.queue_id,
swimlane_id: t.swimlane_id,
icons: t.icons,
order: t.order,
assignees: collect(u)} as tile
RETURN collect(tile) as tiles`
Visual Studio and/or Neo4jClient is not very happy with my attempt:
var compositeTile = client.Cypher
.Match("(t:Tile)<-[:ASSIGNED_TO]-(u:User)")
.Where((Tile t)=> t.summary == tile.summary)
.With( {
summary: t.summary,
id: t.id,
sprint_id: t.sprint_id,
size: t.size,
percent_done: tile.percent_done,
color: t.color,
description: t.description,
queue_id: t.queue_id,
swimlane_id: t.swimlane_id,
icons: t.icons,
Order: t.order,
assignees: collect(u)
} as tile)
.return collect(tile) as tiles;
It just throws lots of red squiggly lines, I suspect because I'm not formatting the .with statement correctly. I haven't been able to find an example, so I'm asking here. How do I do this correctly in Neo4jClient?

So, two answers to your question - the first is that the .With statement takes a string as a parameter, so you just need to wrap your text with " (or #" if you want to keep the formatting). The second answer is more of a problem for you though:
It's not possible to do what you're trying to do with Neo4jClient - at the moment it doesn't allow you to create anonymous types. It's not able to deserialize the content. I thought it might be a quick fix, but it seems like it would be more involved. So, what we're looking at here is a change of the query.
I presume you have a class called Tile with the properties you're after. So I would change the query to something like this:
client.Cypher
.Match("(t:Tile)<-[:ASSIGNED_TO]-(u:User)")
.Where((Tile t) => t.summary == tile.summary)
.Return((t,u) => new {
Tile = t.As<Tile>(),
User = u.CollectAs<User>()
});
Which will give you a C# anonymous type with a Tile and an IEnumerable<Node<User>> elements, obviously you want the Users in your Tile, so you can then parse the result:
var tiles = new List<Tile>();
foreach (var result in results)
{
var tile = result.Tile;
foreach (var user in result.Users)
tile.users.Add(user.Data);
tiles.Add(tile);
}
One thing you might find is that you need to initialize the Users collection, so either add Users = new List<User>() to your Tile constructor, or just before parsing the data.

Related

Iterating through a list, but can't access distinct item values

I am building invoices with PDFsharp.
This is my SQL table (tblTripsPerMonth):
I've bound it to an ObservableCollection (PaidTrips).
The goal is to create one (1) PDF for each distinct "CompanyName" in the LicenseHolderID column.
To achieve this, I convert to a List, and group by LicenseHolderID.
var paidTrips = PaidTrips
.GroupBy(p => new {
p.LicenseHolderID
})
.ToList();
After that, I iterate over the list with a foreach-loop:
foreach (var trip in paidTrips) {
// I grab the distinct name
string licenseholder = trip.Key.LicenseHolderID.ToString();
// I summarize many of the columns
decimal totalPayment = trip.Sum(x => x.Payment);
decimal totalPaymentNet = trip.Sum(x => x.Payment);
decimal totalOrderFee = trip.Sum(x => x.Payment);
decimal totalPaymentFee = trip.Sum(x => x.Payment);
// I grab the first value of some other columns, which won't change
string licenseholderInvoiceID = trip.Select(x => x.LicenseHolderInvoiceID).FirstOrDefault().ToString();
string ridelRegionInvoiceID = trip.Select(x => x.RidelRegionInvoiceID ).FirstOrDefault().ToString();
// Creating PDF document using PDFsharp:
// PDFsharp code
// PDFsharp code
// PDFsharp code
}
I am able to create a PDF-document, one for each distinct LicenseHolderID, by adding the licenseHolderID-string to the file-path of PDFsharps document.Save().
But I am not able to fill the PDF-document with all the information I need.
I have the summarized amounts - good, because I absolutely need to display the full invoice amount.
But -- I also need to go into detail. I do not have amounts per VehicleID.
For "CompanyName1", that'd be AG4203000002 and AG4203000003, with their corresponding row data.
I did do this...
IEnumerable<string> vehicleIds = trip
.Select(x => x.VehicleID)
.Distinct()
.ToArray();
string vehicle = string.Join(", ", vehicleIds);
...in order to separate the VehicleID distinct values from eachother.
I then put the string vehicle into PDFsharps way of drawing text onto the PDF (DrawString):
gfx.DrawString(vehicle, /* font and color customization */);
Which gives me the correct amount of VehicleID for each LicenseHolderID, but in one, long string... Not optimal.
Which brings me to my question: A): I need to bring along the row data of the other columns pertaining to the distinct vehicles in VehicleID, so that I can fill in details in my PDFs, and not just the summarized values, and B): if the solution to A) involves getting rid of the long, hacky string (vehicle), that'd be optimal, if not - that's okay too.
UPDATE (adding code from #Dai's answer):
// PaidTrip = My holder class, which I've bound to a ObservableCollection (PaidTrips)
var paidTrips = PaidTrips.ToList(); // I was unsure about this one
IEnumerable<IGrouping<String, PaidTrip>> tripsGroupedByCompany = paidTrips.GroupBy(pt => pt.LicenseHolderID);
foreach (IGrouping<String, PaidTrip>> companyGroup in tripsGroupedByCompany) {
string licenseHolderId = companyGroup.Key;
gfx.DrawString(/* code goes foo */);
// I tried adding Key here, but that gave me a squiggly under "t.VehicleID"
var groupedByVehicle = companyGroup.GroupBy(t => t.VehicleID);
foreach (IGrouping<String, PaidTrip> vehicleGroup in groupedByVehicle) {
// This is where I get a red squiggly; under Key
String vehicleId = groupedByVehicle.Key;
gfx.DrawString(/* code goes foo */);
foreach (PaidTrip trip in vehicleGroup) {
gfx.DrawString(/* code goes foo */);
}
}
}
This is the error:
CS1061: IEnumerable<IGrouping<string, PaidTrip>> does not contain a definition for 'Key' and no accessible extension method 'Key' accepting a first argument of type 'IEnumerable<IGrouping<string, PaidTrip>>' could be found (are you missing a using directive or an assembly reference?)
I need to bring along the row data of the other columns pertaining to the distinct vehicles in VehicleId, so that I can fill in details in my PDFs, and not just the summarized values.
If I understand you correctly, for each company (LicenseHolderId) you want their relevant Trip objects, but grouped by VehicleId - that's straightforward, just add another GroupBy - and you can iterate over them in an inner foreach:
List<Trip> paidTrips = ...
IEnumerable< IGrouping<String,Trip> > tripsGroupedByCompany = paidTrips.GroupBy( pt => pt.LicenseHolderId );
foreach( IGrouping<String,Trip> companyGroup in tripsGroupedByCompany )
{
String licenseHolderId = companyGroup.Key;
gfx.DrawString( "Company: " + licenseHolderId + "\r\n" );
var groupedByVehicle = companyGroup.GroupBy( t => t.VehicleId );
foreach( IGrouping<String,Trip> vehicleGroup in groupedByVehicle )
{
String vehicleId = vehicleGroup.Key;
gfx.DrawString( "\tVehicle: " + vehicleId + "\r\n" );
foreach( Trip trip in vehicleGroup )
{
gfx.DrawString( $"\t\tTrip: {trip.Year}-{trip.Month:00}. {trip.PaymentNet,11:C2}\r\n" );
}
}
}
I added some formatting instructions and characters which only really apply to text-mode (console applications), not PDF rendering, but if you're curious:
I used tab characters (\t) to indicate indent so related data is visually grouped.
I used formatting specifier :00 to ensure the Month value is displayed as a 2-digit value with a leading zero.
I used formatting specifier ,11:C2 to ensure the PaymentNet value is formatted as a Currency value with 2 decimal places, and is always left-padded to at-least 11 characters width.
This will give you output like this (below).
Note that while in your source-data each VehicleId has only a single trip associated with it, my code above allows a single VehicleId to have multiple trips with both the same, and different, LicenseHolderId values, though the sample print-out below only shows 1 trip per Vehicle.
Company: CompanyName1
Vehicle: AG4203000002
Trip: 2021-07. $107,088.68
Vehicle: AG4203000003
Trip: 2021-07. $138,761.32
Company: CompanyName2:
Vehicle: AG4203000004
Trip: 2021-07. $129,264.15
Vehicle: AG4203000005
Trip: 2021-07. $87,273.58
But note that this code above is bad because it crosses concerns (it does 2 separate things: it traverses a non-trivial object-graph, and it renders data to your PDF library).
A better design would separate out the graph-traversal (perhaps to a single extension method) thus making the PDF rendering code much simpler, however I cannot give you any code examples of this without knowing more about your database design and if/how you're using EF.

How to set List child element with for each If it is empty initially?

I have Ilist to get all Offer from repository using entity framework core. Also I have service model OfferResponseModel which includes
OfferRequestModel as reference. I used mapster to bind entity model to service model. However it only set first child. Now I want to bind it manually. I created "offers" with the size of "Offer". When I try to use foreach loop, I cannot set "offers" child element.Because it has no elements. So, I can I solve this.
var offer = await _unitOfWork.Offers.GetAllOffer();
if (offer == null)
throw ServiceExceptions.OfferNotFound;
var results = new List<OfferResponseModel>(offer.Count);
results.ForEach(c => { c.Offer = new OfferRequestModel(); });
int i = 0;
foreach(var result in results)
{
result.Offer.User = Offer[i].User.Adapt<UserResponseModel>();
result.Offer.Responsible = Offer[i].Responsible.Adapt<EmployeeResponseModel>();
result.CreatedDate = Offer[i].CreatedDate;
result.ModifiedBy = Guid.Parse(Offer[i].UpdatedBy);
result.Active = Offer[i].Status;
result.Offer = Offer[i].Offer;
result.Offer.User.Company = Offer[i].Company.Adapt<CompanyModel>();
i++;
}
I created "offers" with the size of "Offer".
No, you created it with that capacity. It's still an empty list. It's not clear to me why you're trying to take this approach at all - it looks like you want one OfferResponseModel for each entry in offer, directly from that - which you can do with a single LINQ query. (I'm assuming that offer and Offer are equivalent here.)
var results = Offer.Select(o => new OfferResponseModel
{
Offer = new OfferRequestModel
{
User = o.User.Adapt<UserResponseModel>(),
Responsible = o.Responsible.Adapt<EmployeeResponseModel>()
},
CreatedDate = o.CreatedDate,
ModifiedBy = Guid.Parse(o.UpdatedBy),
Active = o.Status
}).ToList();
That doesn't set the Offer.User.Company in each entry, but your original code is odd as it sets the User and Responsible properties in the original Offer property, and then replaces the Offer with Offer[i].Offer. (Aside from anything else, I'd suggest trying to use the term "offer" less frequently - just changing the plural to "offers" would help.)
I suspect that with the approach I've outlined above, you'll be able to work out what you want and express it more clearly anyway. You definitely don't need to take the "multiple loops" approach of your original code.
One thing you have left out is the type of the offer variable that is referenced in the code. But I am thinking you need to do something along these lines:
if (offer == null)
throw ServiceExceptions.OfferNotFound;
var results = offer.Select(o => new OfferResponseModel
{
Offer = new OfferRequestModel
{
User = o.User.Adapt<UserResponseModel>(),
Responsible = o.Responsible.Adapt<EmployeeResponseModel>(),
...
}
}).ToList();
Select basically loops through any items in offer and "converts" them to other objects, in this case OfferResponseModel. So inside select you simply new up an OfferResponseModel and directly sets all the properties you need to set.
You need using System.Linq; for Select to be available.

Optional Random ordered nested grouping in linq

I am really in a position where I can't think of answer regarding optional grouping in linq.
Basically,I want to generate report which comes from a screen having filters.
These filters(mostly grouped) are optional and can be rearranged.It's something like
Filters: 1.Clients Projects Tasks Duration
or
2.Projects Clients Tasks Duration
or
3.Task Duration etc.
with all possible combinations.
Then data should look like
1.ClientA
ProjectA
TaskA
26hrs 45mins
TaskB
43hrs 23mins
ProjectB
TaskX......
2.ProjectA
ClientA
TaskA
26hrs 45mins...
3.TaskA
26hrs 45mins
TaskB
6hrs 35mins
I have data.But unable to write logic which is generalized.
I am thinking with some enum which will hold filters (viewmodel)selected like
enum.Client,enum.Project... and
if (clientGroupChecked) then
foreach(var clientGroup in list){
//group list by client here
if(projectGroupChecked) then
foreach(var projectGroup in clientGroup){
//group list by project here
}
}
I know,it's wrong.This way I have to put logic for all the combinations possible.
Couldn't think of anything else.I want it really to be generalized because it may have more filters added in future and I don't want to change entire logic just for extra filters(Of course,I want to add new filter group somewhere in the logic.But I want it to be more easy to maintain also.
Edited:#sschimmel :My point is grouping can be shuffled(for this I have buttons[selected -->green and unselected-->gray and these buttons are movable for grouping].So when writing linq logic,how can I know on what criteria I have to group in particular way? For ex: I have columns A B C D E F.In this, I can just choose to group by A or by A B or B A or ACB....etc. with all possible combinations.How to achieve this?I don't want if else check because we have many possibilities.If one more filter is added,it would have many more possibilities. That's why I am thinking for need of general approach to do this.
Edit 2:
Please find attachment and how I am trying below.
//for the following ,I need some way of writing properly passing right values
var reportGroupingCP = (from t in TaskEntries
group t by new { clientId,projectId } into g
select new
{
ClientId = g.Key.clientId,
ProjectId = g.Key.projectId,
Result = (Type)g //What could be T
}).ToList();
var reportGroupingCE = (from t in TaskEntries
group t by new { clientId,employeeId } into g
select new
{
ClientId = g.Key.clientId,
EmployeeId = g.Key.employeeId,
Result = (Type)g //What could be T
}).ToList();
//Can't use above if there is filter only for client.similarly for other cases/I don't want to write for each one.I need way to do this dynamically.May be by passing enum or adding ids to some class or something else
Filter 1
Filter 2
If I understood your question correctly, you wan't to do group your data dynamically on multiple properties.
The easiest solution would be to use Dynamic LINQ which lets you create queries from strings you can easily compose from user inputs.
// userSelections is created dynamically from what the user selected.
var userSelections = new[] { "clientId", "projectId" };
var propertiesToGroupBy = string.Join(", ", userSelections);
var groupedData = data.GroupBy("new(" + propertiesToGroupBy + ")");
It's not type safe nor checked during compile time, but fairly easy to use and solves your problem. It's also documented nicely.
I tried to come up with a solution that dynamically combines Expression<Func<TaskEntry, object>>s but got stuck on creating the Expression that ìnstantiates the anonymous type you would use inside the GroupBy(new { ... }). Because the number of selected properties to group by is not known during compile time, it's not possible to create the anonymous type.

Empty rows insertion in Many-to-One and Many-to-Many relationships in GraphDiff

I am using GraphDiff, along with the latest version of the Entity Framework, following the code-first approach.
I am trying to update a Food entity this way:
public void Update(Food food)
{
using (var db = new DatabaseMappingContext())
{
food = db.UpdateGraph(food, map => map.OwnedEntity(f => f.FoodRecipe, withRecipe => withRecipe.
OwnedCollection(r => r.RecipeSteps, withRecipeStep => withRecipeStep.
OwnedCollection(rs => rs.StartObjectSlots, withStartObjectSlots => withStartObjectSlots.
AssociatedEntity(sos => sos.BelongingRecipeStepAsStart)
).
OwnedCollection(rs => rs.EndObjectSlots, withEndObjectSlots => withEndObjectSlots.
AssociatedEntity(eos => eos.BelongingRecipeStepAsEnd)
).
AssociatedEntity(rs => rs.ActionOfUser)
).
AssociatedCollection(r => r.InteractiveObjects)
).
AssociatedCollection(f => f.FoodVarieties));
//....
db.SaveChanges();
}
}
StartObjectSlots and EndObjectSlots are 2 lists containing some other, irrelevant data.
The InteractiveObjects contains objects of a InteractiveObject type, which is the base type for a number of object types that can be put there. One of those derived types (let's say IntObjDerived has a One-to-Many property).
Now, I am trying to update the following entity this way:
ServerAdapter sa = new ServerAdapter();
//Loading a food from DB.
Food food = sa.LoadAllFoods().First();
RecipeStep rs = new RecipeStep();
rs.Name = "This is a test recipe step";
//Adding a User Action from the database.
rs.ActionOfUser = sa.LoadAllUserActions().First();
//....
//Add the step in the recipe
food.FoodRecipe.RecipeSteps.Add(rs);
//Update the food.
sa.Update(food);
Now, when the code is executed, a new empty ActionOfUser entity is inserted into the database. Additionally, a new empty entity is inserted for each of the one-to-many navigation properties of the entities mentioned above three new recipes are inserted in the database, one of empty data, one half filled and this one supposed to be saved. Both situations are unwanted, and I am trying to find the solution. I experimented with some changes, but I have stuck with this. Any suggestions?
(I know that this seems to be 2 questions, but I thought to put it as one as it might be relevant-same problem in database).
EDIT: I downloaded and compiled GraphDiff in order to inspect what is going on, and I noticed the creation of some objects that are empty except from their Entity ID value.
I guess that those side effects are caused because practically I add a new node to the object graph (a new RecipeStep) and I am not sure if graphdiff fully supports this.
UPDATE (tl; dr version): I tried to apply a UpdateGraph call using Entity Framework's GraphDiff of an object with graph depth greater than 2.
By what I have tried, it seems that GraphDiff is applying double insertions in graphs of depth greater than 2 and it takes a lots of time, especially if a new node is added with subnodes loaded from the database. Should I follow a different approach, for example split the UpdateGraph call into multiple calls?
Thank you in advance!
What I finally applied as a workaround, was to perform the update operation by splitting it into multiple UpdateGraph calls with graph depth less than or equal to 2 and apply manually any sub-node addition to the graph:
//Update food in total graph depth <= 2.
db.UpdateGraph(food, map => map.AssociatedCollection(f => f.FoodVarieties));
//.... (Other UpdateGraph calls with graph depth <=2)
//Update recipe steps of recipe in total graph depth <= 2.
foreach (RecipeStep recipeStep in food.FoodRecipe.RecipeSteps)
{
recipeStep.ActionOfUser = db.UserActions.FirstOrDefault(ua => ua.EntityID == recipeStep.ActionOfUser.EntityID);
//If you have to do an inner node adding operation in the graph, do it manually.
if (recipeStep.EntityID == 0)
{
recipeStep.BelongingRecipe = db.Recipes.FirstOrDefault(r => r.EntityID == food.FoodRecipe.EntityID);
db.RecipeSteps.Add(recipeStep);
}
else
{
//Map slots & recipeSteps applied manually here.
recipeStep.StartObjectSlots.ForEach(sos => sos.BelongingRecipeStepAsStart = recipeStep);
recipeStep.EndObjectSlots.ForEach(eos => eos.BelongingRecipeStepAsEnd = recipeStep);
db.UpdateGraph(recipeStep, map => map.OwnedCollection(rs => rs.InteractiveObjectInstancesLists, withIOILists => withIOILists.
OwnedCollection(ioil => ioil.InteractiveObjectsInstances)
).
OwnedCollection(rs => rs.StartObjectSlots, withStartObjectSlots => withStartObjectSlots.
AssociatedEntity(sos => sos.BelongingRecipeStepAsStart)
).
OwnedCollection(rs => rs.EndObjectSlots, withEndObjectSlots => withEndObjectSlots.
AssociatedEntity(eos => eos.BelongingRecipeStepAsEnd)
).
AssociatedEntity(rs => rs.ActionOfUser)
);
}
}
Also, I noticed that the object's graph update was completed much faster than before. These might be indications of something going wrong in GraphDiff complex graph (>2 depth) updating process (or at least I was doing something terribly wrong).

LINQ to SharePoint 2010 getting error "All new entities within an object graph must be added/attached before changes are submitted."

I've been having a problem for some time, and I've exhausted all means of figuring this out for myself.
I have 2 lists in a MS Sharepoint 2010 environment that are holding personal physician data for a medical group...nothing special just mainly text fields and a few lookup choice fields.
I am trying to write a program that will migrate the data over from List A to List B. I am using LINQ to Sharepoint to accomplish this. Everything compiles just fine, but when it runs and hits the SubmitChanges() method, I get a runtime error that states:
"All new entities within an object graph must be added/attached before changes are submitted."
this issue must be outside of my realm of C# knowledge because I simply cannot find the solution for it. The problem is DEFINITELY stemming from the fact that some of the columns are of type "Lookup", because when I create a new "Physician" entity in my LINQ query, if I comment out the fields that deal with the lookup columns, everything runs perfectly.
With the lookup columns included, if I debug and hit breakpoints before the SubmitChanges() method, I can look at the new "Physician" entities created from the old list and the fields, including data from the lookup columns, looks good, the data is in there the way I want it to be, it just flakes out whenever it tries to actually update the new list with the new entities.
I have tried several methods of working around this error, all to no avail. In particular, I have tried created a brand new EntityList list and calling the Attach() method after each new "Physician" Entity is created, but to no avail, it just sends me around in a bunch of circles, chasing other errors such as "ID cannot be null", "Cannot insert entities that have been deleted" etc.,
I am no farther now than when I first got this error and any help that anyone can offer would certainly be appreciated.
Here is my code:
using (ProviderDataContext ctx = new ProviderDataContext("http://dev"))
{
SPSite sitecollection = new SPSite("http://dev");
SPWeb web = sitecollection.OpenWeb();
SPList theOldList = web.Lists.TryGetList("OldList_Physicians");
//Create new Physician entities.
foreach(SPListItem l in theOldList.Items)
{
PhysiciansItem p = new PhysiciansItem()
{
FirstName = (String)l["First Name"],
Title = (String)l["Last Name"],
MiddleInitial = (String)l["Middle Init"],
ProviderNumber = Convert.ToInt32(l["Provider No"]),
Gender = ConvertGender(l),
UndergraduateSchool =(String)l["UG_School"],
MedicalSchool = (String)l["Med_School"],
Residency = (String)l["Residency"],
Fellowship = (String)l["Fellowship"],
Internship = (String)l["Internship"],
PhysicianType = ConvertToPhysiciantype(l),
Specialty = ConvertSpecialties(l),
InsurancesAccepted = ConvertInsurance(l),
};
ctx.Physicians.InsertOnSubmit(p);
}
ctx.SubmitChanges(); //this is where it flakes out
}
}
//Theses are conversion functions that I wrote to convert the data from the old list to the new lookup columns.
private Gender ConvertGender(SPListItem l)
{
Gender g = new Gender();
if ((String)l["Sex"] == "M")
{
g = Gender.M;
}
else g = Gender.F;
return g;
}
//Process and convert the 'Physician Type', namely the distinction between MD (Medical Doctor) and
//DO (Doctor of Osteopathic Medicine). State Regualtions require this information to be attached
//to a physician's profile.
private ProviderTypesItem ConvertToPhysiciantype(SPListItem l)
{
ProviderTypesItem p = new ProviderTypesItem();
p.Title = (String)l["Provider_Title:Title"];
p.Intials = (String)l["Provider_Title"];
return p;
}
//Process and convert current Specialty and SubSpecialty data into the single multi-choice lookup column
private EntitySet<Item> ConvertSpecialties(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
Item i = new Item();
i.Title = (String)l["Provider Specialty"];
theEntityList.Add(i);
if ((String)l["Provider SubSpecialty"] != null)
{
Item theSubSpecialty = new Item();
theSubSpecialty.Title = (String)l["Provider SubSpecialty"];
theEntityList.Add(theSubSpecialty);
}
return theEntityList;
}
//Process and add insurance accepted.
//Note this is a conversion from 3 boolean columns in the SP Environment to a multi-select enabled checkbox
//list.
private EntitySet<Item> ConvertInsurance(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
if ((bool)l["TennCare"] == true)
{
Item TenncareItem = new Item();
TenncareItem.Title = "TennCare";
theEntityList.Add(TenncareItem);
}
if ((bool)l["Medicare"] == true)
{
Item MedicareItem = new Item();
MedicareItem.Title = "Medicare";
theEntityList.Add(MedicareItem);
}
if ((bool)l["Commercial"] == true)
{
Item CommercialItem = new Item();
CommercialItem.Title = "Commercial";
theEntityList.Add(CommercialItem);
}
return theEntityList;
}
}
So this may not be the answer you're looking for, but it's what's worked for me in the past. I've found that updating lookup fields using Linq to Sharepoint to be quite frustrating. It frequently doesn't work, or doesn't work efficiently (forcing me to query an item by ID just to set the lookup value).
You can set up the entity so that it has an int property for the lookup id (for each lookup field) and a string property for the lookup value. If, when you generate the entities using SPMetal, you don't generate the list that is being looked up then it will do this on it's own. What I like to do is (using your entity as an example)
Generate the entity for just that one list (Physicians) in some temporary folder
Pull out the properties for lookup id & value (there will also be private backing fields that need to come along for the ride too) for each of the lookups (or the ones that I'm interested in)
Create a partial class file for Physicians in my actual project file, so that regenerating the entire SPMetal file normally (without restricting to just that list) doesn't overwrite changes
Paste the lookup id & value properties in this partial Physicians class.
Now you will have 3 properties for each lookup field. For example, for PhysicianType there will be:
PhysicianType, which is the one that is currently there. This is great when querying data, as you can perform joins and such very easily.
PhysicianTypeId which can be occasionally useful for queries if you only need ID as it makes it a bit simpler, but mostly I use it whenever setting the value. To set a lookup field you only need to set the ID. This is easy, and has a good track record of actually working (correctly) in my experiences.
PhysicianTypeValue which could be useful when performing queries if you just need the lookup value, as a string (meaning it will be the raw value, rather than something which is already parsed if it's a multivalued field, or a user field, etc. Sometimes I'd rather parse it myself, or maybe just see what the underlying value is when doing development. Even if you don't use it and use the first property, I often bring it along for the ride since I'm already doing most of the work to bring the PhysicianTypeId field over.
It seems a bit hacky, and contrary to the general design of linq-to-SharePoint. I agree, but it also has the advantage of actually working, and not actually being all that hard (once you get the rhythm of it down and learn what exactly needs to be copied over to move the properties from one file to another).

Categories