Fakes: Downcast SqlConnection to DbConnection - c#

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());
}
}
}

Related

How to mock SQL Connection in C# Nunit Testing?

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?

Entity Framework Core for custom MySQL Functions, Literals vs Columns

I'm attempting to map a MySQL function in EF Core 3.1, .NET Core 3.1. More specifically, I'm trying to map a parameter of type params object[].
The example code below uses CONCAT() for illustration purposes.
After some fiddling around with HasTranslation() and a custom RelationalTypeMapping it works with literals. Meaning it produces the correct SQL query and will execute that query successfully.
As soon as I pass a column it fails to produce the correct SQL query and will throw the NotSupportedException upon execution.
There are two issues I'm trying to resolve at this stage:
Passing any column will never hit my function configuration, and by extension, my translation.
Passing any column results in various (and weird) behaviors with other parameters. This could likely be easily resolved after solving #1, but it's interesting, nonetheless. (i.e. "asdf" vs (object)"asdf")
The ToSql() extension method used for debugging is from https://stackoverflow.com/a/67901042/1048799
The output is commented in the code.
Packages
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="MySql.EntityFrameworkCore" Version="3.1.17" />
EfTestService.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace EfTest
{
public interface IEfTestService
{
Task RunAsync();
}
public class EfTestService : IEfTestService
{
private readonly ILogger<IEfTestService> logger;
private readonly EfTestDbContext dbContext;
public EfTestService(ILogger<IEfTestService> logger, EfTestDbContext dbContext)
{
this.logger = logger;
this.dbContext = dbContext;
}
public async Task RunAsync()
{
var concat0 = dbContext.TestRecords.Select(r => dbContext.Concat(0, 1, "2", 3.0d, 4.0m, "five"));
var concat0Sql = concat0.ToSql();
//var concat0Result = await concat0.FirstAsync();
Console.WriteLine(concat0Sql);
/*
* works as expected
*
SELECT `CONCAT`(0, 1, '2', 3, 4.0, 'five')
FROM `TestRecords` AS `t`
*/
var concat1 = dbContext.TestRecords.Select(r => dbContext.Concat(r.TestRecordId));
var concat1Sql = concat1.ToSql();
//var concat1Result = await concat1.FirstAsync();
Console.WriteLine(concat1Sql);
/*
* not CONCAT
*
SELECT `t`.`TestRecordId`
FROM `TestRecords` AS `t`
*/
var concat2 = dbContext.TestRecords.Select(r => dbContext.Concat(r.TestRecordId, 0.1));
var concat2Sql = concat2.ToSql();
//var concat2Result = await concat2.FirstAsync();
Console.WriteLine(concat2Sql);
/*
* not CONCAT, 0.1 is included
*
SELECT `t`.`TestRecordId`, 0.1
FROM `TestRecords` AS `t`
*/
var concat3 = dbContext.TestRecords.Select(r => dbContext.Concat(r.TestRecordId, "asdf"));
var concat3Sql = concat3.ToSql();
//var concat3Result = await concat3.FirstAsync();
Console.WriteLine(concat3Sql);
/*
* not CONCAT, asdf is NOT included
*
SELECT `t`.`TestRecordId`
FROM `TestRecords` AS `t`
*/
var concat4 = dbContext.TestRecords.Select(r => dbContext.Concat(r.TestRecordId, (object)"asdf"));
var concat4Sql = concat4.ToSql();
//var concat4Result = await concat4.FirstAsync();
Console.WriteLine(concat4Sql);
/*
* not CONCAT, asdf is included
*
SELECT `t`.`TestRecordId`, 'asdf'
FROM `TestRecords` AS `t`
*/
}
}
}
EfTestDbContext.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Linq;
using System.Linq.Expressions;
namespace EfTest
{
public class EfTestDbContext : DbContext
{
public EfTestDbContext(DbContextOptions options) : base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder
.HasDbFunction(() => Concat(default))
.HasTranslation(expressions =>
{ /* breakpoint, hit when passing only literals */
if (expressions.First() is SqlConstantExpression expression)
{
if (expression.Value is object[] #params)
{
var args = #params.Select(p => new SqlConstantExpression(Expression.Constant(p), ObjectTypeMapping.Instance));
return SqlFunctionExpression.Create("`CONCAT`", args, typeof(string), null);
}
}
throw new InvalidOperationException();
})
.HasParameter("vals").Metadata.TypeMapping = RelationalTypeMapping.NullMapping;
}
[DbFunction("CONCAT")]
public string Concat(params object[] vals)
=> throw new NotSupportedException(); /* thown at execution when passing a column */
public DbSet<TestRecord> TestRecords { get; set; }
}
public class TestRecord
{
public int TestRecordId { get; set; }
}
}
ObjectTypeMapping.cs
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Text.RegularExpressions;
namespace EfTest
{
public class ObjectTypeMapping : RelationalTypeMapping
{
public static readonly ObjectTypeMapping Instance = new ObjectTypeMapping();
public ObjectTypeMapping() : base("object", typeof(object), System.Data.DbType.Object, true) { }
protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
=> throw new NotImplementedException();
public override string GenerateSqlLiteral(object value)
{
if (value is string strValue)
{
strValue = Regex.Replace(strValue, #"([\\])", #"\$1");
if (!strValue.Contains("'"))
{
return $"'{strValue}'";
}
else if (!strValue.Contains('"'))
{
return $"\"{strValue}\"";
}
else
{
strValue = Regex.Replace(strValue, "(['\"])", #"\$1");
return $"'{strValue}'";
}
}
return base.GenerateSqlLiteral(value);
}
}
}
Program.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading.Tasks;
namespace EfTest
{
class Program
{
static async Task Main(string[] args)
{
IHost host = null;
try
{
host = CreateHostBuilder(null).Build();
var efTestService = ActivatorUtilities.GetServiceOrCreateInstance<IEfTestService>(host.Services);
await host.StartAsync();
await efTestService.RunAsync();
await host.WaitForShutdownAsync();
}
finally
{
host?.Dispose();
}
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
var builder = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddDbContext<EfTestDbContext>(
options => options.UseMySQL(
context.Configuration.GetConnectionString("DefaultConnection")
)
);
services.AddSingleton<IEfTestService, EfTestService>();
});
return builder;
}
}
}
Passing any column will never hit my function configuration, and by extension, my translation.
It is impossible to map params object[] to any EF class directly.
Each parameter must be well defined with a type and count.
Unwind the variations of input and cast to the appropriate EF structure/class prior to mapping.
Yes, lots of data and mapping classes.

Unsupported expression error: m => m when running tests in visual studio

I'm learning ASP.NET and I came across the following error. I'm learning to work with aspnet and I'm learning the testing part.
Below is the error that is giving and my source code.
System.NotSupportedException : Unsupported expression: m =>
m.Categorias Non-overridable members (here: Context.get_Categorias)
may not be used in setup / verification expressions.
using CursoMVC.Models;
using CursosApi.Controllers;
using Microsoft.EntityFrameworkCore;
using Moq;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace TesteCurso
{
public class CategoriasControllerTest
{
private readonly Mock<DbSet<Categoria>> _mockSet;
private readonly Mock<Context> _mockContext;
private readonly Categoria _categoria;
public CategoriasControllerTest()
{
_mockSet = new Mock<DbSet<Categoria>>();
_mockContext = new Mock<Context>();
_categoria = new Categoria {Id=1, Descricao = "Teste Categoria"};
_mockContext.Setup(m => m.Categorias).Returns(_mockSet.Object);
_mockContext.Setup(m => m.Categorias.FindAsync(1)).ReturnsAsync(_categoria);
//_mockContext.Setup(m => m.SetModified(_categoria));
//
// _mockContext.Setup(m => m.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1);
}
[Fact]
public async Task Get_Categoria()
{
var service = new CategoriasController(_mockContext.Object);
await service.GetCategoria(1);
_mockSet.Verify(m => m.FindAsync(1),Times.Once);
}
}
}
can you help me?

Mock DataAccess methods that uses Enterprise Library to return IDataReader

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.

Linq-ing Calculated Properties in nhibernate could not resolve property Error

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>();

Categories

Resources