I'm trying to retrieve only the attributes for a certain entity, that have changed metadata since the last metadataquery - for example: if a user changes the requirement on a certain field of a certain entity, and saves and publishes this change, I want a plugin that fires on message Publish & PublishAll to let me know what attribute and which metadata of that attribute has changed.
This is the code I have so far, based on this example on MSDN: https://msdn.microsoft.com/en-us/library/jj863605.aspx?f=255&MSPPError=-2147217396
I get the attributes for the three entities that are listed in includedEntities, so no problem there.
I get values for RequiredLevel and IsValidForAdvancedSearch, the two attribute properties listed in attributeProperties and the ones I want to watch while the rest returns null, so again, no problem here.
The attributeFilter also does what it's supposed to do: I only get datafields (attributes that do not describe another attribute), so once again: no problem.
The clientversionstamp I pass on is retrieved from a configurationparameter I created, which I update after every query. By watching it during debug, I know that it's the correct value - so I'm quite sure that's not the problem either.
So what is the problem? For each entity, I still have some (about half) of the attributes that are added to the collection of changed attributes in the response, although I didn't change anything.
If I do change something in metadata, that attribute does get added to the response collection as well, so my code does pick up the change. However, I still get a lot more data than I want - the goal is to only get that one attribute that has changed. What am I missing?
MetadataFilterExpression EntityFilter = new MetadataFilterExpression(LogicalOperator.And);
EntityFilter.Conditions.Add(new MetadataConditionExpression("LogicalName", MetadataConditionOperator.In, includedEntities));
MetadataPropertiesExpression EntityProperties = new MetadataPropertiesExpression()
{
AllProperties = false
};
EntityProperties.PropertyNames.AddRange(new string[] { "Attributes" });
MetadataConditionExpression optionsetAttributeName = new MetadataConditionExpression("AttributeOf", MetadataConditionOperator.Equals, null);
MetadataFilterExpression AttributeFilter = new MetadataFilterExpression(LogicalOperator.And);
AttributeFilter.Conditions.Add(optionsetAttributeName);
MetadataPropertiesExpression AttributeProperties = new MetadataPropertiesExpression() { AllProperties = false };
foreach (string attrProperty in attributeProperties)
{
AttributeProperties.PropertyNames.Add(attrProperty);
}
EntityQueryExpression entityQueryExpression = new EntityQueryExpression()
{
Criteria = EntityFilter,
Properties = EntityProperties,
AttributeQuery = new AttributeQueryExpression()
{
Properties = AttributeProperties,
Criteria = AttributeFilter
}
};
RetrieveMetadataChangesRequest req = new RetrieveMetadataChangesRequest()
{
Query = entityQueryExpression,
ClientVersionStamp = clientVersionStamp
};
return (RetrieveMetadataChangesResponse)service.Execute(req);
Related
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.
I am new to Dynamics CRM development. I want to batch update certain fields in Entity using Batch update method in Dynamics CRM Online.
I am using below code for performing batch update:
var multipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = false,
ReturnResponses = true
},
Requests = new OrganizationRequestCollection()
};
foreach (var entity in entities.Entities)
{
UpdateRequest updateRequest = new UpdateRequest { Target = entity };
multipleRequest.Requests.Add(updateRequest);
}
ExecuteMultipleResponse multipleResponse = (ExecuteMultipleResponse)service.Execute(multipleRequest);
How can I specify only fields which I want to update instead of entire entity being updated?
Note: I have around 200,000 records to update using the above code. Currently it takes around 1.5 minute to update a single batch of 1000 records. So was thinking a way to update only required fields.
My recommended approach is to create a new Entity() object for the update. This way your update code doesn't need to worry about what fields were retrieved, it just takes the ones it cares about updating.
foreach (var entity in entities.Entities)
{
var newEntity = new Entity(entity.LogicalName, entity.Id);
//Populate whatever fields you want (this is just an example)
newEntity["new_somefield"] = entity.GetAttributeValue<string>("new_somefield").ToUpper();
UpdateRequest updateRequest = new UpdateRequest { Target = newEntity };
multipleRequest.Requests.Add(updateRequest);
}
You have to look at the way how the EntityCollection entities is filled up. If retrieving using RetrieveMultiple, then Pull the minimal fields may be the native Name field & PK Id field will come by default. This way not the whole entity will be updated back.
Avoid using AllColumns = true. Use ColumnSet to get minimal fields needed for validation.
ColumnSet = new ColumnSet("field_needed"),
Next, assign only the necessary fields like below inside loop.
foreach (var entity in entities.Entities)
{
UpdateRequest updateRequest = new UpdateRequest { Target = entity };
entity.Attributes["field_to_update"] = "field_value";
multipleRequest.Requests.Add(updateRequest);
}
My answer will help you to understand what went wrong & correcting it. Like Nicknow said, you can assign fresh entity to solve issue.
I have this code inside my HTTP PUT Method;
var toUpdate = context.MyTableData.SingleOrDefault(a => a.id == someId);
id = data.id,
name = data.name,
age = data.age,
JObject otherData = (JObject)data;
JToken value;
if(otherData.TryGetValue("children", out value)){
var someIds = (JArray)value;
foreach (var someId in someIds){
toUpdate.MyChildrenTable.Add(new MyChildrenTable{ //-- This line right here
id = data.id,
name = data.name,
age = data.age,
}
}
}
Based on the code, I only wanted to update to update my data. However, using Add, this gives me another set of data (which is, obviously not updating the data). How I may able to modify my code? I tried using Attach but no good. Is there any way like Attach or something to have my code corrected?
To avoid confusions, all of the code are working. I only wanted to modify it in order to have it's flow Update my data instead of Adding another set of data. MyTableData is updating, but the MyChildrenTable is not, since it's on Add method.
I can't figure this one out. I'm creating a new ElasticSearch index using the ElasticProperty attributes/decorators. Here's how I create the index:
client = ClientFactory(indexName);
if (!client.IndexExists(indexName).Exists)
{
var set = new IndexSettings() { NumberOfShards = 1 };
var an = new CustomAnalyzer() { Tokenizer = "standard" };
an.Filter = new List<string>();
an.Filter.Add("standard");
an.Filter.Add("lowercase");
an.Filter.Add("stop");
an.Filter.Add("asciifolding");
set.Analysis.Analyzers.Add("nospecialchars", an);
client.CreateIndex(c => c
.Index(indexName)
.InitializeUsing(set)
.AddMapping<ItemSearchable>(m => m.MapFromAttributes())
);
}
And all of my fields are being created properly from the attributes specified on the class, except these two. They are C# enumerations, which is maybe part of the problem. I'm trying to save them in the index as numeric fields...
[Required, ElasticProperty(Index = FieldIndexOption.NotAnalyzed, Store = true, NumericType = NumberType.Short, IncludeInAll = false)]
public Enums.PlatformType PlatformType { get; set; }
[ElasticProperty(Index = FieldIndexOption.NotAnalyzed, Store = true, NumericType = NumberType.Short, OmitNorms = true, IncludeInAll = false)]
public Enums.ItemType ItemType { get; set; }
When I set up the index and check via Kibana, I don't see PlatformType or ItemType at all in the list of fields in the empty index.
When I insert a record, I can see the values in the source (JSON) as numbers (as expected), but the "Fields" are not there.
So I'm thinking it's either because they're C# enum type, or because I'm trying to store it as a number. But I'm stumped on why Elasticsearch is skipping these fields. Many thanks for any ideas you may have.
UPDATE 1 ... The search still works (even without those fields being shown in the Fields section). I'm thinking it might just be a Kibana problem. In the Table view, it shows my two fields like this...
and hovering over those triangle exclamation marks says "No cache mapping for this field. Refresh your mapping from the Settings > Indices page". But of course I can't find such a page within Kibana.
So I might be fine behind the scenes and this is a non-issue. Does anyone else have any insight on what might fix this, make it clearer, or whether this is known behaviour and I should just move on? Thanks.
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).