How can I edit the Keywords field in SharePoint using c#? - c#

I can edit other fields like Title and even my own created fields, but I cant figure out how to add in Keywords to a document I upload. This is what I have so far. The error I get says that the Keywords column does not exist. The Keywords fields type is Managed Metadata. Here is a image of where I want to add keywords into Here is a image of where I want to add keywords into
Update: Here is the updated code of what worked
static void AddMetaData(ClientContext ctx, string fName, string kWords )
{
List list = ctx.Web.Lists.GetByTitle("Documents");
ListItemCollection items = list.GetItems(CamlQuery.CreateAllItemsQuery());
ctx.Load(items); // loading all the fields
ctx.ExecuteQuery();
TaxonomySession session = TaxonomySession.GetTaxonomySession(ctx);
var store = session.GetDefaultKeywordsTermStore();
var terms = store.KeywordsTermSet.GetAllTerms();
ctx.Load(store, i => i.DefaultLanguage);
ctx.ExecuteQuery();
var collection = new System.Collections.ObjectModel.Collection<Term>();
var keywords = kWords.Split(';');
foreach (var key in keywords)
{
var filtered = ctx.LoadQuery(terms.Where(t => t.Name == key));
ctx.ExecuteQuery();
var term = filtered.SingleOrDefault();
if (term != null)
collection.Add(term);
}
foreach (var item in items)
{
var taxonomyField = ctx.CastTo<TaxonomyField>(list.Fields.GetByInternalNameOrTitle("Keywords"));
ctx.Load(taxonomyField);
ctx.ExecuteQuery();
taxonomyField.SetFieldValueByCollection(item, collection, store.DefaultLanguage);
item.Update();
ctx.ExecuteQuery();
}
}

It's a taxonomy field so first you have to get your keywords from the managed metadata store:
TaxonomySession session = TaxonomySession.GetTaxonomySession(context);
var store = session.GetDefaultKeywordsTermStore();
var terms = store.KeywordsTermSet.GetAllTerms();
context.Load(store, i => i.DefaultLanguage);
context.ExecuteQuery();
var collection = new System.Collections.ObjectModel.Collection<Term>();
var keywords = new string[] { "Key1", "Key2" };
foreach (var key in keywords)
{
var filtered = context.LoadQuery(terms.Where(t => t.Name == key));
context.ExecuteQuery();
var term = filtered.SingleOrDefault();
if (term != null)
collection.Add(term);
}
Then set field using method contained in TaxonomyField class:
var taxonomyField = context.CastTo<TaxonomyField>(list.Fields.GetByInternalNameOrTitle("Keywords"));
context.Load(taxonomyField);
context.ExecuteQuery();
taxonomyField.SetFieldValueByCollection(item, collection, store.DefaultLanguage);
item.Update();
context.ExecuteQuery();

Related

Delete duplicate elements from list by Identifier string (C#)

I have method to add elements to list
Here is code
public static List<InputDevice> GetAudioInputDevices()
{
var inputs = new List<InputDevice>();
var enumerator = new MMDeviceEnumerator();
var devicesAudio = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
foreach (var device in devicesAudio)
{
inputs.Add(new InputDevice()
{
Name = device.FriendlyName,
Status = device.State.ToString(),
DeviceId = device.ID,
Identifier = device.FriendlyName.Replace(" ", "").ToUpper()
});
}
return inputs;
}
But sometimes I can have duplicates in Identifier
How I can return list without duplicates on return?
There's a few ways to accomplish this, you could just skip the Adding of the item if it's already in the list:
foreach (var device in devicesAudio)
{
string identifier = device.FriendlyName.Replace(" ", "").ToUpper();
if (inputs.Any(input => input.Identifier == identifier))
continue;
inputs.Add(new InputDevice()
{
Name = device.FriendlyName,
Status = device.State.ToString(),
DeviceId = device.ID,
Identifier = identifier
});
}
Or you could group the list by the identifier after the foreach, something like this:
inputs = inputs.GroupBy(i => i.Identifier)
.Select(i => new InputDevice()
{
Identifier = i.Key,
Status = i.First().Status,
DeviceId = i.First().DeviceId,
Name = i.First().Name
}).ToList();
It really depends on what you need to do with the duplicated ones.
Hope it helps!
To make it faster you can use HashSet (complexity of Contains for HashSet is o(1)) and ask on each loop whether there already is a specific identifier in inputs List.
public static List<InputDevice> GetAudioInputDevices()
{
var inputs = new List<InputDevice>();
var enumerator = new MMDeviceEnumerator();
var devicesAudio = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
var usedIdentifiers = new HashSet<string>();
foreach (var device in devicesAudio)
{
var identifier = device.FriendlyName.Replace(" ", "").ToUpper();
if (usedIdentifiers.Contains(identifier))
continue;
inputs.Add(new InputDevice()
{
Name = device.FriendlyName,
Status = device.State.ToString(),
DeviceId = device.ID,
Identifier = identifier
});
usedIdentifiers.Add(identifier);
}
return inputs;
}
The best way, I thing is this
public static List<InputDevice> GetAudioInputDevices()
{
var inputs = new List<InputDevice>();
var enumerator = new MMDeviceEnumerator();
var devicesAudio = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
inputs = devicesAudio.GroupBy(d => d.FriendlyName.Replace(" ", "").ToUpper()).Select(g => g.First())
.Select(d => new InputDevice()
{
Name = d.FriendlyName,
Status = d.State.ToString(),
DeviceId = d.ID,
Identifier = d.FriendlyName.Replace(" ", "").ToUpper()
}).ToList();
return inputs;
}
Check in website information about HashSet.

How to add distinct value in database using Entity Framework

IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
}
}
db.SaveChanges();
I want to add only distinct values to database in above code. Kindly help me how to do it as I am not able to find any solution.
IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
var a = db.WebsiteWebPages.Where(i => i.WebPage == value.WebPage.ToString()).ToList();
if (a.Count == 0)
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
db.SaveChanges();
}
}
}
This is the code that I used to add distinct data.I hope it helps
In addition to the code sample Furkan Öztürk supplied, Make sure your DB has a constraint so that you cannot enter duplicate values in the column. Belt and braces approach.
I assume that by "distinct values" you mean "distinct value.WebPage values":
// get existing values (if you ever need this)
var existingWebPages = db.WebsiteWebPages.Select(v => v.WebPage);
// get your pages
var webPages = GetWebPages().Where(v => v.WebPage.Contains(".htm"));
// get distinct WebPage values except existing ones
var distinctWebPages = webPages.Select(v => v.WebPage).Distinct().Except(existingWebPages);
// create WebsiteWebPage objects
var websiteWebPages = distinctWebPages.Select(v =>
new WebsiteWebPage { WebPage = v, WebsiteId = websiteid});
// save all at once
db.WebsiteWebPages.AddRange(websiteWebPages);
db.SaveChanges();
Assuming that you need them to be unique by WebPage and WebSiteId
IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
if (db.WebsiteWebPages.All(c=>c.WebPage != value.WebPage|| c.WebsiteId != websiteid))
{
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
}
}
}
db.SaveChanges();
UPDATE
To optimize this (given that your table contains much more data than your current list), override your equals in WebsiteWebPage class to define your uniqueness criteria then:
var myWebsiteWebPages = data.select(x=> new WebsiteWebPage { WebPage = x.WebPage, WebsiteId = websiteid}).Distinct();
var duplicates = db.WebsiteWebPages.Where(x=> myWebsiteWebPage.Contains(x));
db.WebsiteWebPages.AddRange(myWebsiteWebPages.Where(x=> !duplicates.Contains(x)));
this is a one database query to retrieve ONLY duplicates and then removing them from the list
You can use the following code,
IEnumerable<WebsiteWebPage> data = GetWebPages();
var templist = new List<WebsiteWebPage>();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
templist.Add(pagesinfo);
}
}
var distinctList = templist.GroupBy(x => x.WebsiteId).Select(group => group.First()).ToList();
db.WebsiteWebPages.AddRange(distinctList);
db.SaveChanges();
Or you can use MoreLINQ here to filter distinct the list by parameter like,
var res = tempList.Distinct(x=>x.WebsiteId).ToList();
db.WebsiteWebPages.AddRange(res);
db.SaveChanges();

How to convert TermCollection to Tree object via CSOM

I'm querying a SharePoint 2013 Term Store via the SharePoint Client Object Model in order to get a TermCollection.
I'd like to bind the results to a WPF TreeView control. Any idea how I can turn the TermCollection into something that the TreeView will understand?
public static TermCollection GetTaxonomyTerms(string webUrl, string libraryTitle, string fieldTitle)
{
var context = new ClientContext(webUrl);
var web = context.Web;
var list = web.Lists.GetByTitle(libraryTitle);
var fields = list.Fields;
var field = context.CastTo<TaxonomyField>(fields.GetByInternalNameOrTitle(fieldTitle));
context.Load(field);
var termStores = TaxonomySession.GetTaxonomySession(context).TermStores;
context.Load(termStores);
context.ExecuteQuery(); // TODO: Can this ExecuteQuery be avoided by using a LoadQuery statement?
var termStore = termStores.Where(t => t.Id == field.SspId).FirstOrDefault();
var termSet = termStore.GetTermSet(field.TermSetId);
var terms = termSet.GetAllTerms(); //TODO: Do we need a version that returns a paged set of terms? or queries the server again when a node is expanded?
context.Load(terms);
context.ExecuteQuery();
return terms;
}
I ended up writing my own code (please let me know if there's an easier way to do this).
My 'Term' object below is just a simple POCO with Name and Terms.
var terms = SharePointHelper.GetTaxonomyTerms(webUrl, libraryTitle, fieldTitle);
var term = terms.AsRootTreeViewTerm();
....
}
public static Term AsRootTreeViewTerm(this SP.TermCollection spTerms)
{
var root = new Term();
foreach (SP.Term spTerm in spTerms)
{
List<string> names = spTerm.PathOfTerm.Split(';').ToList();
var term = BuildTerm(root.Terms, names);
if (!root.Terms.Contains(term))
root.Terms.Add(term);
}
return root;
}
static Term BuildTerm(IList<Term> terms, List<string> names)
{
Term term = terms.Where(x => x.Name == names.First())
.DefaultIfEmpty(new Term() { Name = names.First() })
.First();
names.Remove(names.First());
if (names.Count > 0)
{
Term child = BuildTerm(term.Terms, names);
if (!term.Terms.Contains(child))
term.Terms.Add(child);
}
return term;
}

Exclude certain items from being added to a list with C#

I am getting data from a Web Api and adding it to a list in my Windows store touch app with c#. I use the code below which works fine.
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://localhost:12345/api/items");
var info = new List<SampleDataGroup>();
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var item = JsonConvert.DeserializeObject<dynamic>(content);
foreach (var data in item)
{
var infoSect = new info
(
(string)data.Id.ToString(),
(string)data.Name,
(string)"",
(string)data.PhotoUrl,
(string)data.Description
);
info.Add(infoSect);
}
}
else
{
MessageDialog dlg = new MessageDialog("Error");
await dlg.ShowAsync();
}
this.DefaultViewModel["Sections"] = info;
How do I check the data retrieved from the web api, to exclude items from the list whose Name contains the word "Test" in it. For example, an item with the Name "Soda Test" should not be included in the list.
So if Name contains "Test", do not add to list.
var items = item.Where( d =>!d.Name.Contains("Test")).ToList();
items.ForEach(i => info.Add(new info(){
data.Id.ToString(),
...
}));
!you should be able to do this using Linq.
info.AddRange(item.Where(i => !i.Name.Contains("Test"))
.Select(i => {
new info
(
(string)data.Id.ToString(),
(string)data.Name,
(string)"",
(string)data.PhotoUrl,
(string)data.Description
)}));
var item = JsonConvert.DeserializeObject<dynamic>(content).Where(i=>!i.Name.Contains("Test"))
Try this :
if(!data.Name.ToString().Contains("Test"))
{
//Add Item to List
}

C# Groupby Linq and foreach

I need a more efficient way of producing multiple files from my data group.
Im using a List<MyObject> type and my object has some public properties in which I need to group the data by.
I have heard of Linq and it sounds like something I could use. However Im not sure how to go about it.
I need to produce a text file for each STATE, so grouping all the MyObjects (people) by state, then running a foreach look on them to build the TEXT file.
void Main()
{
List<MyObject> lst = new List<MyObject>();
lst.Add(new MyObject{ name = "bill", state = "nsw", url = "microsoft.com"});
lst.Add(new MyObject{ name = "ted", state = "vic", url = "apple.com"});
lst.Add(new MyObject{ name = "jesse", state = "nsw", url = "google.com"});
lst.Add(new MyObject{ name = "james", state = "qld", url = "toshiba.com"});
string builder = "";
foreach (MyObject item in myObjects) {
builder += item.name + "\r\n";
builder += item.url + "\r\n" + "\r\n\r\n";
}
and out to the `StreamWriter` will be the filenames by state.
In total for the above data I need 3 files;
-nsw.txt
-vic.txt
-qld.txt
Something like this, perhaps?
var groups = lst.GroupBy(x => x.state);
foreach (var group in groups)
{
using (var f = new StreamWriter(group.Key + ".txt"))
{
foreach (var item in group)
{
f.WriteLine(item.name);
f.WriteLine(item.url);
}
}
}
You def. could use LINQ here.
lst.GroupBy(r=> r.state).ToList().ForEach(r=> {
//state= r.Key
//
foreach (var v in r)
{
}
});
The thing about linq. If you want to know how to do something in it. Think "how would I do this in SQL". The keywords are for the most part the same.
You can actually produce entire content with LINQ:
var entryFormat = "{1}{0}{2}{0}{0}{0}";
var groupsToPrint = lst
.GroupBy(p => p.state)
.Select(g => new
{
State = g.Key,
// produce file content on-the-fly from group entries
Content = string.Join("", g.Select(v => string.Format(entryFormat,
Environment.NewLine, v.name, v.url)))
});
var fileNameFormat = "{0}.txt";
foreach (var entry in groupsToPrint)
{
var fileName = string.Format(fileNameFormat, entry.State);
File.WriteAllText(fileName, entry.Content);
}
Something like...
string builderNsw = "";
foreach (MyObject item in lst.Where(o=>o.state == 'nsw')) {
builderNsw += item.name + "\r\n";
builderNsw += item.url + "\r\n" + "\r\n\r\n";
}
...but there are probably many ways to achieve this.
Same as Above - Iterating through groups by group, can get group name also
int itemCounter = 1;
IEnumerable<DataRow> sequence = Datatables.AsEnumerable();
var GroupedData = from d in sequence group d by d["panelName"]; // GroupedData is now of type IEnumerable<IGrouping<int, Document>>
foreach (var GroupList in GroupedData) // GroupList = "document group", of type IGrouping<int, Document>
{
bool chk = false;
foreach (var Item in GroupList)
{
if (chk == false) // means when header is not inserted
{
var groupName = "Panel Name : " + Item["panelName"].ToString();
chk = true;
}
var count = itemCounter.ToString();
var itemRef = Item["reference"].ToString();
itemCounter++;
}
}

Categories