Calling Scalar User Defined Function in Where Condition in Entity Framework - c#

I have a SQL Function 'DecryptField' as :
ALTER FUNCTION [dbo].[DecryptField]
(
#EField as varchar(10)
)
RETURNS varchar(10)
BEGIN
Declare #decrypted varchar(10)
SET #decrypted = 'Something' + #EField
return #decrypted
END
I want to call that function in Entity Framework.(EF Version : 6.0 target framework: 4.0).
By searching over the Internet, I found a solution to create a class like :
public static class UDFFunctions
{
[EdmFunction("MyModel.Store", "DecryptField")]
public static string DecryptField(string field)
{
// This code will never been run against real SQL database
// This will help any test requires this method pass
throw new NotSupportedException("Direct calls not supported");
}
}
And then use this function as :
User user = null;
var query = from u in _context.Users.AsNoTracking()
where u.Login == userName && UDFFunctions.DecryptField(u.Password) == password
select u;
user = query.SingleOrDefault();
return user;
I am getting an error at runtime : LINQ to Entities does not recognize the method 'System.String DecryptField(System.String)' method, and this method cannot be translated into a store expression.
Please tell me if I am missing anything or doing anything wrong.

Related

C# Dynamic Linq: Implement "Like" in The Where Clause

So I want to make a general sorter for my data. I have this code to get data from the database which will extract the data only which contains value.
using System.Linq.Dynamic;
public static IQueryable<object> SortList(string searchString, Type modelType,
IQueryable<object> model)
{
....
string toStringPredicate = type == typeof(string) ? propertyName +
".Contains(#0)" : propertyName + ".ToString().Contains(#0)";
model = model.Where(propertyName + " != NULL AND " + toStringPredicate, value);
}
The model is this:
public class ManageSubscriberItems
{
public int? UserId { get; set; }
public string Email { get; set; }
public Guid SubscriberId { get; set; }
}
When I call:
models = (IQueryable<ManageSubscriberItems>)EcommerceCMS.Helpers.FilterHelper
.SortList(searchString, typeof(ManageSubscriberItems), models);
if(models.Any())
It throws this error:
"LINQ to Entities does not recognize the method 'System.String
ToString()' method, and this method cannot be translated into a store
expression."
EDIT
I found the problem, but I still cannot fix it. So if the property is not string, it will throw an error when calling .ToString().Contains().
model = model.Where(propertyName + " != NULL AND " + propertyName +
".ToString().Contains(#0)", value);
What I want is to implement LIKE in the query. Can anyone help me?
If you use System.Linq.Dynamic.Core with EF Core, you have an option to use
var q = context.Cars.Where(config, "DynamicFunctions.Like(Brand, \"%a%\")");
See this link for an example:
https://github.com/StefH/System.Linq.Dynamic.Core/blob/6fc7fcc43b248940560a0728c4d181e191f9eec1/src-console/ConsoleAppEF2.1.1/Program.cs#L117
And I just tested in linqpad connecting to a real database, and code like this just works?
var result1 = Entity1s.Where("Url != NULL AND it.Url.Contains(#0)", "e");
[UPDATE 2019-04-17]]
In case you don't know the type, you can cast it to object and then cast that to a string.
Code:
var r = Entity1s.Select("string(object(Rating))").Where("Contains(#0)", "6");
so the problem here is that IQueryable thing happens on the SQL server not in C#... so SQL server doesn't know anything about .toString() method.
so => and Like operator it self works on strings.. so it's nvarchar and varchar data types in SQL server.
I could give You an example of how to achieve it if you could tell me more about your problem and what You want to achieve.
could do a sample.
You already have a "Like" in Linq that can run in the database and works with strings, it's called "IndexOf":
((IQueryable)model).Where(m => m.Property.IndexOf(searchString) == 1);
According to MSDN:
IndexOf(string)
'The zero-based index position of value if that string is found, or -1 if it is not. If value is Empty, the return value is 0.'
So I want to make a general sorter for my data.
instead of fixing 'invoke issue', general way should use generics, like
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source,
string property,
bool asc = true) where T : class
{
//STEP 1: Validate MORE!
var searchProperty = typeof(T).GetProperty(property);
if (searchProperty == null) throw new ArgumentException("property");
....
//STEP 2: Create the OrderBy property selector
var parameter = Expression.Parameter(typeof(T), "o");
var selectorExpr = Expression.Lambda(Expression.Property(parameter, property), parameter)
//STEP 3: Update the IQueryable expression to include OrderBy
Expression queryExpr = source.Expression;
queryExpr = Expression.Call(typeof(Queryable), asc ? "OrderBy" : "OrderByDescending",
new Type[] { source.ElementType, searchProperty.PropertyType },
queryExpr,
selectorExpr);
return source.Provider.CreateQuery<T>(queryExpr);
}
having property name string and direction usually used for making 'column sorting' on data.
Next are relation predicates are coming, and 'Linq.Dynamic' seems reasonable when doing from scratch, but there is generic and canonical form exists.

How to assign ObjectResult<> from EF

After using DB first approach with EF my context.cs file has the follwing for a stored procedure:
public virtual ObjectResult<selectCases_Result> selectCases()
{
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<selectCases_Result>("selectPurgeCaseFolio");
}
In a sepearte class file I'm trying to invoke it to get the result with the following:
public SelectCases()
{
var result = _context.selectCases;
}
However the error I get is on result:
"Cannot assign method group to an implicitly-typed local variable"
How can I get the result of this select query into a dataset or anyother type of object to see the results?
You forgot to call the method (with ())
var result = _context.selectCases();
You are trying to call it like a property when you should be calling it as a method

Entity Framework error when import scalar function

I have an application with C#, I use entity framework to connect at database T-SQL. So I want to call a scalar function.
When I try to import this function I have a warning message:
Avviso 1 Error 6046: Unable to generate function import return type of
the store function 'F_GetCodiceAppezzamento'. The store function will
be ignored and the function import will not be
generated. C:\Users\michele.castriotta\Source\Workspaces\OmniaFarm\ws\WSOmniaFarm\WSOmniaFarm\FarmGEO_ToolEntities.edmx 1 1 WSOmniaFarm
So I have insert this method into FarmGeoo_ToolEntities class:
[DbFunction("FarmGEO_ToolEntities.Store", "F_GetCodiceAppezzamento")]
public string F_GetCodiceAppezzamento(string partitaIva)
{
var lObjectContext = ((IObjectContextAdapter)this).ObjectContext;
return lObjectContext.
CreateQuery<string>(
"FarmGEO_ToolEntities.F_GetCodiceAppezzamento",
new ObjectParameter("PIVAImpresa", partitaIva)).
Execute(MergeOption.NoTracking).
FirstOrDefault();
}
But when I try to call this method I have error:
'F_GetCodiceAppezzamento' non definito nel contenitore di entità
'FarmGEO_ToolEntities'. Vicino identificatore semplice, riga 1,
colonna 34."}

Get Entity Model from stored procedure?

I am trying to get a stored procedure's return type to match an entity model, this is a simplified version:
ALTER PROCEDURE [dbo].[GetPerson]
#userId INT
AS
BEGIN
SET NOCOUNT ON;
-- Perform INSERT statement
SELECT
*
FROM
People
WHERE
PersonId = #result;
END
At the moment, this stored procedure is returning an integer according to my EDMX. However, I want it to return a Person object instead. How do I go about doing this? I am using Entity Framework 6
IMHO You cannot return a CLR object from sql server. The best you can do is to return the proper string which exactly matches with the entity type in your C# code and use reflection to create an object.
For example
You can have a string something to the following "NamespaceName.TypeName,AssemblyName" which will be returned from your sql
Then on the c# side you can split it to typename and assemblyname. Then you can pass these to the following method.
using System.Reflection;
public static object CreateInstance(string typeName, string assemblyName)
{
object result = null;
try
{
Assembly assembly = Assembly.Load(assemblyName);
Type type = assembly.GetType(typeName, true, false);
result = Activator.CreateInstance(type);
}
catch (Exception ex)
{
//Handle exception here
}
if (result == null)
{
//Can handle null here
}
return result;
}
Of course this method assumes that your class has a default constructor. If not then you need to modify the code accordingly.
Hope this helps.

Can you convince a DataContext to treat a column as always dirty?

Is there a way to force LINQ-to-SQL to treat a column as dirty? Globally would suffice....
Basically, I've got a problem with some audit code on a legacy system that I'm talking to with L2S, imagine:
var ctx = new SomeDataContext(); // disposed etc - keeping it simple for illustration
var cust = ctx.Customers.First(); // just for illustration
cust.SomeRandomProperty = 17; // whatever
cust.LastUpdated = DateTime.UtcNowl;
cust.UpdatedBy = currentUser;
ctx.SubmitChanges(); // uses auto-generated TSQL
This is fine, but if the same user updates it twice in a row, the UpdatedBy is a NOP, and the TSQL will be (roughly):
UPDATE [dbo].[Customers]
SET SomeRandomColumn = #p0 , LastUpdated = #p1 -- note no UpdatedBy
WHERE Id = #p2 AND Version = #p3
In my case, the problem is that there is currently a belt-and-braces audit trigger on all tables, which checks to see if the audit column has been updated, and if not assumes the developer is at fault (substituting SUSER_SNAME(), although it could just as readily throw an error).
What I'd really like to be able to do is say "always update this column, even if it isn't dirty" - is this possible?
Based on KristoferA's answer, I ended up with something like below; this is evil and brittle (reflection often is), but may have to suffice for now. The other side of the battle is to change the triggers to behave:
partial class MyDataContext // or a base-class
{
public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode)
{
this.MakeUpdatesDirty("UpdatedBy", "Updated_By");
base.SubmitChanges(failureMode);
}
}
public static class DataContextExtensions
{
public static void MakeUpdatesDirty(
this DataContext dataContext,
params string[] members)
{
if (dataContext == null) throw new ArgumentNullException("dataContext");
if (members == null) throw new ArgumentNullException("members");
if (members.Length == 0) return; // nothing to do
foreach (object instance in dataContext.GetChangeSet().Updates)
{
MakeDirty(dataContext, instance, members);
}
}
public static void MakeDirty(
this DataContext dataContext, object instance ,
params string[] members)
{
if (dataContext == null) throw new ArgumentNullException("dataContext");
if (instance == null) throw new ArgumentNullException("instance");
if (members == null) throw new ArgumentNullException("members");
if (members.Length == 0) return; // nothing to do
const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
object commonDataServices = typeof(DataContext)
.GetField("services", AllInstance)
.GetValue(dataContext);
object changeTracker = commonDataServices.GetType()
.GetProperty("ChangeTracker", AllInstance)
.GetValue(commonDataServices, null);
object trackedObject = changeTracker.GetType()
.GetMethod("GetTrackedObject", AllInstance)
.Invoke(changeTracker, new object[] { instance });
var memberCache = trackedObject.GetType()
.GetField("dirtyMemberCache", AllInstance)
.GetValue(trackedObject) as BitArray;
var entityType = instance.GetType();
var metaType = dataContext.Mapping.GetMetaType(entityType);
for(int i = 0 ; i < members.Length ; i++) {
var member = entityType.GetMember(members[i], AllInstance);
if(member != null && member.Length == 1) {
var metaMember = metaType.GetDataMember(member[0]);
if (metaMember != null)
{
memberCache.Set(metaMember.Ordinal, true);
}
}
}
}
}
Unfortunately, I think you will have to use a new DataContext
Details at: http://blog.benhall.me.uk/2008/01/custom-insert-logic-with-linq-to-sql.html
You can override the default update behavior. There are 2 ways of doing this
The easiest is to create a stored procedure (if you can't do that on your database, the second method should work) which takes the parameters of your customer object and updates the table:
Create the stored procedure that has a parameter for each property of Customers that needs to be updated.
Import that stored procedure into your Linq To SQL DBML file.
Now you can right click on your customers entity and select "Configure Behavior".
Select your Customers class under the Class dropdown and "Update" on the behavior drop down.
Select the "Customize" radio button and choose the stored procedure you just created.
Now you can map class's properties to the stored procedure.
Now when Linq to SQL tries to update your Customers table, it'll use your stored procedure instead. Just be careful because this will override the update behavior for Customers everywhere.
The second method is to use partial methods. I haven't actually tried this, so hopefully this might just give you some general direction to pursue. In a partial class for your data context, make a partial method for the update (It'll be Update_____ with whatever your class is in the blank. I'd suggest searching in your data context's designer file to make sure you get the right one)
public partial SomeDataContext
{
partial void UpdateCustomer(Customer instance)
{
// this is where you'd do the update, but I'm not sure exactly how it's suppose to work, though. :(
}
}
If you want to go down the [dirty] reflection route, you could try something along the lines of:
1) Override SubmitChanges
2) Go through the change set
3) Use reflection to get hold of the change tracker for each updated object (see What's the cleanest way to make a Linq object "dirty"? )
4) Make the column dirty (there's a dirtyMemberCache field in the StandardTrackedObject class)
The following works for me. Note though that I'm using the linq2sql provider from DevArt, but that may not matter:
MyDataContext dc = new MyDataContext();
Message msg = dc.Messages.Single(m => m.Id == 1);
Message attachingMsg = new Message();
attachingMsg.Id = msg.Id;
dc.Messages.Attach(attachingMsg);
attachingMsg.MessageSubject = msg.MessageSubject + " is now changed"; // changed
attachingMsg.MessageBody = msg.MessageBody; // not changed
dc.SubmitChanges();
This produces the following sql:
UPDATE messages SET messageSubject = :p1, messageBody = :p2 WHERE Id = :key1
So, messageBody is updated even though its value is not changed.
One other change necessary for this, is that for each property (column) of my entity Message, I have set UpdatedCheck = UpdateCheck.Never, except for its ID, which is the primary key.

Categories