Since i've updated EF Core from RC1 to RC2, the tests are passing on my local VS2015, but not in TFS Build (VSTest).
For the tests failing, i'm using the In-memory test provider as follows:
/// <summary>
/// Initializes the in-memory database, as recommended by the EF Core
/// documentation.
/// </summary>
private DbContextOptions CreateNewContextOptions()
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
var builder = new DbContextOptionsBuilder();
builder.UseInMemoryDatabase()
.UseInternalServiceProvider(serviceProvider);
return builder.Options;
}
[Test]
public void TestMethod()
{
//Arrange
var contextOptions = CreateNewContextOptions();
var dataMapper = new DAL.DataMappers.BuurtcampagneDataMapper(contextOptions);
//Act
dataMapper.Add(_buurtcampagne);
var count = dataMapper.FindAll();
//Assert
Assert.AreEqual(1, count);
}
The data access is as follows:
ctor blabla..
private DbContextOptions _options;
public IEnumerable<Buurtcampagne> FindAll()
{
using (var context = new BuurkrachtContext(_options))
{
var query = context.Buurtcampagnes
.ToList();
return query;
}
}
public void Add(Buurtcampagne buurtcampagne)
{
using (var context = new BuurkrachtContext(_options))
{
context.Buurtcampagnes.Add(buurtcampagne);
context.SaveChanges();
}
}
The error stack trace is as follows:
System.AppDomainUnloadedException : Attempted to access an unloaded AppDomain.
at System.Runtime.Remoting.ObjectHandle.Unwrap()
at Microsoft.Extensions.Caching.Memory.CacheEntryHelper.get_Scopes()
at Microsoft.Extensions.Caching.Memory.CacheEntryHelper.EnterScope(CacheEntry entry)
at Microsoft.Extensions.Caching.Memory.CacheEntry..ctor(Object key, Action`1 notifyCacheEntryDisposed, Action`1 notifyCacheOfExpiration)
at Microsoft.Extensions.Caching.Memory.MemoryCache.CreateEntry(Object key)
at Microsoft.Extensions.Caching.Memory.CacheExtensions.Set[TItem](IMemoryCache cache, Object key, TItem value)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQuery[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at Remotion.Linq.QueryableBase`1.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at InfoSupport.BSBuurkracht.DAL.DataMappers.BuurtcampagneDataMapper.FindAll() in D:\4\_work\2\s\BSBuurkracht\InfoSupport.BSBuurkracht.DAL\DataMappers\BuurtcampagneDataMapper.cs:line 62
at InfoSupport.BSBuurkracht.DALTests.EFCoreDataMapperTests.BuurtcampagneDataMapperTests.TestMethod() in D:\4\_work\2\s\BSBuurkracht\InfoSupport.BSBuurkracht.DALTests\EFCoreDataMapperTests\BuurtcampagneDataMapperTests.cs:line 49
Who can help me?
Related
I'm using MySQL with EF Core. I am currently using Pomelo Provider for MySQL. I need to implement Unit Of Work Pattern for transactions. I have a Service which calls two methods in repository. I am not able to implement nested transactions. This is how my method in service looks now:
public void methodA(param)
{
using (TransactionScope tx = new
TransactionScope(TransactionScopeOption.Required))
{
repo1.save(data1);
repo2.save(data2);
tx.complete();
}
}
This is how save method in repo1 is implemented
private readonly UserDbContext appDbContext;
public repo1(UserDbContext _appDbContext)
{
appDbContext = _appDbContext;
}
public void save(User entity)
{
var dbset = appDbContext.Set<User>().Add(entity);
appDbContext.SaveChanges();
}
This is how save method in repo2 is implemented
private readonly UserDbContext appDbContext;
public repo2(UserDbContext _appDbContext)
{
appDbContext = _appDbContext;
}
public void save(UserRole entity)
{
var dbset = appDbContext.Set<UserRole>().Add(entity);
appDbContext.SaveChanges();
}
I am getting the following error while running method in service:
Error generated for warning 'Microsoft.EntityFrameworkCore.Database.Transaction.AmbientTransactionWarning: An ambient transaction has been detected. The current provider does not support ambient transactions. See http://go.microsoft.com/fwlink/?LinkId=800142'. This exception can be suppressed or logged by passing event ID 'RelationalEventId.AmbientTransactionWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
This is how I registered UserDbContext in Startup.cs
services.AddDbContext<UserDbContext>(options => options.UseLazyLoadingProxies().UseMySql("Server = xxxx; Database = xxx; Uid = xx;ConnectionReset=True;", b => b.MigrationsAssembly("AssemblyName")));
I even tried adding a middleware which starts transaction at the begining of request and commits/rollbacks during the response . But still I am not able to manage nested transactions.
This is how my middleware looks:
public class TransactionPerRequestMiddleware
{
private readonly RequestDelegate next_;
public TransactionPerRequestMiddleware(RequestDelegate next)
{
next_ = next;
}
public async Task Invoke(HttpContext context, UserDbContext
userDbContext)
{
var transaction = userDbContext.Database.BeginTransaction(
System.Data.IsolationLevel.ReadCommitted);
await next_.Invoke(context);
int statusCode = context.Response.StatusCode;
if (statusCode == 200 || statusCode==302)
{
transaction.Commit();
}
else
{
transaction.Rollback();
}
}
}
Can anyone help me please?
I'm trying to run a simple test to check values in a properties file which I've saved in the src/test/resources folder of my Maven project but the JUnit test is failing. My test is picking up the File OK but it doesn't return the expected value as the file doesn't look like its getting loaded. Anyone else have a similar issue? My code/test are as follows:
My Application Context File:
<bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:test.properties"/>
</bean>
My Code:
#Resource(name = "myProps") private Properties myProps;
#Value("#{myProps['totalNumberOfChanges']}") private String totalNumberOfChangesStr;
#Value("#{myProps['delayTime']}") private String delayTimeStr;
public void parseAttributesFromConfigFile() {
String methodName = "parsePropertyAttributesFromConfigFile";
try {
totalNumberOfChanges = Integer.parseInt(totalNumberOfChangesStr);
delayTime = Integer.parseInt(delayTimeStr);
numEntriesToIterateThru = (totalNumberOfChanges / delayTime);
} catch (NumberFormatException nfe) {
LOGGER.error(methodName, "", "Number Format Exception Occured" + nfe.getMessage());
}
}
My Junit Test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:META-INF/spring/Testpu.xml" })
public class ConfigPropertiesTest {
private final int NUM_ENTRIES_TO_ITERATE_THRU = 100;
private final int TOTAL_NUMBER_OF_CHANGES = 100000;
private final int DELAY_TIME = 1000;
private ConfigProperties configProperties;
#Before
public void setUp() throws Exception {
configProperties = new ConfigProperties();
}
#Test
public final void testParseAttributesFromConfigFileIsCalled() {
configProperties.parseAttributesFromConfigFile();
int numEntriesToIterateOver = configProperties.getNumEntriesToIterateThru();
assertEquals(numEntriesToIterateOver, NUM_ENTRIES_TO_ITERATE_THRU);
int numberOfChanges = configProperties.getTotalNumberOfChanges();
assertEquals(numberOfChanges, TOTAL_NUMBER_OF_CHANGES);
int delayTime = configProperties.getDelayTime();
assertEquals(delayTime, DELAY_TIME);
}
}
You are creating the ConfigProperties class in your Before method. If you want Spring to populated values based on annotations the bean must be created as part of the Spring context. If you have an instance of ConfigProperties in your Spring context, load that instance into your test using #Autowired
I am trying to insert json data in mySQL database using camel and hibernate.
Everything is working.
for (Module module : modules) {
from("timer://foo?delay=10000")
.loop(7)//not working
.to(module.getUrl() + "/api/json")
.convertBodyTo(String.class)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
int index = (Integer)exchange.getProperty("CamelLoopIndex"); // not working
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(exchange.getIn().getBody().toString());
String[] lijst = {"lastBuild", "lastCompletedBuild", "lastFailedBuild", "lastStableBuild", "lastSuccessfulBuild", "lastUnstableBuild", "lastUnsuccessfulBuild"};
JSONObject obj = new JSONObject();
JsonNode node = root.get(lijst[index]);
JsonNode build = node.get("number");
obj.put("description", lijst[index]);
obj.put("buildNumber", build);
exchange.getIn().setBody(obj.toString());
}
})
.unmarshal(moduleDetail)
.to("hibernate:be.kdg.teamf.model.ModuleDetail")
.end();
}
When I debug, my CamelLoopIndex remains 0 so it is not incremented every time it goes through the loop.
All help is welcome!
In your case the only first instruction is processed in scope of the loop: .to(module.getUrl() + "/api/json"). You can add more instructions into a loop using Spring DSL, but I don't know how to declare a loop scope using Java DSL explicitly. I hope experts will explain more about a loop scope in Java DSL.
As a workaround I suggest to move all iteration instructions to a separate direct: route.
I can't reproduce your problem. This works:
from("restlet:http://localhost:9010}/loop?restletMethod=get")
.loop(7)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
int index = (int) exchange.getProperty("CamelLoopIndex");
exchange.getIn().setBody("index=" + index);
}
})
.convertBodyTo(String.class)
.end();
Output:
index=6
If I use this code in each Repository class then I get SQL profiling to work but I want to move that code from each class into the class where StructureMap handles the DB.
Example of a Repository class:
public DB CreateNewContext()
{
var sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["connection"].ConnectionString);
var profiledConnection = ProfiledDbConnection.Get(sqlConnection);
return DataContextUtils.CreateDataContext<DB>(profiledConnection);
}
public SqlRecipeRepository(DB dataContext)
{
_db = CreateNewContext();
}
Now I want the dataContext variable to be the profiled version and so come from my DBServiceRegistry class.
Here is the DBServiceRegistry class:
public class DBServiceRegistry : Registry
{
public DBServiceRegistry()
{
var sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["GetMeCooking.Data.Properties.Settings.server"].ConnectionString);
var profiledConnection = ProfiledDbConnection.Get(sqlConnection);
For<DB>().HybridHttpOrThreadLocalScoped().Use(() => DataContextUtils.CreateDataContext<DB>(profiledConnection));
//Original method just had this:
//For<DB>().HybridHttpOrThreadLocalScoped().Use(() => new DB());
}
}
This code does not cause any errors but I don't get the SQL profiling, what am I doing wrong?
The comment is correct, by creating the sql connection outwith the For line, you are overriding the scope command.
Far better to encapsulate the whole lot into an anonymous delegate
using System.Configuration;
using System.Data.SqlClient;
using System.Threading.Tasks;
using StructureMap;
using StructureMap.Configuration.DSL;
using Xunit;
public class DBServiceRegistry : Registry
{
private string connString = ConfigurationManager.ConnectionStrings["GetMeCooking.Data.Properties.Settings.server"].ConnectionString;
public DBServiceRegistry()
{
For<DB>().HybridHttpOrThreadLocalScoped().Use(
() =>
{
var sqlConnection = new SqlConnection(connString);
var profiledConnection = new StackExchange.Profiling.Data.ProfiledDbConnection(sqlConnection, MiniProfiler.Current);
return DataContextUtils.CreateDataContext<DB>(profiledConnection);
});
}
}
You can use unit tests to verify that the scope is correct (test syntax is xunit.net)
public class DBRegistryTests : IDisposable
{
private Container container;
public DBRegistryTests()
{
// Arrange (or test setup)
container = new Container(new DBServiceRegistry());
}
[Fact]
public void ConnectionsAreSameInThread()
{
// Create two connections on same thread
var conn1 = container.GetInstance<DB>();
var conn2 = container.GetInstance<DB>();
// Assert should be equal because hybrid thread is scope
// and test executes on same thread
Assert.Equal(conn1, conn2);
// Other assertions that connection is profiled
}
[Fact]
public void ConnectionAreNotSameInDifferentThreads()
{
var conn1 = container.GetInstance<DB>();
// Request second connection from a different thread
// (for < c# 4.0 use Thread instead of task)
var conn2 = new Task<DB>(() => this.container.GetInstance<DB>());
conn2.Start();
conn2.Wait();
// Assert that request from two different threads
// are not the same
Assert.NotEqual(conn1, conn2.Result);
}
public void Dispose()
{
// Test teardown
container.Dispose();
}
}
I am in the process of rewriting a bottle neck in the code of the project I am on, and in doing so I am creating a top level item that contains a self populating Ehcache. I am attempting to write a test to make sure that the basic call chain is established, but when the test executes it hands when retrieving the item from the cache.
Here are the Setup and the test, for reference mocking is being done with Mockito:
#Before
public void SetUp()
{
testCache = new Cache(getTestCacheConfiguration());
recordingFactory = new EntryCreationRecordingCache();
service = new Service<Request, Response>(testCache, recordingFactory);
}
#Test
public void retrievesResultsFromSuppliedCache()
{
ResultType resultType = mock(ResultType.class);
Response expectedResponse = mock(Response.class);
addToExpectedResults(resultType, expectedResponse);
Request request = mock(Request.class);
when(request.getResultType()).thenReturn(resultType);
assertThat(service.getResponse(request), sameInstance(expectedResponse));
assertTrue(recordingFactory.requestList.contains(request));
}
private void addToExpectedResults(ResultType resultType,
Response response) {
recordingFactory.responseMap.put(resultType, response);
}
private CacheConfiguration getTestCacheConfiguration() {
CacheConfiguration cacheConfiguration = new CacheConfiguration("TEST_CACHE", 10);
cacheConfiguration.setLoggingEnabled(false);
return cacheConfiguration;
}
private class EntryCreationRecordingCache extends ResponseFactory{
public final Map<ResultType, Response> responseMap = new ConcurrentHashMap<ResultType, Response>();
public final List<Request> requestList = new ArrayList<Request>();
#Override
protected Map<ResultType, Response> generateResponse(Request request) {
requestList.add(request);
return responseMap;
}
}
Here is the ServiceClass
public class Service<K extends Request, V extends Response> {
private Ehcache cache;
public Service(Ehcache cache, ResponseFactory factory) {
this.cache = new SelfPopulatingCache(cache, factory);
}
#SuppressWarnings("unchecked")
public V getResponse(K request)
{
ResultType resultType = request.getResultType();
Element cacheEntry = cache.get(request);
V response = null;
if(cacheEntry != null){
Map<ResultType, Response> resultTypeMap = (Map<ResultType, Response>) cacheEntry.getValue();
try{
response = (V) resultTypeMap.get(resultType);
}catch(NullPointerException e){
throw new RuntimeException("Result type not found for Result Type: " + resultType);
}catch(ClassCastException e){
throw new RuntimeException("Incorrect Response Type for Result Type: " + resultType);
}
}
return response;
}
}
And here is the ResponseFactory:
public abstract class ResponseFactory implements CacheEntryFactory{
#Override
public final Object createEntry(Object request) throws Exception {
return generateResponse((Request)request);
}
protected abstract Map<ResultType,Response> generateResponse(Request request);
}
After wrestling with it for a while, I discovered that the cache wasn't being initialized. Creating a CacheManager and adding the cache to it resolved the problem.
I also had a problem with EHCache hanging, although only in a hello-world example. Adding this to the end fixed it (the application ends normally).
CacheManager.getInstance().removeAllCaches();
https://stackoverflow.com/a/20731502/2736496