Here is my method file looks like
`
public List<NewAssetChangeApproval> GetNewAssetChangeApprovals(int userId, bool isHistory)
{
List<NewAssetChangeApproval> nac;
SqlParameter[] parms =
{
new SqlParameter ("changeType", "Asset"),
new SqlParameter ("userId", userId)
};
if (isHistory)
nac = DbContext.Database.SqlQuery<NewAssetChangeApproval>("EXEC AM.GetAssetChangeApprovalsHistory #changeType, #userId", parms).ToList();
else
nac = DbContext.Database.SqlQuery<NewAssetChangeApproval>("EXEC AM.GetAssetChangeApprovals #changeType, #userId", parms).ToList();
return nac;
}
`
I Am unable to call the SQL connection from this method.
I tried using FakeDB and Test base files. below is my Test Base.
`
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Teneo.Core.Models.Database.Parser;
namespace Teneo.ParserFileDirector.Test.Common
{
public class TestBase
{
private bool useSqlite;
public ParserDataContext GetDbContext()
{
var builder = new DbContextOptionsBuilder<ParserDataContext>();
if (useSqlite)
{
// Use SqliteDb..
builder.UseSqlite("Datasource=:memory:", x => { });
}
else
{
// Use In-memory Db...
builder.UseInMemoryDatabase(Guid.NewGuid().ToString());
}
var dbcontext = new ParserDataContext(builder.Options);
if (useSqlite)
{
dbcontext.Database.OpenConnection();
}
else { dbcontext.Database.EnsureCreated(); }
// dbcontext.Database.EnsureCreated();
return dbcontext;
}
public void UseSqlite()
{
useSqlite = true;
}
}
}
`
I tried using the above TestBase i have created but I am unable to call it properly. is there is nay way to call the SQL connections?
Related
I have setup a generic query executer using dapper but I wonder if there are ways to create mapper for object contain list of object using stored procedures.
For example:
I need to retrieve company and related product
public class company
{
public List<Product> products {get;set;}
}
public static async Task<List<DTO>> ExecuteQueryAsync<DTO>(string query , object param) where DTO : class, new()
{
List<DTO> result = null;
try
{
var connection = new DbConnection().GetConnection();
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
if (connection.State == ConnectionState.Open)
{
result = await SqlMapper.QueryAsync<DTO>(connection, query,param) as List<DTO>;
connection.Close();
}
}
catch (Exception ex)
{
throw ex;
}
return result;
}
You can make your code much "cleaner" by injecting the method to handle the GridReader to Poco's.
This allows separation of concerns.
"DataLayer" object
using Dapper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
namespace MyNamespace.DataLayer
{
public class MyCustomObjectData : IMyCustomObjectData
{
public async Task<ICollection<MyCustomObject>> MyMethodAsync(Func<GridReader, ICollection<MyCustomObject>> handleFunction)
{
ICollection<MyCustomObject> returnItems = null;
string sqlProcedureName = "dbo.uspMyCustomObjectSelectStuff";
try
{
using (IDbConnection dbConnection = /* your code here */)
{
DynamicParameters parameters = new DynamicParameters();
parameters.Add(/* your code here */);
GridReader gr = await dbConnection.QueryMultipleAsync(sqlProcedureName, parameters, commandType: CommandType.StoredProcedure, commandTimeout: 120);
if (null != handleFunction)
{
returnItems = handleFunction(gr);
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return returnItems;
}
}
}
"DomainDataLayer" object
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static Dapper.SqlMapper;
namespace MyNamespace.DomainDataLayer
{
public class MyCustomObjectDomainData : IMyCustomObjectDomainData
{
public MyCustomObjectDomainData(IMyCustomObjectData crgDataLayer)
{
this.MyCustomObjectData = crgDataLayer ?? throw new ArgumentNullException("IMyCustomObjectData is null");
}
public async Task<ICollection<MyCustomObject>> MyCustomObjectGetMethodAsync()
{
ICollection<MyCustomObject> returnCollection = null;
/* CALL the datalayer, but INJECT the method to handle the GridReader */
returnCollection = await this.MyCustomObjectData.MyMethodAsync(this.HandleMyCustomObjectGridReaderResult);
return returnCollection;
}
public ICollection<MyCustomObject> HandleMyCustomObjectGridReaderResult(GridReader gr)
{
ICollection<MyCustomObject> returnCollection = null;
using (gr)
{
/* Get objects from each SELECT statement in the stored procedure */
returnCollection = gr.Read<MyCustomObject>().ToList();
/* this would be how to handle a SECOND "select" statement in the stored procedure */
IEnumerable<MyOtherCustomObjectFromASecondStoredProcedureSelectStatement> otherThings = gr.Read<MyOtherCustomObjectFromASecondStoredProcedureSelectStatement>().ToList();
/* optionally, you can hand-map the pocos to each other */
//returnCollection = new MyCustomObjectObjectMapper().MapMultipleMyCustomObject(returnCollection, otherThings);
}
return returnCollection;
}
}
}
I was given a task to rewrite an Api and corresponding unit tests. I am still quite new to unit testing and so I am struggling to write the test code for a data access class that uses Enterprise Library. Here is the code:
namespace TOS_Landside_BL.Classes {
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Interfaces;
public class DataAccess : IDataAccess {
private readonly Database yasDB;
public DataAccess () {
DatabaseProviderFactory factory = new DatabaseProviderFactory ();
DatabaseFactory.SetDatabaseProviderFactory (factory, false);
yasDB = factory.Create ("AutomationDB");
}
// caller is responsible for closing the reader.
public IDataReader ExecuteReaderForStoredProcedure (string cmdName, List<DbParameter> parameters = null) {
DbCommand cmd = yasDB.GetStoredProcCommand (cmdName);
if (parameters != null)
cmd.Parameters.AddRange (parameters.ToArray ());
return yasDB.ExecuteReader (cmd);
}
public IDataReader ExecuteReaderForSqlStringCommand (string cmdScript, List <DbParameter> parameters = null) {
DbCommand cmd = yasDB.GetSqlStringCommand (cmdScript);
if (parameters != null) {
cmd.Parameters.AddRange(parameters.ToArray());
}
return yasDB.ExecuteReader (cmd);
}
}
}
Here is the interface IDataAccess:
namespace TOS_Landside_BL.Interfaces {
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
public interface IDataAccess {
IDataReader ExecuteReaderForStoredProcedure (string cmdName, List<DbParameter> parameters = null);
IDataReader ExecuteReaderForSqlStringCommand (string cmdScript, List <DbParameter> parameters = null);
}
}
And here is a sample on how I would call it:
namespace TOS_Landside_BL.Data {
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using Classes;
using Enums;
using Factories;
using Interfaces;
public class LandsideTransactionDataProvider {
private readonly IDataAccess _dataAccess;
public LandsideTransactionDataProvider (IDataAccess dataAccess = null)
{
_dataAccess = dataAccess ?? new DataAccess ();
}
public LandsideTransaction GetLandsideTransaction (string keyword, bool isKioskID = false)
{
var strCmd = isKioskID ? "proc_get_landside_transaction_by_kiosk_id" : "[LandSide].[GetLandsideTransactionDetail]";
var parameters = new List <DbParameter> () {
new SqlParameter ("#keyword", SqlDbType.NVarChar, 11) {Value = keyword}
};
using (var reader = _dataAccess.ExecuteReaderForStoredProcedure (strCmd, parameters)) {
return reader.Read () ? reader.CreateLandSideTransaction () : null;
}
}
}
In the SetUp method of my test class, I tried this:
var dataAccess = new Mock<IDataAccess>();
dataAccess.SetUp(d => d.ExecuteReaderFromStoredProcedure(null, null)).Returns(IDataReader);
but it won't accept IDataReader or any concrete data reader classes.
Please advise as to what I'm doing wrong, and any suggestion would really be appreciated. Thank you.
You can create a mock for the IDataReader and set it up for the test as well.
var reader = new Mock<IDataReader>();
//..Setup the reader as expected for the test.
//Like reader.Read(), etc.
var dataAccess = new Mock<IDataAccess>();
dataAccess
.Setup(_ => _.ExecuteReaderForStoredProcedure(It.IsAny<string>(), It.IsAny<List<DbParameter>>()))
.Returns(reader.Object);//return mocked reader.
How can I get this fake to work? I'd expect the last Assert to pass.
System.Data.fakes
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
<Assembly Name="System.Data" Version="4.0.0.0"/>
</Fakes>
Test.cs
using System.Data.Common;
using System.Data.SqlClient;
using System.Data.SqlClient.Fakes;
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class FakeTest
{
[TestMethod]
public void DownCast()
{
using (ShimsContext.Create())
{
SqlConnection sqlCn = new ShimSqlConnection
{
CreateCommand = () => new ShimSqlCommand(),
CreateDbCommand = () => new ShimSqlCommand()
};
Assert.IsNotNull(sqlCn.CreateCommand());
DbConnection dbCn = sqlCn;
Assert.IsNotNull(dbCn.CreateCommand()); // How can I make this pass?
}
}
}
Adding the line new ShimDbConnection(sqlCn) { CreateCommand = () => new ShimSqlCommand() }; after the initial setup allows the test to pass.
using System.Data.Common;
using System.Data.Common.Fakes;
using System.Data.SqlClient;
using System.Data.SqlClient.Fakes;
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class FakeTest
{
[TestMethod]
public void DownCast()
{
using (ShimsContext.Create())
{
SqlConnection sqlCn = new ShimSqlConnection
{
CreateCommand = () => new ShimSqlCommand(),
CreateDbCommand = () => new ShimSqlCommand()
};
new ShimDbConnection(sqlCn) { CreateCommand = () => new ShimSqlCommand() }; // Adding this line, the test passes.
Assert.IsNotNull(sqlCn.CreateCommand());
DbConnection dbCn = sqlCn;
Assert.IsNotNull(dbCn.CreateCommand());
}
}
}
I'm learning how to create a local database on mobile devices by using SQLite.Net.Async. I'm following a tutorial step by step but something wrong with my database path. The error message says 'Argument 1 cannot convert from string to System.Func SQLite.Net.SQLiteConnectionWithLock'. I was confused because this code works well on the tutorial sample.
This is my code:
using SQLite.Net.Async;
namespace ToDoList
{
public class TodoItemDatabase
{
readonly SQLiteAsyncConnection asyncdatabase;
public TodoItemDatabase (string dbPath)
{
asyncdatabase = new SQLiteAsyncConnection(dbPath);
//I haven't done further because I was stucked by dbPath error
}
}
}
The sample code works well:
using System.Collections.Generic;
using System.Threading.Tasks;
using SQLite;
namespace Todo
{
public class TodoItemDatabase
{
readonly SQLiteAsyncConnection database;
public TodoItemDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<TodoItem>().Wait();
}
public Task<List<TodoItem>> GetItemsAsync()
{
return database.Table<TodoItem>().ToListAsync();
}
public Task<List<TodoItem>> GetItemsNotDoneAsync()
{
return database.QueryAsync<TodoItem>("SELECT * FROM [TodoItem] WHERE [Done] = 0");
}
public Task<TodoItem> GetItemAsync(int id)
{
return database.Table<TodoItem>().Where(i => i.ID == id).FirstOrDefaultAsync();
}
public Task<int> SaveItemAsync(TodoItem item)
{
if (item.ID != 0)
{
return database.UpdateAsync(item);
}
else {
return database.InsertAsync(item);
}
}
public Task<int> DeleteItemAsync(TodoItem item)
{
return database.DeleteAsync(item);
}
}
}
This is the package which is been used in the sample application sqlite-net-pcl
I have been trying to create a calculated property in my persistence layer by follow
Hendry Luk's solution for calculated properties.
I am able to select the values from the DB using a linq query:
var result = from parcel in Repository.Query();
When I try to do a where on the selected result, I get a could not resolve property error.
Here is what my code looks like.
My Model:
namespace ConsoleApplication14
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
public class Common1 : ICommon
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
//public static readonly Expression<Func<Common1, string>> CalculatedDisplayExpression = x => ("Common 1 Display: " + x.Id + " - " + x.Name);
public static readonly Expression<Func<Common1, string>> CalculatedDisplayExpression = x => (x.Id + "");
private static readonly Func<Common1, string> CalculateDisplay = CalculatedDisplayExpression.Compile();
public virtual string Display { get { return CalculateDisplay(this); } }
}
}
My Interface the model implementing:
namespace ConsoleApplication14
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;`enter code here`
public interface ICommon
{
int Id { get; set; }
string Name { get; set; }
string Display { get; }
}
}
Model's mapping
namespace ConsoleApplication14
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
public class Common1Map : ClassMapping<Common1>
{
public Common1Map()
{
Id(x => x.Id, map => map.Generator(Generators.Native));
Property(x => x.Name);
}
}
}
namespace ConsoleApplication14
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using NHibernate.Hql.Ast;
using NHibernate.Linq;
using NHibernate.Linq.Functions;
using NHibernate.Linq.Visitors;
public class CalculatedPropertyGenerator<T, TResult> : BaseHqlGeneratorForProperty
{
public static void Register(ILinqToHqlGeneratorsRegistry registry, Expression<Func<T, TResult>> property, Expression<Func<T, TResult>> calculationExp)
{
registry.RegisterGenerator(ReflectionHelper.GetProperty(property), new CalculatedPropertyGenerator<T, TResult> { _calculationExp = calculationExp });
}
private CalculatedPropertyGenerator() { }
private Expression<Func<T, TResult>> _calculationExp;
public override HqlTreeNode BuildHql(MemberInfo member, Expression expression, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
return visitor.Visit(_calculationExp);
}
}
}
namespace ConsoleApplication14
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Tool.hbm2ddl;
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping;
using Iesi.Collections.Generic;
using System.Reflection;
using NHibernate.Linq.Functions;
using NHibernate.Linq;
public class SessionProvider
{
private static ISessionFactory sessionFactory;
public static SessionProvider Instance { get; private set; }
//DefaultLinqToHqlGeneratorsRegistry registry = new DefaultLinqToHqlGeneratorsRegistry();
ILinqToHqlGeneratorsRegistry registry = new DefaultLinqToHqlGeneratorsRegistry();
static SessionProvider()
{
var provider = new SessionProvider();
provider.Initialize();
Instance = provider;
}
private SessionProvider() { }
private void Initialize()
{
const string connString = "server=(local)\\mssql2008;database=Common;integrated security=sspi";
Configuration configuration = new Configuration();
configuration
.DataBaseIntegration(db =>
{
db.ConnectionString = connString;
db.Dialect<MsSql2008Dialect>();
db.Driver<SqlClientDriver>();
db.LogSqlInConsole = true;
db.IsolationLevel = System.Data.IsolationLevel.ReadCommitted;
})
.AddDeserializedMapping(GetMappings(), null);
CalculatedPropertyGenerator<Common1, string>.Register(registry, x => x.Display, Common1.CalculatedDisplayExpression);
// registry.RegisterGenerator(ReflectionHelper.GetProperty<Common1, string>(x => x.Display), new CalculatedPropertyGenerator());
var exporter = new SchemaExport(configuration);
exporter.Execute(true, true, false);
sessionFactory = configuration.BuildSessionFactory();
}
private HbmMapping GetMappings()
{
ModelMapper mapper = new ModelMapper();
mapper.AddMappings(Assembly.GetAssembly(typeof(Common1Map)).GetExportedTypes());
HbmMapping mappings = mapper.CompileMappingForAllExplicitlyAddedEntities();
return mappings;
}
public ISession OpenSession()
{
return sessionFactory.OpenSession();
}
}
}
Client:
namespace ConsoleApplication14
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Linq;
using NHibernate.Linq.Functions;
public class Tester
{
private ILinqToHqlGeneratorsRegistry registry = new DefaultLinqToHqlGeneratorsRegistry();
public void Go()
{
using (ISession session = SessionProvider.Instance.OpenSession())
{
CreateData(session);
IQueryable<ICommon> commons = session.Query<ICommon>();//.Where(x => x.Display.Contains("Common1 #7"));
var common1 = session.Query<Common1>().Where(x => x.Display.Contains("Common1 #7"));
foreach(var common in commons)
{
Console.WriteLine(common.Display);
}
}
}
private void CreateData(ISession session)
{
using (ITransaction tx = session.BeginTransaction())
{
for (int i = 0; i < 10; i++)
{
session.SaveOrUpdate(new Common1() { Name = "Common1 #" + i });
}
tx.Commit();
}
}
}
}
Register in SessionProvider class, like
configuration.LinqToHqlGeneratorsRegistry<MyLinqToHqlGeneratorsRegistry>();
MyLinqToHQLGeneratorRegistry implementation looks like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Linq.Functions;
using NHibernate.Linq;
namespace ConsoleApplication14
{
public class MyLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public MyLinqToHqlGeneratorsRegistry()
: base()
{
CalculatedPropertyGenerator<Common1, string>.Register(this, x => x.Display, Common1.CalculatedDisplayExpression);
}
}
}
You need to derive a class from DefaultLinqToHqlGeneratorsRegistry. Add the registration logic in its constructor (passing 'this' to CalculatedPropertyGenerator<>.Register()). Then register the class with the NHibernate configuration using:
cfg.LinqToHqlGeneratorsRegistry<OurLinqFunctionRegistry>();