I have a database with a table describing multiple entities, with one column being the name of another database holding data for that entity. All entity databases are on the same SQL Server as the one listing them and all have an identical schema.
I know that I can use Ctrl-drag to add the additional databases to my script but what I actually want is to do this dynamically from the database name. Something like this.
var entities = ParentDatabase.EntityList
.Where(e => ??)
.Select(e => new { e.Id, e.DatabaseName });
var results = new List<ResultCarrier>();
foreach (var entity in entities)
{
results.AddRange(
GetDataContextFor(entity.DatabaseName).SomeTable
.Select(t => new ResultCarrier()
{
EntityId = e.Id,
Column1 = t.Column1,
Column2 = t.Column2,
...
}));
}
// further process combined results
Is this possible?
I see that the type of one of these databases is LINQPad.User.DatabaseNameTypes.TypedDataContext and wondered whether, as each database has the same schema, there might be a base class that I could use in some way to achieve this.
TypedDataContext is your base class, and you can just create a new instance of this and pass it the sql connection string.
You can find your current connection string using
this.Connection.ConnectionString.Dump();
For example, I use Integrated Security and I have a little routine that goes through all the database in my server and dumps out a table, so I use the following routine.
var databases = ExecuteQuery<String>("SELECT name FROM sys.databases").ToList();
foreach(var r in databases)
{
switch (r)
{
case "master" :
case "tempdb" :
case "model" :
case "msdb" :
break;
default:
try
{
string newConnectionString = String.Format("Data Source={0};Integrated Security=SSPI;Initial Catalog={1};app=LINQPad", this.Connection.DataSource, r);
var dc = new TypedDataContext(newConnectionString);
dc.Table.Dump(r);
}
catch (Exception ex)
{
ex.Message.Dump(r);
}
break;
}
}
#sgmoore's answer got me on the right track. I hadn't come across ExecuteQuery in LINQPad and I was able to use it to achieve what I wanted. Below is the code I ended up with. I will now extend it to further retrieve data from a service and join it to databaseLocations to give the final result I'm after.
void Main()
{
var organisations = ExecuteQuery<OrganisationCarrier>(#"
SELECT do.GroupId [Id], o.sOrganisationName [Name], o.sConnectDatabase [Database]
FROM dbo.Organisation o
INNER JOIN dynamix.Organisations do ON o.liOrgID = do.OrganisationID
INNER JOIN dynamix.OrganisationFeatures oft ON do.OrganisationKey = oft.OrganisationKey
INNER JOIN dynamix.Features ft ON oft.FeatureKey = ft.FeatureKey
WHERE ft.FeatureName = 'LightningLocations'").ToList();
var databaseLocations = new List<LocationExtract>();
foreach (var organisation in organisations)
{
this.Connection.ConnectionString = $"Data Source={this.Connection.DataSource};Integrated Security=SSPI;Initial Catalog={organisation.Database};app=LINQPad";
databaseLocations.AddRange(ExecuteQuery<LocationCarrier>(#"
SELECT dml.DmxLocationId [Id], ml.sLocationName [Name], ml.bDeleted [IsDeleted]
FROM dynamix.MapLocations dml
INNER JOIN dbo.MapLocations ml ON dml.FmLocationId = ml.liLocationID")
.Select(l => new LocationExtract(organisation.Id, l.Id, l.Name, l.IsDeleted)));
}
databaseLocations.Dump();
}
class OrganisationCarrier
{
public long Id { get; set; }
public string Name { get; set; }
public string Database { get; set; }
}
class LocationCarrier
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
class LocationExtract
{
public long OrganisationId { get; }
public long LocationId { get; }
public string Name { get; }
public bool IsDeleted { get; }
public LocationExtract(long organisationId, long locationId, string name, bool isDeleted)
{
OrganisationId = organisationId;
LocationId = locationId;
Name = name;
IsDeleted = isDeleted;
}
}
I am using EF Core 1.0 in an ASP.NET Core App. Can you please point me to the proper way of executing stored procedures? The old method with ObjectParameters and ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction is not working.
Support for stored procedures in EF Core 1.0 is resolved now, this also supports the mapping of multiple result-sets.
Check here for the fix details
And you can call it like this in c#
var userType = dbContext.Set().FromSql("dbo.SomeSproc #Id = {0}, #Name = {1}", 45, "Ada");
Stored procedure support is not yet (as of 7.0.0-beta3) implemented in EF7. You can track the progress of this feature using issue #245.
For now, you can do it the old fashioned way using ADO.NET.
var connection = (SqlConnection)context.Database.AsSqlServer().Connection.DbConnection;
var command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "MySproc";
command.Parameters.AddWithValue("#MyParameter", 42);
command.ExecuteNonQuery();
To execute the stored procedures, use FromSql method which executes RAW SQL queries
e.g.
var products= context.Products
.FromSql("EXECUTE dbo.GetProducts")
.ToList();
To use with parameters
var productCategory= "Electronics";
var product = context.Products
.FromSql("EXECUTE dbo.GetProductByCategory {0}", productCategory)
.ToList();
or
var productCategory= new SqlParameter("productCategory", "Electronics");
var product = context.Product
.FromSql("EXECUTE dbo.GetProductByName #productCategory", productCategory)
.ToList();
There are certain limitations to execute RAW SQL queries or stored procedures. You can’t use it for INSERT/UPDATE/DELETE. if you want to execute INSERT, UPDATE, DELETE queries, use the ExecuteSqlCommand
var categoryName = "Electronics";
dataContext.Database
.ExecuteSqlCommand("dbo.InsertCategory #p0", categoryName);
The support for stored procedure in EF Core is similar to the earlier versions of EF Code first.
You need to create your DbContext class by inherting the DbContext class from EF. The stored procedures are executing using the DbContext.
First step is to write a method that create a DbCommand from the DbContext.
public static DbCommand LoadStoredProc(
this DbContext context, string storedProcName)
{
var cmd = context.Database.GetDbConnection().CreateCommand();
cmd.CommandText = storedProcName;
cmd.CommandType = System.Data.CommandType.StoredProcedure;
return cmd;
}
To pass parameters to the stored procedure use the following method.
public static DbCommand WithSqlParam(
this DbCommand cmd, string paramName, object paramValue)
{
if (string.IsNullOrEmpty(cmd.CommandText))
throw new InvalidOperationException(
"Call LoadStoredProc before using this method");
var param = cmd.CreateParameter();
param.ParameterName = paramName;
param.Value = paramValue;
cmd.Parameters.Add(param);
return cmd;
}
Finally for mapping the result into a list of custom objects use the MapToList method.
private static List<T> MapToList<T>(this DbDataReader dr)
{
var objList = new List<T>();
var props = typeof(T).GetRuntimeProperties();
var colMapping = dr.GetColumnSchema()
.Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
.ToDictionary(key => key.ColumnName.ToLower());
if (dr.HasRows)
{
while (dr.Read())
{
T obj = Activator.CreateInstance<T>();
foreach (var prop in props)
{
var val =
dr.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
prop.SetValue(obj, val == DBNull.Value ? null : val);
}
objList.Add(obj);
}
}
return objList;
}
Now we’re ready for execute the stored procedute with the ExecuteStoredProc method and maps it to the a List whose type that’s passed in as T.
public static async Task<List<T>> ExecuteStoredProc<T>(this DbCommand command)
{
using (command)
{
if (command.Connection.State == System.Data.ConnectionState.Closed)
command.Connection.Open();
try
{
using (var reader = await command.ExecuteReaderAsync())
{
return reader.MapToList<T>();
}
}
catch(Exception e)
{
throw (e);
}
finally
{
command.Connection.Close();
}
}
}
For example, to execute a stored procedure called “StoredProcedureName” with two parameters called “firstparamname” and “secondparamname” this is the implementation.
List<MyType> myTypeList = new List<MyType>();
using(var context = new MyDbContext())
{
myTypeList = context.LoadStoredProc("StoredProcedureName")
.WithSqlParam("firstparamname", firstParamValue)
.WithSqlParam("secondparamname", secondParamValue).
.ExecureStoredProc<MyType>();
}
I tried all the other solutions but didn't worked for me. But I came to a proper solution and it may be helpful for someone here.
To call a stored procedure and get the result into a list of model in EF Core, we have to follow 3 steps.
Step 1.
You need to add a new class just like your entity class. Which should have properties with all the columns in your SP. For example if your SP is returning two columns called Id and Name then your new class should be something like
public class MySPModel
{
public int Id {get; set;}
public string Name {get; set;}
}
Step 2.
Then you have to add one DbQuery property into your DBContext class for your SP.
public partial class Sonar_Health_AppointmentsContext : DbContext
{
public virtual DbSet<Booking> Booking { get; set; } // your existing DbSets
...
public virtual DbQuery<MySPModel> MySP { get; set; } // your new DbQuery
...
}
Step 3.
Now you will be able to call and get the result from your SP from your DBContext.
var result = await _context.Query<MySPModel>().AsNoTracking().FromSql(string.Format("EXEC {0} {1}", functionName, parameter)).ToListAsync();
I am using a generic UnitOfWork & Repository. So my function to execute the SP is
/// <summary>
/// Execute function. Be extra care when using this function as there is a risk for SQL injection
/// </summary>
public async Task<IEnumerable<T>> ExecuteFuntion<T>(string functionName, string parameter) where T : class
{
return await _context.Query<T>().AsNoTracking().FromSql(string.Format("EXEC {0} {1}", functionName, parameter)).ToListAsync();
}
Hope it will be helpful for someone !!!
"(SqlConnection)context"
-- This type-casting no longer works. You can do: "SqlConnection context;
".AsSqlServer()"
-- Does not Exist.
"command.ExecuteNonQuery();"
-- Does not return results. reader=command.ExecuteReader() does work.
With dt.load(reader)... then you have to switch the framework out of 5.0 and back to 4.51, as 5.0 does not support datatables/datasets, yet. Note: This is VS2015 RC.
Currently EF 7 or EF Core does not support the old method of importing Stored procedures in designer and calling them directly. You can have a look at the roadmap to see what is going to be supported in the future:
EF core roadmap.
So for now it is better to use SqlConnection to call stored procedures or any raw query, since you do not need the entire EF for this job. Here are two examples:
Call stored procedure that return single value. String in this case.
CREATE PROCEDURE [dbo].[Test]
#UserName nvarchar(50)
AS
BEGIN
SELECT 'Name is: '+#UserName;
END
Call stored procedure that return a list.
CREATE PROCEDURE [dbo].[TestList]
AS
BEGIN
SELECT [UserName], [Id] FROM [dbo].[AspNetUsers]
END
To call these stored procedure it is better to create static class that holds all of these functions, for example, I called it DataAccess class, as follows:
public static class DataAccess
{
private static string connectionString = ""; //Your connection string
public static string Test(String userName)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
// 1. create a command object identifying the stored procedure
SqlCommand cmd = new SqlCommand("dbo.Test", conn);
// 2. set the command object so it knows to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// 3. add parameter to command, which will be passed to the stored procedure
cmd.Parameters.Add(new SqlParameter("#UserName", userName));
// execute the command
using (var rdr = cmd.ExecuteReader())
{
if (rdr.Read())
{
return rdr[0].ToString();
}
else
{
return null;
}
}
}
}
public static IList<Users> TestList()
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
// 1. create a command object identifying the stored procedure
SqlCommand cmd = new SqlCommand("dbo.TestList", conn);
// 2. set the command object so it knows to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// execute the command
using (var rdr = cmd.ExecuteReader())
{
IList<Users> result = new List<Users>();
//3. Loop through rows
while (rdr.Read())
{
//Get each column
result.Add(new Users() { UserName = (string)rdr.GetString(0), Id = rdr.GetString(1) });
}
return result;
}
}
}
}
And Users class is like this:
public class Users
{
public string UserName { set; get; }
public string Id { set; get; }
}
By the way you do not need to worry about the performance of opening and closing a connection for every request to sql as the asp.net is taking care of managing these for you.
And I hope this was helpful.
I had a lot of trouble with the ExecuteSqlCommand and ExecuteSqlCommandAsync, IN parameters were easy, but OUT parameters were very difficult.
I had to revert to using DbCommand like so -
DbCommand cmd = _context.Database.GetDbConnection().CreateCommand();
cmd.CommandText = "dbo.sp_DoSomething";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#firstName", SqlDbType.VarChar) { Value = "Steve" });
cmd.Parameters.Add(new SqlParameter("#lastName", SqlDbType.VarChar) { Value = "Smith" });
cmd.Parameters.Add(new SqlParameter("#id", SqlDbType.BigInt) { Direction = ParameterDirection.Output });
I wrote more about it in this post.
Since it is agreed in my team that we are going to use generic UnitOfWork pattern, I have taken a bit of everyone's solutions when creating mine.
I am posting a bit of UnitOfWork code as well so you can get a grasp of why I needed to implement it like so.
public interface IUnitOfWork : IDisposable
{
DbContext Context { get; }
Task<List<T>> ExecuteStoredProc<T>(string storedProcName, Dictionary<string, object> procParams) where T : class;
}
Interface implementation:
public class UnitOfWork : IUnitOfWork
{
public DbContext Context { get; private set; }
/// <summary>
/// Execute procedure from database using it's name and params that is protected from the SQL injection attacks.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="storedProcName">Name of the procedure that should be executed.</param>
/// <param name="procParams">Dictionary of params that procedure takes. </param>
/// <returns>List of objects that are mapped in T type, returned by procedure.</returns>
public async Task<List<T>> ExecuteStoredProc<T>(string storedProcName, Dictionary<string, object> procParams) where T : class
{
DbConnection conn = Context.Database.GetDbConnection();
try
{
if(conn.State != ConnectionState.Open)
await conn.OpenAsync();
await using (DbCommand command = conn.CreateCommand())
{
command.CommandText = storedProcName;
command.CommandType = CommandType.StoredProcedure;
foreach (KeyValuePair<string, object> procParam in procParams)
{
DbParameter param = command.CreateParameter();
param.ParameterName = procParam.Key;
param.Value = procParam.Value;
command.Parameters.Add(param);
}
DbDataReader reader = await command.ExecuteReaderAsync();
List<T> objList = new List<T>();
IEnumerable<PropertyInfo> props = typeof(T).GetRuntimeProperties();
Dictionary<string, DbColumn> colMapping = reader.GetColumnSchema()
.Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
.ToDictionary(key => key.ColumnName.ToLower());
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
T obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in props)
{
object val =
reader.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
prop.SetValue(obj, val == DBNull.Value ? null : val);
}
objList.Add(obj);
}
}
reader.Dispose();
return objList;
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message, e.InnerException);
}
finally
{
conn.Close();
}
return null; // default state
}
Example usage is like so:
public class MyService : IMyService
{
private readonly IUnitOfWork _uow;
public MyService(IUnitOfWork uow)
{
_uow = uow;
}
public async Task<List<TreeViewModel>> GetTreeOptions()
{
var procParams = new Dictionary<string, object>()
{
{"#Id", 2}
};
var result = await _uow.ExecuteStoredProc<TreeViewModel>("FetchTreeProcedure", procParams);
return result;
}
}
I found this extention very usefull: StoredProcedureEFCore
Then the usage is like this
List<Model> rows = null;
ctx.LoadStoredProc("dbo.ListAll")
.AddParam("limit", 300L)
.AddParam("limitOut", out IOutParam<long> limitOut)
.Exec(r => rows = r.ToList<Model>());
long limitOutValue = limitOut.Value;
ctx.LoadStoredProc("dbo.ReturnBoolean")
.AddParam("boolean_to_return", true)
.ReturnValue(out IOutParam<bool> retParam)
.ExecNonQuery();
bool b = retParam.Value;
ctx.LoadStoredProc("dbo.ListAll")
.AddParam("limit", 1L)
.ExecScalar(out long l);
Using MySQL connector and Entity Framework Core 2.0
My issue was that I was getting an exception like fx. Ex.Message = "The required column 'body' was not present in the results of a 'FromSql' operation.". So, in order to fetch rows via a stored procedure in this manner, you must return all columns for that entity type which the DBSet is associated with, even if you don't need to access all of it for your current request.
var result = _context.DBSetName.FromSql($"call storedProcedureName()").ToList();
OR with parameters
var result = _context.DBSetName.FromSql($"call storedProcedureName({optionalParam1})").ToList();
I'm using Entity Framework Core with my ASP.Net Core 3.x WebAPI. I wanted one of my end points just to execute a particular Stored Procedure, and this is the code I needed:
namespace MikesBank.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ResetController : ControllerBase
{
private readonly MikesBankContext _context;
public ResetController(MikesBankContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult> Get()
{
try
{
using (DbConnection conn = _context.Database.GetDbConnection())
{
if (conn.State != System.Data.ConnectionState.Open)
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = "Reset_Data";
await cmd.ExecuteNonQueryAsync();
}
return new OkObjectResult(1);
}
catch (Exception ex)
{
return new BadRequestObjectResult(ex.Message);
}
}
}
}
Notice how I need to get my DbContext which has been injected, but I also need to Open() this connection.
Create the special class according the fields in your Select query of your stored procedure.
For example I will call this class ResulData
Add to context of you EF
modelBuilder.Entity<ResultData>(e =>
{
e.HasNoKey();
});
And this a sample function to get data using the store procedure
public async Task<IEnumerable<ResultData>> GetDetailsData(int id, string name)
{
var pId = new SqlParameter("#Id", id);
var pName = new SqlParameter("#Name", name);
return await _context.Set<ResultData>()
.FromSqlRaw("Execute sp_GetDeailsData #Id #Name", parameters: new[] { pId, pName })
.ToArrayAsync();
}
I used StoredProcedureEFCore nuget package by https://github.com/verdie-g/StoredProcedureEFCore,EnterpriseLibrary.Data.NetCore,EFCor.SqlServer,EFCore.Tools
I tried DbFirst approach with {Repository pattern}.. i think so
startup.cs
ConfigureServices(IServiceCollection services){
services.AddDbContext<AppDbContext>(opt => opt
.UseSqlServer(Configuration.GetConnectionString("SampleConnectionString")));
services.AddScoped<ISomeDAL, SomeDAL>();
}
public class AppDbContext : DbContext{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{}
}
ISomeDAl Interface has {GetPropertiesResponse GetAllPropertiesByCity(int CityId);}
public class SomeDAL : ISomeDAL
{
private readonly AppDbContext context;
public SomeDAL(AppDbContext context)
{
this.context = context;
}
public GetPropertiesResponse GetAllPropertiesByCity(int CityId)
{
//Create Required Objects for response
//wont support ref Objects through params
context.LoadStoredProc(SQL_STATEMENT)
.AddParam("CityID", CityId).Exec( r =>
{
while (r.Read())
{
ORMapping<GenericRespStatus> orm = new ORMapping<GenericRespStatus>();
orm.AssignObject(r, _Status);
}
if (r.NextResult())
{
while (r.Read())
{
Property = new Property();
ORMapping<Property> orm = new ORMapping<Property>();
orm.AssignObject(r, Property);
_propertyDetailsResult.Add(Property);
}
}
});
return new GetPropertiesResponse{Status=_Status,PropertyDetails=_propertyDetailsResult};
}
}
public class GetPropertiesResponse
{
public GenericRespStatus Status;
public List<Property> PropertyDetails;
public GetPropertiesResponse()
{
PropertyDetails = new List<Property>();
}
}
public class GenericRespStatus
{
public int ResCode { get; set; }
public string ResMsg { get; set; }
}
internal class ORMapping<T>
{
public void AssignObject(IDataReader record, T myClass)
{
PropertyInfo[] propertyInfos = typeof(T).GetProperties();
for (int i = 0; i < record.FieldCount; i++)
{
if (propertyInfos.Any(obj => obj.Name == record.GetName(i))) //&& record.GetValue(i) != DBNull.Value
{
propertyInfos.Single(obj => obj.Name == record.GetName(i)).SetValue(myClass, Convert.ChangeType(record.GetValue(i), record.GetFieldType(i)));
}
}
}
}
It's embarrassing how lacking the docs are on this. 😩
It took me a lot of hit and trial to figure this out 😪, so I wanted to document 📝 it here so someone else doesn't have to spend time figuring this out. I'm using Entity Framework Core Version 6.0.10.
Summary
There are 2 kinds of Stored Procedures that return result. And the approach to call them and get result is different from each other.
SPs that return table-based output.
SPs that return output in the output parameters.
SPs that return table based output
If your SP looks like this:
CREATE PROCEDURE dbo.MyTestSPToGetShopType #ShopId AS VARCHAR(25)
AS
SELECT ShopId, ShopType
FROM Shop
WHERE SHOPID = #ShopId
👇
Step 1:
Create a class based on your output
public class MyTestSpResult
{
public string ShopId { get; set; }
public string ShopType { get; set; }
}
Step 2:
Register this in your DbContext's protected override void OnModelCreating(ModelBuilder builder) method:
builder.Entity<MyTestSpResult>(e => e.HasNoKey());
Step 3:
Call the SP from the code, and get the result:
var myTestSpResult = await _dbContext.Set<MyTestSpResult>()
.FromSqlInterpolated($"EXEC dbo.MyTestSPToGetShopType #ShopId = \"A03\"")
.ToArrayAsync();
var myShopType = myTestSpResult[0].ShopType;
Alternate Way
Steps 1 and 2 are same as above.
Step 3:
Register the class from Step 1 in your DbContext:
public virtual DbSet<MyTestSpResult> MyTestSpResult { get; set; }
Step 4:
Call the SP from the code, and get the result:
var mySPResult = await _dbContext.MyTestSpResult
.FromSqlInterpolated($"EXEC dbo.MyTestSPToGetShopType #ShopId = \"A03\"")
.ToArrayAsync();
var myShopType = mySPResult[0].ShopType;
SPs that return output in the output parameters
If your SP looks like this:
CREATE PROCEDURE dbo.MyOtherTestSPToGetShopType
(
#ShopId VARCHAR(25)
,#ShopType VARCHAR(25) OUTPUT
)
AS
SELECT #ShopType = ShopType
FROM Shop
WHERE SHOPID = #ShopId
👇
Step 1:
Create the SQL parameters:
var shopId = new SqlParameter()
{
ParameterName = "#ShopId",
Value = "A03",
Direction = System.Data.ParameterDirection.Input,
SqlDbType = System.Data.SqlDbType.VarChar,
Size = 25
};
var shopType = new SqlParameter()
{
ParameterName = "#ShopType",
Direction = System.Data.ParameterDirection.Output,
SqlDbType = System.Data.SqlDbType.VarChar,
Size = 25
};
Step 2:
Call the SP from code using those parameters, and get the result:
await _dbContext.Database
.ExecuteSqlInterpolatedAsync($"EXEC dbo.MyOtherTestSPToGetShopType #ShopId = {shopId}, #ShopType = {shopType} OUT");
var myShopType = shopType.Value as string;
If you are executing a stored procedure from Informix using EntityFrameworkCore you need to include the command EXECUTE PROCEDURE
var spresult = _informixContext.procdata.FromSql("EXECUTE PROCEDURE dummyproc ({0},{1},{2})", parameters: new[] { p0, p1,p2 }).ToList();
Nothing have to do... when you are creating dbcontext for code first approach initialize
namespace below the fluent API area make list of sp and use it another place where you want.
public partial class JobScheduleSmsEntities : DbContext
{
public JobScheduleSmsEntities()
: base("name=JobScheduleSmsEntities")
{
Database.SetInitializer<JobScheduleSmsEntities>(new CreateDatabaseIfNotExists<JobScheduleSmsEntities>());
}
public virtual DbSet<Customer> Customers { get; set; }
public virtual DbSet<ReachargeDetail> ReachargeDetails { get; set; }
public virtual DbSet<RoleMaster> RoleMasters { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//modelBuilder.Types().Configure(t => t.MapToStoredProcedures());
//modelBuilder.Entity<RoleMaster>()
// .HasMany(e => e.Customers)
// .WithRequired(e => e.RoleMaster)
// .HasForeignKey(e => e.RoleID)
// .WillCascadeOnDelete(false);
}
public virtual List<Sp_CustomerDetails02> Sp_CustomerDetails()
{
//return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Sp_CustomerDetails02>("Sp_CustomerDetails");
// this.Database.SqlQuery<Sp_CustomerDetails02>("Sp_CustomerDetails");
using (JobScheduleSmsEntities db = new JobScheduleSmsEntities())
{
return db.Database.SqlQuery<Sp_CustomerDetails02>("Sp_CustomerDetails").ToList();
}
}
}
}
public partial class Sp_CustomerDetails02
{
public long? ID { get; set; }
public string Name { get; set; }
public string CustomerID { get; set; }
public long? CustID { get; set; }
public long? Customer_ID { get; set; }
public decimal? Amount { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int? CountDay { get; set; }
public int? EndDateCountDay { get; set; }
public DateTime? RenewDate { get; set; }
public bool? IsSMS { get; set; }
public bool? IsActive { get; set; }
public string Contact { get; set; }
}
I'm using Entity Framework Core. The support for stored procedures and adhoc queries doesn't feel as fluid as it does in Framework.
Here are some examples for future reference:
Populate a list of entities from the results of a stored procedure:
The [dbo].[GetUnarchivedJobs] stored procedure returns a list of records that matches the Job entity.
We can use the FromSqlInterpolated() method on the Jobs property to call the stored procedure and have a list of Job returned.
NoTracking() is used to speed up the performance, and in this case we wont be updating the entities.
using Microsoft.EntityFrameworkCore;
public class DbContext : Microsoft.EntityFrameworkCore.DbContext
{
protected DbSet<Job> Jobs { get; set; }
// Populate a list of entities from the results of a stored procedure
public Task<List<Job>> GetUnarchivedJobs(int maxQty, CancellationToken cancellationToken) =>
Jobs.FromSqlInterpolated($"EXEC [dbo].[GetUnarchivedJobs] #MaxQty = {maxQty}")
.AsNoTracking()
.ToListAsync(cancellationToken)
;
public DbContext(DbContextOptions<DbContext> options) : base(options) { }
}
Send an array of integers to a stored procedure:
The [dbo].[SetJobListArchiveFlags] stored procedure has a single parameter of type integer_list_tbltype.
We need to create a DataTable to match the integer_list_tbltype type which has a single column named n.
The int values need to be added to the DataTable.
A SqlParameter is used to pass the populated DataTable to the stored procedure.
Because we are not populating any entities, we need to use methods on the Database property to call the stored procedure.
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System.Data;
public class DbContext : Microsoft.EntityFrameworkCore.DbContext
{
// Send an array of integers to a stored procedure
public async Task<int> MarkJobsAsArchived(IEnumerable<int> jobIds, CancellationToken cancellationToken)
{
// This DataTable matches the `integer_list_tbltype` db type
var table = new DataTable();
table.Columns.Add("n", typeof(int));
foreach (var id in jobIds) table.Rows.Add(id);
var parameter = new SqlParameter("#jobIds", SqlDbType.Structured);
parameter.Value = table;
parameter.TypeName = "integer_list_tbltype";
var rowsUpdatedCount = await Database.ExecuteSqlRawAsync("EXEC [dbo].[SetJobListArchiveFlags] #jobIds", new[] { parameter }, cancellationToken);
return rowsUpdatedCount;
}
}
From EF core 3.0 onwards, we can also use the following commands:
dbContext.Database.ExecuteSqlInterpolated():
var name = "Ramin";
var outArticleCount = new SqlParameter(
"#outArticleCount",
SqlDbType.Int) { Direction = ParameterDirection.Output };
dbContext.Database.ExecuteSqlInterpolated(
$"exec dbo.MySp {name}, {outputArticleCount} out");
dbContext.Database.ExecuteSqlInterpolatedAsync():
dbContext.Database.ExecuteSqlRaw():
SqlParameter[] parameters =
{
new SqlParameter("#name", SqlDbType.Varchar { Direction = ParameterDirection.Input, Value = "Ramin" },
new SqlParameter("#outArticleCount", SqlDbType.Int { Direction = ParameterDirection.Output }
}
dbContext.Database.ExecuteSqlRaw("exec dbo.MySp #name, #outArticleCount OUTPUT", parameters);
dbContext.Database.ExecuteSqlRawAsync()
Raw and Interpolated versions of above methods are equal but Interpolated versions are safe against SQL Injection attacks (read more here and here).
We should create a property with DbQuery not DbSet in the model for the db context like below...
public class MyContextContext : DbContext
{
public virtual DbQuery<CheckoutInvoiceModel> CheckoutInvoice { get; set; }
}
After than a method that can be used to return result
public async Task<IEnumerable<CheckoutInvoiceModel>> GetLabReceiptByReceiptNo(string labReceiptNo)
{
var listing = new List<CheckoutInvoiceModel>();
try
{
var sqlCommand = $#"[dbo].[Checkout_GetLabReceiptByReceiptNo] {labReceiptNo}";
listing = await db.Set<CheckoutInvoiceModel>().FromSqlRaw(sqlCommand).ToListAsync();
}
catch (Exception ex)
{
return null;
}
return listing;
}
From above example, we can use any one option you like.
Hope this helpful for you!
I need to make a query against a document collection that matches several properties.
(Cross post from the mailing list: https://groups.google.com/forum/?fromgroups=#!topic/ravendb/r5f1zr2jd_o)
Here is the document:
public class SessionToken
{
[JsonProperty("jti")]
public string Id { get; set; }
[JsonProperty("aud")]
public Uri Audience { get; set; }
[JsonProperty("sub")]
public string Subject { get; set; }
[JsonProperty("claims")]
public Dictionary<string, string> Claims { get; set; }
}
And here is the test:
[TestFixture]
public class RavenDbTests
{
private IDocumentStore documentStore;
[SetUp]
public void SetUp()
{
this.documentStore = new EmbeddableDocumentStore() { RunInMemory = true };
this.documentStore.Initialize();
}
[Test]
public async void FirstOrDefault_WhenSessionTokenExists_ShouldReturnSessionToken()
{
var c = new SessionToken()
{
Audience = new Uri("http://localhost"),
Subject = "NUnit",
Claims = new Dictionary<string, string>()
{
{ ClaimTypes.System, "NUnit" }
}
};
using (var session = this.documentStore.OpenAsyncSession())
{
await session.StoreAsync(c);
await session.SaveChangesAsync();
// Check if the token exists in the database without using Where clause
var allTokens = await session.Query<SessionToken>().ToListAsync();
Assert.That(allTokens.Any(x => x.Subject == "NUnit" && x.Audience == new Uri("http://localhost")));
// Try getting token back with Where clause
var token = await session.Query<SessionToken>().Customize(x => x.WaitForNonStaleResults()).Where(x => x.Subject == "NUnit" && x.Audience == new Uri("http://localhost")).ToListAsync();
Assert.IsNotNullOrEmpty(token.First().Id);
}
}
}
The last Assert is the one that is failing.
I must admit Im not sure whether this is a bug or a failure on my part.
As far as I understand, this is supposed to work.
PS. I´ve tried with a standalone document store as well as embedded without running in memory, but with same result.
You are getting stale results. In a unit test, you need to allow time for indexing to occur.
Add .Customize(x=> x.WaitForNonStaleResults()) to your queries and the test should pass.
Also, I think you left the Id property off your question when you cut/paste because it doesn't compile as-is.
UPDATE
Per discussion in comments, the issue was that you were applying the [JsonProperty] attribute to the Id property. Since the Id property represents the document key, and is not serialized as part of the JSON document, you can't apply the [JsonProperty] attribute to it.
I am relatively new to REST and WebAPI by Microsoft. We are implementing a hub REST service that will house several types of object gets and sets. Being the lead on the project, I am being tasked with coming up with the proper Uri design we are going with. I was wondering what thoughts were on war is better. Yes I specifically phased that without using the word "standard".
Here are the options my team and I are currently entertaining:
Http://servername/API/REST/Ldap/AD/employees?username=jsmith
Http://servername/API/REST/Ldap/AD/employee/UserName?searchTerm=jsmith (this seems RPC to me)
Http://servername/API/REST/Ldap/AD/employees/getusername?searchterm?jsmith
We are also creating a Soap version hence the rest in the Uri.
Thanks for the input
I recommend you take a look at Web API Design from Brian Mulloy. He has several recommendations when it comes to searching and filtering.
Simplify associations - sweep complexity under the ‘?’
Most APIs have intricacies beyond the base level of a resource.
Complexities can include many states that can be updated, changed,
queried, as well as the attributes associated with a resource. Make it
simple for developers to use the base URL by putting optional states
and attributes behind the HTTP question mark. Keep your API intuitive
by simplifying the associations between resources, and sweeping
parameters and other complexities under the rug of the HTTP question
mark.
Tips for search
While a simple search could be modeled as a resourceful API (for
example, dogs/?q=red), a more complex search across multiple resources
requires a different design. If you want to do a global search across
resources, we suggest you follow the Google model:
Global search
/search?q=fluffy+fur
Here, search is the verb; ?q represents the query.
Scoped search
To add scope to your search, you can prepend with the scope of the
search. For example, search in dogs owned by resource ID 5678
/owners/5678/dogs?q=fluffy+fur
Notice that the explicit search has been dropped from the URL and
instead it relies on the parameter 'q' to indicate the scoped query.
Pagination and partial response
Support partial response by adding optional fields in a comma
delimited list.
/dogs?fields=name,color,location
Use limit and offset to make it easy for developers to paginate
objects.
/dogs?limit=25&offset=50
To Oppositional's comment, this is what I put together some time ago.
https://groups.google.com/d/msg/servicestack/uoMzASmvxho/CtqpZdju7NcJ
public class QueryBase
{
public string Query { get; set; }
public int Limit { get; set; }
public int Offset { get; set; }
}
[Route("/v1/users")]
public class User : IReturn<List<User>>
{
public string Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public class RequestFilterAttribute : Attribute, IHasRequestFilter
{
#region IHasRequestFilter Members
public IHasRequestFilter Copy()
{
return this;
}
public int Priority
{
get { return -100; }
}
public void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)
{
var query = req.QueryString["q"] ?? req.QueryString["query"];
var limit = req.QueryString["limit"];
var offset = req.QueryString["offset"];
var user = requestDto as QueryBase;
if (user == null) { return; }
user.Query = query;
user.Limit = limit.IsEmpty() ? int.MaxValue : int.Parse(limit);
user.Offset = offset.IsEmpty() ? 0 : int.Parse(offset);
}
#endregion
}
[Route("/v1/users/search", "GET")]
[RequestFilter]
public class SearchUser : QueryBase, IReturn<PagedResult<User>> { }
public class UsersService : Service
{
public static List<User> UserRepository = new List<User>
{
new User{ Id="1", FirstName = "Michael", LastName = "A", Email = "michaelEmail" },
new User{ Id="2", FirstName = "Robert", LastName = "B", Email = "RobertEmail" },
new User{ Id="3", FirstName = "Khris", LastName = "C", Email = "KhrisEmail" },
new User{ Id="4", FirstName = "John", LastName = "D", Email = "JohnEmail" },
new User{ Id="4", FirstName = "Lisa", LastName = "E", Email = "LisaEmail" }
};
public PagedResult<User> Get(SearchUser request)
{
var query = request.Query;
var users = request.Query.IsNullOrEmpty()
? UserRepository.ToList()
: UserRepository.Where(x => x.FirstName.Contains(query) || x.LastName.Contains(query) || x.Email.Contains(query)).ToList();
var totalItems = users.Count;
var totalPages = (int)Math.Ceiling((decimal)totalItems / (decimal)request.Limit);
var currentPage = request.Offset;
users = users.Skip(request.Offset * request.Limit).Take(request.Limit).ToList();
var itemCount = users.Count;
return new PagedResult<User>
{
TotalItems = totalItems,
TotalPages = totalPages,
ItemCount = itemCount,
Items = users,
CurrentPage = currentPage
};
}
}