I need to complete my task using only stored procedures.
In my app I have Models called 'Document' and 'DocumentInfo' which refers to mysql tables. For example I got simplest stored procedure to get list of documentInfos by Filename:
SQL:
CREATE PROCEDURE `GetDocumentByName` (DocName varchar(255))
BEGIN
Select * from GetAllDocuments where DocumentName like DocName;
END$$
C#:
public List<DocumentsInfo> GetDocumentByName(string Filename)
{
return db.DocumentsInfo.FromSql("CALL GetDocumentByName({0})", Filename).ToList();
}
As you can see I use db - this is dbContext. DocumentsInfo it's my model and I returned a list of DocumentsInfo objects. But what if I don't need to return whole object, but only one column?
Now I need to do the same but with 'Document' but only this time I need to take only one field - DocumentBody, which is BLOB
SQL:
CREATE PROCEDURE `GetDocumentBodyById` (DocumentBodyID INT(11))
BEGIN
Select DocumentBody from Document where idDocumentBody = DocumentBodyID;
END$$
C#:
var test = db.FromSql("CALL GetDocumentBodyById({0})", DocumentID).FirstOrDefault();
Gives me an error:
'DBContext' does not contain a definition for 'FromSql' and no
accessible extension method 'FromSql' accepting a first argument of
type 'DBContext' could be found (are you missing a using directive or
an assembly reference?)
Also tried to use this option:
var test = db.Database.SqlQuery<object>("CALL GetDocumentBodyById({0})", DocumentID).FirstOrDefault();
But received new error:
'DatabaseFacade' does not contain a definition for 'SqlQuery' and no
accessible extension method 'SqlQuery'
How to call stored procedure which should return only one value, not whole model object? Is it possible with .net core?
For FromSql, it is used with Query, you could define a new model for return result.
public class ResultDto
{
public string Name { get; set; }
}
Define Query in OnModelCreating
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Query<ResultDto>();
}
Useage:
var result = _context.Query<ResultDto>().FromSql("exec GetDocumentBodyById {0}", 1).ToList();
Related
I am trying to make c# sql query builder.
So I have built some method which can be used as following.
Query.Select("Order.OrderId").From("Order")
I already have classes (models) generated from entity framework.
So is there a way, I can use their class name or/and their properties to be passed here as argument and get their name as string?
e.g. (notice no double quotes)
Query.Select(Order.OrderId).From(Order)
so far what I am using for time being is below.
Query.Select($"{nameof(Order)}.{nameof(OrderId)}").From(nameof(Order))
which as you can see is too ugly and destroys entire purpose of making sql query readable.
Reason I am not using EF queries is it gets too complex for complex queries.
https://en.m.wikipedia.org/wiki/Object-relational_impedance_mismatch
An example to use the class name or/and their properties to be passed as argument and get their name as string.
private static string abc<T>(string propertyName)
{
if(String.IsNullOrEmpty(propertyName))
{
return typeof(T).Name;
}
else
{
PropertyInfo columnProperty = typeof(T).GetProperty(propertyName);
return typeof(T).Name + "." + columnProperty.Name;
}
}
//An usage
Query.Select(abc<Order>("OrderId")).From(abc<Order>(""));
I am using Dapper with the Dapper.SimpleCrud extension.
In my Dapper class, I have made an abstraction of the insert method, like this:
public static int Insert(object entity) {
try {
using (SqlConnection sqlConnection = new SqlConnection(connectionString)) {
sqlConnection.Open();
return sqlConnection.Insert(entity) ?? -1;
}
}
catch(Exception ex) {
// log
return -1;
}
}
This lets me call insert and pass an object of any type that is in the db.
The code that currently calls this method looks like this:
DapperORM.Insert(new Menu {
RestaurantID = RestaurantID,
Name = Name});
This throws an error: {"Incorrect syntax near ')'."}
Ok, so now I think there is something wierd with the data I pass in or something. But no. When I change my own Insert method to take a Menu-object instead of a general object, it works.
The Dapper.SimpleCrud Insert overload method obviously can't figure out which object it is. Do you know how to fix it?
Have you had a look at the generated SQL? In stack trace may be? I guess it must be missing name of database table. Yes; I guess. Because I never used SimpleCRUD.
an object of any type that is in the db
How do SimpleCRUD know that the object you send in is "of type that is in the db"?
I think object type parameter is the problem. To accept "an object of any type that is in the db" you should consider converting your method to use generic type instead of object.
When I change my own Insert method to take a Menu-object instead of a general object, it works.
This confirms my earlier diagnosis.
Convert your method to something like below:
public static int Insert<TPoco>(TPoco entity) where TPoco : class
or
public static int Insert<TPoco>(TPoco entity) where TPoco : BasePoco
or similar as per your other code structure.
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
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.
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.