I'm trying to access a SharePoint instance using SharePoint Online Client Components SDK (sharepointclientcomponents_16-6518-1200_x64-en-us.msi) through .Net 6 console application.
My Code:
internal class SpMain
{
public static void GetApprovedAppsByView(
string siteUrl, NetworkCredential credentials, string listName, string viewName, string camlQuery)
{
try
{
using var context = new ClientContext(siteUrl);
context.Credentials = credentials;
var list = context.Web.Lists.GetByTitle(listName);
var view = list.Views.GetByTitle(viewName);
var query = new CamlQuery();
context.Load(view);
if (context.HasPendingRequest)
{
context.ExecuteQuery();
}
query.ViewXml = $"<View><Query>{view.ViewQuery}{camlQuery}</Query></View>";
var items = list.GetItems(query);
context.Load(items);
if (context.HasPendingRequest)
{
context.ExecuteQuery();
}
foreach (var listItem in items)
{
Console.WriteLine(listItem["Title"]);
}
}
catch (Exception ex)
{
Console.WriteLine("Error Message: " + ex.Message);
}
}
}
I'm calling this function like:
// Target Page: https://dummy.company.com/commy/cotrev/Lists/DevCMRL/DaMemos.aspx
const string siteUrl = "https://dummy.company.com/commy/cotrev/";
const string listName = "DevCMRL";
const string viewName = "DaMemos";
const string query = "*";
var credentials = new NetworkCredential("sp_testUser", "AwesomePassword", "myDomain");
SpMain.GetApprovedAppsByView(siteUrl, credentials, listName, viewName, query);
Console.ReadKey();
But I keep getting Bad Request at the first context.ExecuteQuery();. The credentials are correct. I think I have the Url, List, & View names set up incorrectly. And also, CamlQuery is completely wrong for sure. But this is something new for me and the docs at Microsoft are talking about SharePoint Online only.
Given the Target URL of the page with the actual List, how should I set the correct parameters?
I'm new to Xamarin, I have an app in Xamarin-Form that it's fetching data from web api and getting user input from Entry control.
The web api service class is working fine and reaches the deserialization in the getCourses method as seen below in Code Snippet 1.
The Entry control as well is working fine until it retrieves the user input on the MainPage class, OnOkGetCourseButton method as seen below Code Snippet 2.
What I want to achieve is, inside MainPage.xaml.cs, I create a method that takes the user input data and check agaisnt the deseriaized json data (the Id specially),
if it finds the Id in deserialized List of data, then it can send the found data to another ViewPage and display them.
if It cannot find the data, it shows a dialog box.
So far, I tried to call Task<ObservableCollection> getCourses() method from the MainPage class, inside CheckCourseComplete as seen below but it giving me no value/nothing, some kind of null value.
I don't want to filter the user input against web api json response inside getCourses(),
I want to do that in a separate method to follow S-OLID (Single Responsibility Principle).
If it's not possible in a separate method, then I just need to get it worked.
Please what is the best way to achieve it?
Code Snippet 1
public class CourseService : ICourseService
{
string Base_Url = "https://www.test.com/api/TheCourse";
public async Task<ObservableCollection<Course>> getCourses()
{
try
{
string url = Base_Url;
HttpClient client = new HttpClient();
HttpResponseMessage responseMessage = await client.GetAsync(url);
if (responseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
var result = await responseMessage.Content.ReadAsStringAsync();
var deserializedClass = JsonConvert.DeserializeObject<ObservableCollection<Course>>(result);
// I don't want to do that here, as it will violate SRP (SOLID)
return deserializedClass;
}
return null;
}
catch (Exception)
{
throw;
}
}
}
Code Snippet 2
namespace CourseMobile
{
public partial class MainPage : ContentPage
{
private string _getEntryText;
private readonly CourseViewModel orderViewModel;
public Course FetchCourse { get; set; }
public MainPage()
{
InitializeComponent();
CheckCourseComplete();
BindingContext = new CourseViewModel();
}
public string GetEntryText
{
get => _getEntryText;
set => _getEntryText = value;
}
public async void OnOkGetCourseButton(object sender, EventArgs e)
{
var inputtedCourseNumber = this.GetEntryText;
if(inputtedCourseNumber == string.Empty)
{
await DisplayAlert("", "Please enter your Course number", "OK 3");
}
else
{
CheckCourseComplete();
this.GetEntryText = inputtedCourseNumber;
await DisplayAlert("New Text", inputtedCourseNumber, "OK 2");
}
}
void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
var newText = e.NewTextValue;
this.GetEntryText = newText;
}
public async void CheckCourseComplete()
{
CourseService myCourse = new CourseService();
await myCourse.getCourses(); // It doesn't return the json data (web api data)
// I need to check user input + (web api data) here
}
}
}
getCourses is async, so you need to use await when calling it
public async void CheckCourseComplete()
{
CourseService myCourse = new CourseService();
var data = await myCourse.getCourses();
// now filter data
}
I am storing some cardcredentials and i look to display the last 4 digits in a list but i am not sure how i can keep adding data.
With my current code it doesnt keep adding onto the list, instead it replaces the old one.
async void AddCard (object s, Eventargs e)
{
Account account = new Account();
account.Username = "Credentials";
account.Properties.Add("LastFour", LastFourString);
account.Properties.Add("CardBrand", BrandString);
account.Properties.Add("CardToken", TokenString);
AccountStore.Create().Save(account, "Credentials");
AddNewCard();
}
public class Info
{
public string LastFour { get; set; }
public string CardBrand { get; set; }
public string CardToken { get; set; }
}
async void AddNewCard()
{
var Account = AccountStore.Create().FindAccountsForService("Credentials");
var InfoList = new List<Info>();
foreach (var Data in Account)
{
InfoList.Add(new Info()
{
LastFour = "**********" + Data.Properties["LastFour"],
CardBrand = Data.Properties["CardBrand"]
CardToken = Data.Properties["CardToken"]
});
}
CardListView.ItemsSource = InfoList;
}
First of all, avoid async void if possible. For example, it seems that AddCard method is an event handler for a button, so its signature is fixed, but you can use async Task for add new card method.
For your requirement, you can use https://learn.microsoft.com/en-us/xamarin/essentials/secure-storage easily.
I have a table named profit model:
I would like to active different profitmodel, and the data in table will be updated.
Now I am using UpdateAsync, but it doesn't work... How can I achieve this?
async void Active_Clicked(object sender, System.EventArgs e)
{
var profitmodel = (sender as Button).CommandParameter as ProfitModel;
await conn.CreateTableAsync<ProfitModelInUsed>();
//System.Diagnostics.Debug.WriteLine(product.ProductName);
var profitmodelInUsed = new ProfitModelInUsed
{
ProfitModel_ID = profitmodel.ProfitModel_ID,
ProfitModel_Name = profitmodel.ProfitModel_Name,
ExchangeRate = profitmodel.ExchangeRate,
Profit = profitmodel.Profit
};
await conn.UpdateAsync(profitmodelInUsed);
await DisplayAlert("This ProfitModel is Applied", profitmodelInUsed.ProfitModel_Name, "OK");
}
And I don't want to have more than one rows in this table.
Apply a PrimaryKey to your ProfitModel_ID property within your ProfitModelInUsed model:
public class ProfitModelInUsed
{
[PrimaryKey]
public string ProfitModel_ID { get; set; }
// ~~~ other properties
}
And set the ProfitModel_ID property using a const:
profitmodelInUsed.ProfitModel_ID = "InUseProfitModel"
await conn.UpdateAsync(profitmodelInUsed);
Retrive it via:
var profitmodelinuse = conn.FindAsync<ProfitModelInUsed>("InUseProfitModel");
I realize that a lot of questions have been asked relating to full text search and Entity Framework, but I hope this question is a bit different.
I am using Entity Framework, Code First and need to do a full text search. When I need to perform the full text search, I will typically have other criteria/restrictions as well - like skip the first 500 rows, or filter on another column, etc.
I see that this has been handled using table valued functions - see http://sqlblogcasts.com/blogs/simons/archive/2008/12/18/LINQ-to-SQL---Enabling-Fulltext-searching.aspx. And this seems like the right idea.
Unfortunately, table valued functions are not supported until Entity Framework 5.0 (and even then, I believe, they are not supported for Code First).
My real question is what are the suggestions for the best way to handle this, both for Entity Framework 4.3 and Entity Framework 5.0. But to be specific:
Other than dynamic SQL (via System.Data.Entity.DbSet.SqlQuery, for example), are there any options available for Entity Framework 4.3?
If I upgrade to Entity Framework 5.0, is there a way I can use table valued functions with code first?
Thanks,
Eric
Using interceptors introduced in EF6, you could mark the full text search in linq and then replace it in dbcommand as described in http://www.entityframework.info/Home/FullTextSearch:
public class FtsInterceptor : IDbCommandInterceptor
{
private const string FullTextPrefix = "-FTSPREFIX-";
public static string Fts(string search)
{
return string.Format("({0}{1})", FullTextPrefix, search);
}
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
}
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
}
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
RewriteFullTextQuery(command);
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
}
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
RewriteFullTextQuery(command);
}
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
}
public static void RewriteFullTextQuery(DbCommand cmd)
{
string text = cmd.CommandText;
for (int i = 0; i < cmd.Parameters.Count; i++)
{
DbParameter parameter = cmd.Parameters[i];
if (parameter.DbType.In(DbType.String, DbType.AnsiString, DbType.StringFixedLength, DbType.AnsiStringFixedLength))
{
if (parameter.Value == DBNull.Value)
continue;
var value = (string)parameter.Value;
if (value.IndexOf(FullTextPrefix) >= 0)
{
parameter.Size = 4096;
parameter.DbType = DbType.AnsiStringFixedLength;
value = value.Replace(FullTextPrefix, ""); // remove prefix we added n linq query
value = value.Substring(1, value.Length - 2);
// remove %% escaping by linq translator from string.Contains to sql LIKE
parameter.Value = value;
cmd.CommandText = Regex.Replace(text,
string.Format(
#"\[(\w*)\].\[(\w*)\]\s*LIKE\s*#{0}\s?(?:ESCAPE N?'~')",
parameter.ParameterName),
string.Format(#"contains([$1].[$2], #{0})",
parameter.ParameterName));
if (text == cmd.CommandText)
throw new Exception("FTS was not replaced on: " + text);
text = cmd.CommandText;
}
}
}
}
}
static class LanguageExtensions
{
public static bool In<T>(this T source, params T[] list)
{
return (list as IList<T>).Contains(source);
}
}
For example, if you have class Note with FTS-indexed field NoteText:
public class Note
{
public int NoteId { get; set; }
public string NoteText { get; set; }
}
and EF map for it
public class NoteMap : EntityTypeConfiguration<Note>
{
public NoteMap()
{
// Primary Key
HasKey(t => t.NoteId);
}
}
and context for it:
public class MyContext : DbContext
{
static MyContext()
{
DbInterception.Add(new FtsInterceptor());
}
public MyContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
public DbSet<Note> Notes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new NoteMap());
}
}
you can have quite simple syntax to FTS query:
class Program
{
static void Main(string[] args)
{
var s = FtsInterceptor.Fts("john");
using (var db = new MyContext("CONNSTRING"))
{
var q = db.Notes.Where(n => n.NoteText.Contains(s));
var result = q.Take(10).ToList();
}
}
}
That will generate SQL like
exec sp_executesql N'SELECT TOP (10)
[Extent1].[NoteId] AS [NoteId],
[Extent1].[NoteText] AS [NoteText]
FROM [NS].[NOTES] AS [Extent1]
WHERE contains([Extent1].[NoteText], #p__linq__0)',N'#p__linq__0 char(4096)',#p__linq__0='(john)
Please notice that you should use local variable and cannot move FTS wrapper inside expression like
var q = db.Notes.Where(n => n.NoteText.Contains(FtsInterceptor.Fts("john")));
I have found that the easiest way to implement this is to setup and configure full-text-search in SQL Server and then use a stored procedure. Pass your arguments to SQL, allow the DB to do its job and return either a complex object or map the results to an entity. You don't necessarily have to have dynamic SQL, but it may be optimal. For example, if you need paging, you could pass in PageNumber and PageSize on every request without the need for dynamic SQL. However, if the number of arguments fluctuates per query, it will be the optimal solution.
As the other guys mentioned, I would say start using Lucene.NET
Lucene has a pretty high learning curve, but I found an wrapper for it called "SimpleLucene", that can be found on CodePlex
Let me quote a couple of codeblocks from the blog to show you how easy it is to use. I've just started to use it, but got the hang of it really fast.
First, get some entities from your repository, or in your case, use Entity Framework
public class Repository
{
public IList<Product> Products {
get {
return new List<Product> {
new Product { Id = 1, Name = "Football" },
new Product { Id = 2, Name = "Coffee Cup"},
new Product { Id = 3, Name = "Nike Trainers"},
new Product { Id = 4, Name = "Apple iPod Nano"},
new Product { Id = 5, Name = "Asus eeePC"},
};
}
}
}
The next thing you want to do is create an index-definition
public class ProductIndexDefinition : IIndexDefinition<Product> {
public Document Convert(Product p) {
var document = new Document();
document.Add(new Field("id", p.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
document.Add(new Field("name", p.Name, Field.Store.YES, Field.Index.ANALYZED));
return document;
}
public Term GetIndex(Product p) {
return new Term("id", p.Id.ToString());
}
}
and create an search index for it.
var writer = new DirectoryIndexWriter(
new DirectoryInfo(#"c:\index"), true);
var service = new IndexService();
service.IndexEntities(writer, Repository().Products, ProductIndexDefinition());
So, you now have an search-able index. The only remaining thing to do is.., searching! You can do pretty amazing things, but it can be as easy as this: (for greater examples see the blog or the documentation on codeplex)
var searcher = new DirectoryIndexSearcher(
new DirectoryInfo(#"c:\index"), true);
var query = new TermQuery(new Term("name", "Football"));
var searchService = new SearchService();
Func<Document, ProductSearchResult> converter = (doc) => {
return new ProductSearchResult {
Id = int.Parse(doc.GetValues("id")[0]),
Name = doc.GetValues("name")[0]
};
};
IList<Product> results = searchService.SearchIndex(searcher, query, converter);
The example here http://www.entityframework.info/Home/FullTextSearch is not complete solution. You will need to look into understand how the full text search works. Imagine you have a search field and the user types 2 words to hit search. The above code will throw an exception. You need to do pre-processing on the search phrase first to pass it to the query by using logical AND or OR.
for example your search phrase is "blah blah2" then you need to convert this into:
var searchTerm = #"\"blah\" AND/OR \"blah2\" ";
Complete solution would be:
value = Regex.Replace(value, #"\s+", " "); //replace multiplespaces
value = Regex.Replace(value, #"[^a-zA-Z0-9 -]", "").Trim();//remove non-alphanumeric characters and trim spaces
if (value.Any(Char.IsWhiteSpace))
{
value = PreProcessSearchKey(value);
}
public static string PreProcessSearchKey(string searchKey)
{
var splitedKeyWords = searchKey.Split(null); //split from whitespaces
// string[] addDoubleQuotes = new string[splitedKeyWords.Length];
for (int j = 0; j < splitedKeyWords.Length; j++)
{
splitedKeyWords[j] = $"\"{splitedKeyWords[j]}\"";
}
return string.Join(" AND ", splitedKeyWords);
}
this methods uses AND logic operator. You might pass that as an argument and use the method for both AND or OR operators.
You must escape none-alphanumeric characters otherwise it would throw exception when a user enters alpha numeric characters and you have no server site model level validation in place.
I recently had a similar requirement and ended up writing an IQueryable extension specifically for Microsoft full text index access, its available here IQueryableFreeTextExtensions