Using Dynamic LINQ with EF.Functions.Like - ef-core-3.1

On the Dynamic LINQ website there's an example using the Like function.
I am unable to get it to work with ef core 3.1
[Test]
public void DynamicQuery()
{
using var context = new SamDBContext(Builder.Options);
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var lst = context.Contacts.Where(config, "DynamicFunctions.Like(FirstName, \"%Ann%\")".ToList();
lst.Should().HaveCountGreaterThan(1);
}
Example from the Dynamic LINQ website
var example1 = Cars.Where(c => EF.Functions.Like(c.Brand, "%t%"));
example1.Dump();
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var example2 = Cars.Where(config, "DynamicFunctions.Like(Brand, \"%t%\")");
example2.Dump();
Looks like my code. But I am getting the following error
System.Linq.Dynamic.Core.Exceptions.ParseException : No property or field 'DynamicFunctions' exists in type 'Contact'

you don't need the ResolveTypesBySimpleName, implement your wont type provider.
The piece below people to use PostgreSQL ILike with unnaccent
public class LinqCustomProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
var result = base.GetCustomTypes();
result.Add(typeof(NpgsqlFullTextSearchDbFunctionsExtensions));
result.Add(typeof(NpgsqlDbFunctionsExtensions));
result.Add(typeof(DbFunctionsExtensions));
result.Add(typeof(DbFunctions));
result.Add(typeof(EF));
return result;
}
}
// ....
var expressionString = $"EF.Functions.ILike(EF.Functions.Unaccent(People.Name), \"%{value}%\")";
var config = new ParsingConfig()
{
DateTimeIsParsedAsUTC = true,
CustomTypeProvider = new LinqCustomProvider()
};
return query.Where(config, expressionString);
Hope this helps people, took me some time to get this sorted.

Related

Azure Search CreateIndexAsync fails with CamelCase field names FieldBuilder

Azure Search V11
I can't get this to work. But with the standard FieldBuilder the index is created.
private static async Task CreateIndexAsync(SearchIndexClient indexClient, string indexName, Type type)
{
var builder = new FieldBuilder
{
Serializer = new JsonObjectSerializer(new JsonSerializerOptions {PropertyNamingPolicy = new CamelCaseNamingPolicy()})
};
var searchFields = builder.Build(type).ToArray();
var definition = new SearchIndex(indexName, searchFields);
await indexClient.CreateIndexAsync(definition);
}
`
public class CamelCaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return char.ToLower(name[0]) + name.Substring(1);
}
}
See our sample for FieldBuilder. Basically, you must use a naming policy for both FieldBuilder and the SearchClient:
var clientOptions = new SearchClientOptions
{
Serializer = new JsonObjectSerializer(
new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
}),
};
var builder = new FieldBuilder
{
Serializer = clientOptions.Serializer,
};
var index = new SearchIndex("name")
{
Fields = builder.Build(type),
};
var indexClient = new SearchIndexClient(uri, clientOptions);
await indexClient.CreateIndexAsync(index);
await Task.DelayAsync(5000); // can take a little while
var searchClient = new SearchClient(uri, clientOptions);
var response = await searchClient.SearchAsync("whatever");
While this sample works (our sample code comes from oft-executed tests), if you have further troubles, please be sure to post the exact exception message you are getting.

Entity Framework - LINQ - Use Expressions in Select

I am using within my code some EF LINQ expressions to keep complex queries over my model in one place:
public static IQueryable<User> ToCheck(this IQueryable<User> queryable, int age, bool valueToCheck = true)
{
return queryable.Where(ToBeReviewed(age, valueToCheck));
}
public static Expression<Func<User, bool>> ToCheck(int age, bool valueToCheck = true)
{
return au => au.Status == UserStatus.Inactive
|| au.Status == UserStatus.Active &&
au.Age.HasValue && au.Age.Value > age;
}
I am then able to use them in queries:
var globalQuery = db.Users.ToCheck(value);
And also in selects:
var func = EntityExtensions.ToCheck(value);
var q = db.Department.Select(d => new
{
OrdersTotal = d.Orders.Sum(o => o.Price),
ToCheck = d.Users.AsQueryable().Count(func),
})
What I am trying to achieve is to actually use the same expression/function within a select, to evaluate it for each row.
var usersQuery = query.Select(au => new {
Id = au.Id,
Email = au.Email,
Status = au.Status.ToString(),
ToBeChecked = ???, // USE FUNCTION HERE
CreationTime = au.CreationTime,
LastLoginTime = au.LastLoginTime,
});
I am pretty that threre would be a way using plain EF capabilities or LINQKit, but can't find it.
Answering my own question :)
As pointed by #ivan-stoev, the use of Linqkit was the solution:
var globalQueryfilter = db.Users.AsExpandable.Where(au => au.Department == "hq");
var func = EntityExtensions.ToCheck(value);
var usersQuery = globalQueryfilter.Select(au => new
{
Id = au.Id,
Email = au.Email,
Status = au.Status.ToString(),
ToBeChecked = func.Invoke(au),
CreationTime = au.CreationTime,
LastLoginTime = au.LastLoginTime,
});
return appUsersQuery;
It's required to use the AsExpandable extension method from Linqkit along with Invoke with the function in the select method.
I want to add one more example:
Expression<Func<AddressObject, string, string>> selectExpr = (n, a) => n == null ? "[no address]" : n.OFFNAME + a;
var result = context.AddressObjects.AsExpandable().Select(addressObject => selectExpr.Invoke(addressObject, "1"));
Also, expression can be static in a helper.
p.s. please not forget to add "using LinqKit;" and use "AsExpandable".

Unable to cast object of type Entity to Type ActivityParty

Im working with a custom plugin for CRM online 2015 and every time I try to access the activityparty from the field "Email.To" I get
"base {System.SystemException} = {"Unable to cast object of type 'Microsoft.Xrm.Sdk.Entity' to type ...ActivityParty'."}"
Here is how my code looks like:
public class PreCreate : Plugin
{
public PreCreate()
: base(typeof(PreCreate))
{
base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Create", "email", new Action<LocalPluginContext>(ExecutePreEntityCreate)));
}
public void ExecutePreEntityCreate(LocalPluginContext localContext)
{
var target = (Entity)localContext.PluginExecutionContext.InputParameters["Target"];
using (var context = new XrmServiceContext(localContext.OrganizationService))
{
var email = target.ToEntity<Email>(); //The entity has the right values
var activityPartyList=email.To // here I see the exception
//If I use the following code:
var activityParty = email.GetAttributeValue<EntityCollection>("to");
//I get an empty ActivityParty(empty Id)
}
}
}
Do I have to do some initialization for activityparty types?
There is no issue with the code, the field Email.To will return a EntityCollection and to obtain that you need to use:
var entityCollection = email.GetAttributeValue<EntityCollection>("to");
This will give you a collection of entities that need to be converted to ActivityParty(entityCollection.Entities).
To convert the Entities you need to:
foreach (var entityItem in entityCollection.Entities)
{
var ap = entityItem.ToEntity<ActivityParty>();
//Here you will get the LogicalName in this case Lead
// the Id and the name
var leadId = ap.PartyId.Id;
//To get the Lead
var lead=context.LeadSet.FirstOrDefault(l => l.Id == leadId);
}

Querying OCB from JavaScript (WireCloud)

I'm trying to get type fields for each attribute of my entities. Quering Orion and getting entities is not the problem (I do this through NGSI Source widget) but the way getting these parameters.
From NGSI Source (usual suscription to Orion instance):
var doInitialSubscription = function doInitialSubscription() {
this.subscriptionId = null;
this.ngsi_server = MashupPlatform.prefs.get('ngsi_server');
this.ngsi_proxy = MashupPlatform.prefs.get('ngsi_proxy');
this.connection = new NGSI.Connection(this.ngsi_server, {
ngsi_proxy_url: this.ngsi_proxy
});
var types = MashupPlatform.prefs.get('ngsi_entities').split(new RegExp(',\\s*'));
var entityIdList = [];
var entityId;
for (var i = 0; i < types.length; i++) {
entityId = {
id: '.*',
type: types[i],
isPattern: true
};
entityIdList.push(entityId);
}
var attributeList = null;
var duration = 'PT3H';
var throttling = null;
var notifyConditions = [{
'type': 'ONCHANGE',
'condValues': MashupPlatform.prefs.get('ngsi_update_attributes').split(new RegExp(',\\s*'))
}];
var options = {
flat: true,
onNotify: handlerReceiveEntity.bind(this),
onSuccess: function (data) {
this.subscriptionId = data.subscriptionId;
this.refresh_interval = setInterval(refreshNGSISubscription.bind(this), 1000 * 60 * 60 * 2); // each 2 hours
window.addEventListener("beforeunload", function () {
this.connection.cancelSubscription(this.subscriptionId);
}.bind(this));
}.bind(this)
};
this.connection.createSubscription(entityIdList, attributeList, duration, throttling, notifyConditions, options);
};
var handlerReceiveEntity = function handlerReceiveEntity(data) {
for (var entityId in data.elements) {
MashupPlatform.wiring.pushEvent("entityOutput", JSON.stringify(data.elements[entityId]));
}
};
To MyWidget:
MashupPlatform.wiring.registerCallback("entityInput", function (entityString) {
var entity;
entity = JSON.parse(entityString);
id = entity.id;
type = entity.type;
for(var attr in entity){
attribute = entity[attr];
}
I'm trying to code something similar to obtain the value of type fields. How can I do that? (I'm sure it's quite easy...)
You cannot make use of the current NGSI source operator implementation (at least v3.0.2) if you want to get the type metadata of attributes as the NGSI source makes use of the flat option (discarding that info).
We are studying updating this operator to allow creating subscriptions without using the flat option. The main problem here is that other components expect the data provided by this operator being provided in the format returned when using the flat option. I will update this answer after analysing deeper the issue.

Get only a specified field in MongoDB with C#

first time i'm using MongoDB.
I have read this example:
SELECT a,b FROM users WHERE age=33
db.users.find({age:33}, {a:1,b:1})
But I can't translate it into C#. Can anyone help me?
I have translated your query below using the new C# driver (2.2)
var mongoClient = new MongoClient(""mongodb://127.0.0.1:27017"");
var database = mongoClient.GetDatabase("databaseName");
IMongoCollection<Users> _collection = database.GetCollection<Users>("Users");
var condition = Builders<Users>.Filter.Eq(p => p.age, 33);
var fields = Builders<Users>.Projection.Include(p => p.a).Include(p => p.b);
var results= _collection.Find(condition).Project<Users>(fields).ToList().AsQueryable();
You can do it using SetFields method of MongoCursor class, below full example:
var server = MongoServer.Create(connectionString);
var db = _server.GetDatabase("dbName");
var users = db.GetCollection("users");
var cursor = users.FindAs<DocType>(Query.EQ("age", 33));
cursor.SetFields(Fields.Include("a", "b"));
var items = cursor.ToList();
you can use anonymous class
public class User
{
public int age;
public string a;
public string b;
}
var collection = db.GetCollection<User>("Users");
var results = collection.Find(Builders<User>.Filter.Eq(user => user.age, 33))
.Project(u => new { u.a, u.b }).ToList();
//create user class
//(not sure how your class looks like)
public class User
{
public int age;
public string a;
public string b;
}
//then you can use LINQ easily
var server = MongoServer.Create(connectionString);
var db = server.GetDatabase("dbName");
var usersCollection = db.GetCollection<User>("users");
var filteredCollection = usersCollection.AsQueryable().Where(x=> x.age < 33).Where(x=> x.a != null).Contains(x=> x.b != null);