That's something I can't believe, but it's happening.
I have the following code:
public class UserData
{
public string displayname;
public int age;
}
UserData ud = new UserData();
ud.displayname = "bar";
ud.age = 2;
HttpContext.Current.Cache.Insert("foo", ud, null, DateTime.Now.AddMinutes(20), TimeSpan.Zero, CacheItemPriority.Normal, null);
UserData lud = HttpContext.Current.Cache["foo"];
HttpContext.Current.Trace.Write("displayname",ud.displayname);
Guess what is the output?
"bar"
Quoted!!!!
(The int field is not affected, it returned as int, not quoted, etc...)
Why is that? I searched on MSDN and google to no avail.
Use an ashx page to see the output.
Related
We have a situation where the CrmServiceClient class cannot be instantiated, with an 'Object reference not set to an object' error coming from deep within the bowels of the constructor. I've also received a Collection was modified; enumeration operation may not execute error in a few situations.
This does not happen all the time, but we seem to be able to reproduce it when we trigger multiple requests very quickly.
We create the object as follows:
var ctx = new CrmServiceClient(ConfigurationManager.ConnectionStrings["Xrm"].ConnectionString);
The connection string is valid and we have set RequireNewInstance to true
We were originally using the ctx in a using block but we were advised that we shouldn't be disposing of the CrmServiceClient, so we've removed the using block, but this has not resolved the problem.
The stack trace is below - i've only pasted the relevant part. The stack leading up to this point can be any piece of code that attempts to connect to the CRM to retrieve data
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
at System.Collections.Generic.List`1.Enumerator.MoveNext()
at Microsoft.Xrm.Tooling.Connector.Utilities.GetOrgnameAndOnlineRegionFromServiceUri(Uri serviceUri, String& onlineRegion, String& organizationName, Boolean& isOnPrem)
at Microsoft.Xrm.Tooling.Connector.CrmConnection.SetOrgnameAndOnlineRegion(Uri serviceUri)
at Microsoft.Xrm.Tooling.Connector.CrmConnection..ctor(String serviceUri, String userName, String password, String domain, String homeRealmUri, String authType, String requireNewInstance, String clientId, String redirectUri, String tokenCacheStorePath, String loginPrompt, String certStoreName, String certThumbprint, String skipDiscovery)
at Microsoft.Xrm.Tooling.Connector.CrmConnection..ctor(IDictionary`2 connection)
at Microsoft.Xrm.Tooling.Connector.CrmConnection.Parse(String connectionString)
at Microsoft.Xrm.Tooling.Connector.CrmServiceClient.ConnectToCrmWebService(String crmConnectionString)
at Microsoft.Xrm.Tooling.Connector.CrmServiceClient..ctor(String crmConnectionString)
I believe I've tracked down the issue. I used DotNetPeek to look at the underlying code that was failing. The static method GetOrgnameAndOnlineRegionFromServiceUriwas where the error was occurring.
I tracked it down to a static list (discoSvcs) that was being set to null before the method returns. Other threads that call this method are also trying to do things with this list. It ends up that there is a race condition where one thread could check to see if it isn't null.
The only way I can get around it now is making sure only one CrmServiceClient is being instantiated at any time, by using a lock. This isn't ideal but I am running out of time
Static list definition
namespace Microsoft.Xrm.Tooling.Connector
{
public class Utilities
{
private static CrmOnlineDiscoveryServers discoSvcs;
private static List<string> _autoRetryRetrieveEntityList;
private Utilities()
{
}
Problem Function
The static list variable is checked at the beginning of this function and if it is null, it is populated with some values. It is then used later in the method before being set to null in the finally block.
public static void GetOrgnameAndOnlineRegionFromServiceUri(
Uri serviceUri,
out string onlineRegion,
out string organizationName,
out bool isOnPrem)
{
isOnPrem = false;
onlineRegion = string.Empty;
organizationName = string.Empty;
if (serviceUri.Host.ToUpperInvariant().Contains("DYNAMICS.COM") || serviceUri.Host.ToUpperInvariant().Contains("MICROSOFTDYNAMICS.DE") || (serviceUri.Host.ToUpperInvariant().Contains("MICROSOFTDYNAMICS.US") || serviceUri.Host.ToUpperInvariant().Contains("DYNAMICS-INT.COM")))
{
if (Utilities.discoSvcs == null)
Utilities.discoSvcs = new CrmOnlineDiscoveryServers();
try
{
List<string> stringList = new List<string>((IEnumerable<string>) serviceUri.Host.Split(new string[1]
{
"."
}, StringSplitOptions.RemoveEmptyEntries));
organizationName = stringList[0];
stringList.RemoveAt(0);
StringBuilder stringBuilder = new StringBuilder();
foreach (string str in stringList)
{
if (!str.Equals("api"))
stringBuilder.AppendFormat("{0}.", (object) str);
}
string crmKey = stringBuilder.ToString().TrimEnd('.').TrimEnd('/');
stringBuilder.Clear();
if (!string.IsNullOrEmpty(crmKey))
{
CrmOnlineDiscoveryServer onlineDiscoveryServer = Utilities.discoSvcs.OSDPServers.Where<CrmOnlineDiscoveryServer>((Func<CrmOnlineDiscoveryServer, bool>) (w =>
{
if (w.DiscoveryServer != (Uri) null)
return w.DiscoveryServer.Host.Contains(crmKey);
return false;
})).FirstOrDefault<CrmOnlineDiscoveryServer>();
if (onlineDiscoveryServer != null && !string.IsNullOrEmpty(onlineDiscoveryServer.ShortName))
onlineRegion = onlineDiscoveryServer.ShortName;
}
isOnPrem = false;
}
finally
{
Utilities.discoSvcs.Dispose();
Utilities.discoSvcs = (CrmOnlineDiscoveryServers) null;
}
}
else
{
isOnPrem = true;
if (((IEnumerable<string>) serviceUri.Segments).Count<string>() < 2)
return;
organizationName = serviceUri.Segments[1].TrimEnd('/');
}
}
I'm just learning visual c# in visual studio
Ok, so I have a ton of data fields in a form, and then I want to write the handlers to all call one main method, update, which then updates a resultsEntry object, which encapsulates a bunch of uint variables with various names.
How do I write an update method to put in either the results object, or the update method that will take the name of the variable in resultsEntry as a string, and the integer to update it with, and then update that field.
Basically, I need to do a
resultsEntry.(inStringHere) = inValueHere;
where resultsEntry is the object being updated, the inStringHere specifies the field to be updated, and the inValueHere represents the integer value to assign to it.
Thanks!
Sam French
You have two challenges,
Setting a field/property in class using a string (the focus of your question). This will be accomplished using reflection.
Converting values to the type in your class (this may not be a problem for you, you may have 'typed' values. I have an ugly solution because this is not the main focus of your question.
Setting a property by name (see comments preceded with '**'):
static class Program
{
// A 'ResultEntry' type
public class ResultEntry
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
static void Main()
{
// some tuples Field1 = Property Name; Field2 = Raw Value (string)
List<Tuple<string, string>> rawEntries = new List<Tuple<string, string>>() {
new Tuple<string,string>("ID", "1")
, new Tuple<string, string>("FirstName", "George")
, new Tuple<string, string>("LastName", "Washington")
};
ResultEntry resultEntry = new ResultEntry();
// ** Get MemberInfo's for your ResultEntry. Do this once, not for each instance of ResultEntry!
MemberInfo[] members = resultEntry.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
// Iterate over input
foreach (var raw in rawEntries)
{
// find a MemberInfo (PropertyInfo) that matches your input 'PropertyName'
MemberInfo member = members.FirstOrDefault(m => m.MemberType == MemberTypes.Property && m.Name == raw.Item1);
if (member != null)
{
// if you input is typed you will not have to deal with
// conversion of the string to the actual type of the property
object val = raw.Item2.MyConverter(((PropertyInfo)member).PropertyType);
// ** set the value in 'ResultEntry'
((PropertyInfo)member).SetValue(resultEntry, val, null);
}
}
Console.WriteLine(string.Format("Result Entry: ID = {0}, FirstName = {1}, LastName = {2}", resultEntry.ID, resultEntry.FirstName, resultEntry.LastName));
Console.ReadLine();
}
}
If you need to deal with converting raw string input to type (e.g. string to int), then is just one strategy...
public static class Extensions
{
public static object MyConverter(this string rawValue, Type convertToMe)
{
// ugly, there are other strategies
object val;
if (convertToMe == typeof(Int32))
val = Convert.ToInt32(rawValue);
// ... add other type conversions
else
val = rawValue;
return val;
}
}
Hope this helps.
I'm trying to find a string from the List....seems like its not working and if I have just List<string> it does work.. meaning like the below code...
List<string> c = new List<string>();
c.Add("John Doe"));
c.Add("Erich Schulz"));
//I think the problem with the Criterion class?
here is my class structure:
public class Criterion
{
public Criterion(String propertyName, object value)
{
this.PropertyName = propertyName;
this.Value = value;
}
}
//here is the method...
public static List<Criterion> LoadNames()
{
List<Criterion> c = new List<Criterion>();
c.Add(new Criterion("Name1", "John Doe"));
c.Add(new Criterion("Name2", "Erich Schulz"));
return c;
}
here is the code I'm trying to make it work:
bool isExists = LoadNames.Any(s=> "Erich Schulz".Contains(s));
Error:
does not contain a definition for 'Any' and the best extension method overload 'System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,bool>)' has some invalid arguments
When you call .Contains(s), s isn't a string, it's Criterion. Use .Contains(s.propertyName).
bool isExists = LoadNames().Any(s=> "Erich Schulz".Contains(s.PropertyName));
Also you're using LoadNames as a method, you need to execute it first.
You're attempting to compare a string to a Criterion object, which just doesn't work.
Here is the fixed code:
bool isExists = LoadNames.Any(criterion => String.Equals(criterion.PropertyName, "Erich Schulz", StringComparison.OrdinalIgnoreCase));
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
I want to retrieve a specific record using IQueryable. But i get error 'No generic method 'Where' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.'. I got the selected row id, but I cannot display it out. Here is my code.
internal static IQueryable GetRecordsFromPrimaryKeys(this IQueryable datasource, List<FilterDescriptor> primaryKeys)
{
IQueryable data = datasource;
ParameterExpression paramExp = null;
bool firstLoop = false;
System.Linq.Expressions.Expression predicate = null;
var RecordType = datasource.GetObjectType();
paramExp = RecordType.Parameter();
foreach (FilterDescriptor primaryKey in primaryKeys)
{
if (!(firstLoop))
{
predicate = data.Predicate(paramExp, primaryKey.ColumnName, primaryKey.Value, FilterType.Equals, false, RecordType);
firstLoop = true;
}
else
{
predicate = predicate.AndPredicate(data.Predicate(paramExp, primaryKey.ColumnName, primaryKey.Value, FilterType.Equals, false, RecordType));
}
}
if (paramExp != null && predicate != null)
{
var lambda = Expression.Lambda(predicate, paramExp);
data = data.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
"Where",
new Type[] { data.ElementType },
data.Expression,
lambda
)
);
}
return data;
}
My Code works well for IEnumerable/IQueryable/ICollection . But it throws the exception when i specify the class with the keyword virtual and type as ICollection. My code is
public class RoomType
{
public int ID { get; set; }
[MaxLength(10, ErrorMessage = "Room code cannot be longer than 10 characters.")]
public string Code { get; set; }
[MaxLength(50, ErrorMessage = "Room name cannot be longer than 50 characters.")]
public string Name { get; set; }
public virtual ICollection<RoomCategory> RoomCategories { get; set; }
}
Some random values gets appended to 'RecordType' while using the keyword 'virtual'. I think this leads to the exception. Still searching for the solution.
I don't know what is going wrong . Any suggestions welcome.
Thanks.
I just ran into a similar situation. The problem stems from the fact that in some cases you're dealing with the "proxy" not the actual entity. So, you want to make sure that RecordType matches data.ElementType.
try:
var recordType = datasource.GetObjectType();
// make sure we have the correct type (not the proxy)
if (recordType.BaseType.Name != "Object")
recordType = recordType.BaseType;
Or better yet, try:
var recordType = data.ElementType
Try to use typeof(Enumerable) instead of typeof(Queryable)