Register multiple dbcontext using autofac - entity-framework

We have application in asp.net MVC with DDD architecture with autofac IOC container. We are trying to register two dbcontext with different database connect. But the only last one is came in to effect. We are using Entity Framework 4.4.0. Here is the code.
var masterDataSettingManager = new SaasDataSettingManager();
if (masterDataSettingManager.LoadSettings() != null)
{
var masterProviderSettings = masterDataSettingManager.LoadSettings();
builder.Register(c => masterDataSettingManager.LoadSettings()).As<DataSettings>();
builder.Register(x => new EfDataProviderManager(x.Resolve<DataSettings>())).As<BaseDataProviderManager>().InstancePerDependency();
builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IDataProvider>().InstancePerDependency();
builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IEfDataProvider>().InstancePerDependency();
if (masterDataSettingManager != null && masterProviderSettings.IsValid())
{
var efDataProviderManager = new EfDataProviderManager(masterDataSettingManager.LoadSettings());
var dataProvider = (IEfDataProvider)efDataProviderManager.LoadDataProvider();
dataProvider.InitConnectionFactory();
var dbProviderFactory = efDataProviderManager.LoadDbProviderFactories();
builder.Register<IDbContext>(c => new MyDbContext1(masterProviderSettings.DataConnectionString, dbProviderFactory)).InstancePerHttpRequest();
}
else
{
builder.Register<IDbContext>(c => new MyDbContext1(masterDataSettingManager.LoadSettings().DataConnectionString)).InstancePerHttpRequest();
}
}
//data layer
var dataSettingsManager = new DataSettingsManager();
var dataProviderSettings = dataSettingsManager.LoadSettings();
builder.Register(c => dataSettingsManager.LoadSettings()).As<DataSettings>();
builder.Register(x => new EfDataProviderManager(x.Resolve<DataSettings>())).As<BaseDataProviderManager>().InstancePerDependency();
builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IDataProvider>().InstancePerDependency();
builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IEfDataProvider>().InstancePerDependency();
if (dataProviderSettings != null && dataProviderSettings.IsValid())
{
var efDataProviderManager = new EfDataProviderManager(dataSettingsManager.LoadSettings());
var dataProvider = (IEfDataProvider)efDataProviderManager.LoadDataProvider();
dataProvider.InitConnectionFactory();
var dbProviderFactory = efDataProviderManager.LoadDbProviderFactories();
builder.Register<IDbContext>(c => new MyDbContext2(dataProviderSettings.DataConnectionString, dbProviderFactory)).InstancePerHttpRequest();
}
else
{
builder.Register<IDbContext>(c => new MyDbContext2(dataSettingsManager.LoadSettings().DataConnectionString)).InstancePerHttpRequest();
}
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerHttpRequest();

Register them like this:
builder
.Register(c =>
new MyDbContext1(dataProviderSettings.DataConnectionString, dbProviderFactory))
.Named<IDbContext>("dbContext1").InstancePerHttpRequest();
builder
.Register(c =>
new MyDbContext2(dataProviderSettings.DataConnectionString, dbProviderFactory))
.Named<IDbContext>("dbContext2").InstancePerHttpRequest();
And then resolve them:
IDbContext dbContext = ctx.ResolveNamed<IDbContext>("dbContext1");
If you would like to inject it in a constructor you may do it like this:
builder
.RegisterType<SomeService>()
.WithParameter(
(p, c) => p.Name == "dbContext",
(p, c) => c.ResolveNamed<IDbContext>("dbContext1")
);
Apart from having named registrations pf dbContexts you still can have unnamed defined in parallel with them - it may be treated as default one. Iy then in your application you would like rather to use a named registration then you specify this desire in the above way.

Related

Get all the roles after include with main entity in Entity framework Core

I have three entities which are Account, Role and AccountRole. Through repository pattern I have fetched all the accounts, roles and accountRoles like below:
var accounts = _accountRepository.Query()
.Include(x => x.AccountRoles)
.ThenInclude(x => x.Role);
Now I would like to get a single account information with all it's roles. So I have written the below query. But the problem is only first role comes out.
var userAccount = accounts.Where(x => x.Id == id)
.Select(x => new AccountDto
{
Id = x.Id,
Name = x.UserFullName,
FirstName = x.FirstName,
LastName = x.LastName,
Email = x.Email,
Mobile = x.Mobile,
UserName = x.UserName,
PhotoUrl = x.PhotoUrl,
IsActive = x.IsActive,
Roles = accounts.Where(x => x.Id == id).Select(x => new RoleDto
{
Title = x.AccountRoles
.Where(x => x.IsActive == true)
.Select(x => x.Role.Title).FirstOrDefault()
}).ToList()
}).FirstOrDefault();
Can anyone help me to write the proper query?
First, this part
Roles = accounts.Where(x => x.Id == id).Select(x => new RoleDto
{
Title = x.AccountRoles
.Where(x => x.IsActive == true)
.Select(x => x.Role.Title).FirstOrDefault()
}).ToList()
is quite strange mixture, you should simply use the navigation property (yes, it is not only for includes as some people think).
Second, don't apply FirstOrDefault() when you want to get all items rather than the first only
// here x variable type is Account (coming from the outer Select)
Roles = x.AccountRoles
.Where(ar => ar.IsActive)
.Select(ar => new RoleDto
{
Title = ar.Role.Title,
}).ToList()

Moq MongoDB UpdateOneAsync method for unit testing

I want to moq update method that is using mongodbContext. here is what i am trying to do but its not working. how to pass UpdateResult return type .ReturnsAsync<UpdateResult>(). I am very new to Mongodb database Unit Testing with .net core.
public void UpdateEventAsync_Test()
{
//Arrange
var eventRepository = EventRepository();
var pEvent = new PlanEvent
{
ID = "testEvent",
WorkOrderID = "WorkOrderID",
IsDeleted = false,
IsActive = true,
EquipmentID = "EquipmentID"
};
////Act
mockEventContext.Setup(s => s.PlanEvent.UpdateOneAsync(It.IsAny<FilterDefinition<PlanEvent>>(), It.IsAny<UpdateDefinition<Model.EventDataModel.PlanEvent>>(), It.IsAny<UpdateOptions>(), It.IsAny<System.Threading.CancellationToken>())).ReturnsAsync<UpdateResult>();
var result = eventRepository.UpdateEventAsync(pEvent);
////Assert
result.Should().NotBeNull();
Assert.AreEqual(true, result);
}
below is the code for which i want to write Moq Test
public async Task<bool> UpdateEventAsync(Model.EventDataModel.PlanEvent eventobj)
{
var filter = Builders<Model.EventDataModel.PlanEvent>.Filter.Where(f => f.ID == eventobj.ID);
// TO Do: Use replace instead of update.
var updatestatement = Builders<Model.EventDataModel.PlanEvent>.Update.Set(s => s.IsDeleted, eventobj.IsDeleted)
.Set(s => s.EquipmentID, eventobj.EquipmentID)
.Set(s => s.PlannedStartDateTime, eventobj.PlannedStartDateTime)
.Set(s => s.PlannedEndDatetime, eventobj.PlannedEndDatetime)
.Set(s => s.WorkOrderID, eventobj.WorkOrderID)
.Set(s => s.ResourceID, eventobj.ResourceID)
.Set(s => s.LastUpdatedBy, eventobj.LastUpdatedBy)
.Set(s => s.EventComment, eventobj.EventComment)
.Set(s => s.SiteID, eventobj.SiteID)
.Set(s => s.LastUpdatedDateTime, DateTime.UtcNow.ToString());
UpdateResult updateResult = await _eventContext.PlanEvent.UpdateOneAsync(filter, updatestatement);
return updateResult != null && updateResult.IsAcknowledged && updateResult.ModifiedCount > 0;
}
Either create an instance or mock UpdateResult and return that from the setup
public async Task UpdateEventAsync_Test() {
//...omitted for brevity
var mockUpdateResult = new Mock<UpdateResult>();
//Set up the mocks behavior
mockUpdateResult.Setup(_ => _.IsAcknowledged).Returns(true);
mockUpdateResult.Setup(_ => _.ModifiedCount).Returns(1);
mockEventContext
.Setup(_ => _.PlanEvent.UpdateOneAsync(It.IsAny<FilterDefinition<PlanEvent>>(), It.IsAny<UpdateDefinition<Model.EventDataModel.PlanEvent>>(), It.IsAny<UpdateOptions>(), It.IsAny<System.Threading.CancellationToken>()))
.ReturnsAsync(mockUpdateResult.Object);
//Act
var result = await eventRepository.UpdateEventAsync(pEvent);
//Assert
result.Should().Be(true);
}
Also note that the test needs to be made async to be exercised accurately.

Why is Automapper.ProjectTo() throwing a null reference exception?

I have a mapping:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Foo, FooDto>()
.ForMember(dest => dest.Id,
opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Name,
opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.PhoneNumber,
opt => opt.MapFrom(src => src.PhoneNumbers.Number)) //etc.
});
and I'm trying to use it in a unit test calling into some mocked up EF objects:
var ids = new List<string>()
{
"123";
"456";
"789";
};
var data = new List<Foo>();
foreach(var id in ids)
{
data.Add(new Foo() { Id = id });
}
this.mockContext = new Mock<entities>();
this.mockSet = new Mock<DbSet<Foo>>();
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.Provider).Returns(data.Provider);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.Expression).Returns(data.Expression);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.ElementType).Returns(data.ElementType);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.GetEnumerator()).Returns(data.GetEnumerator());
When I query the entities directly:
var id = "123";
var bar = this.mockContext.Object.Foo.Where(p => p.id == id);
I get back an IQueryable() with a single result, as expected. But when I try to project my object into a DTO:
var id = "123";
var buzz = this.mockContext.Object.Foo.Where(p => p.id == id).ProjectTo<FooDto>(this.config);
The IQueryable I get back throws a Null Reference Exception if I try to access the results in any way. So for example:
buzz.ToList();
buzz.SingleOrDefault(); // This mirrors the actual call in my code since this is a GetById query.
both fail. What am I missing here?
The problem lies is that Foo uses EF navigation properties to refer to other objects. Specifically a PhoneNumber in this instance. Since the test data is created without the linked PhoneNumber object, it breaks inside of the ProjectTo method. This isn't a problem when grabbing the top level Queryable directly, but Automapper needs the objects to exist (even if they're empty) in order to complete the mapping. Changing that line to:
data.Add(new Foo() { Id = id, PhoneNumber = new PhoneNumber() });
allows the ProjectTo method to complete, albeit with null values.

Entity Framework - How to pass Include off to method caller?

I have a method that calls a DbSet from an Entity Framework database:
public static List<CostEntryVM> ToViewModelList(this DbSet<CostEntry> CostEntrys, Expression<Func<CostEntry, bool>> query) {
return AutoMapper.Mapper.Map<List<CostEntry>, List<CostEntryVM>>(
CostEntrys
.Include(x => x.Job)
.Include(x => x.User)
.Where(query)
.ToList());
}
To use this I can then do for example:
CostEntrys.ToViewModelList(x => x.Active == true);
I want to also be able to call:
CostEntrys.ToViewModelList(x => x.Include(y => y.Job).Include(y.User), x => x.Active == true);
I can't for the life of me figure out how the method signature should look or how I would then apply that to the DbSet.
How can I do this?
First you need to change the extension method to:
public static List<CostEntryVM> ToViewModelList(
this DbSet<CostEntry> CostEntrys,
Expression<Func<CostEntry, bool>> query,
Func<IQueryable<CostEntry>, IQueryable<CostEntry>> func)
{
// Adding the predicate query
IQueryable<CostEntry> queryable = CostEntrys.Where(query);
// Adding include paths
IQueryable<CostEntry> queryableWithFetch = func(queryable);
// Executing the query and map it to the view model object
return AutoMapper.Mapper.Map<List<CostEntry>, List<CostEntryVM>>(
queryableWithFetch.ToList());
}
And then you can call it:
CostEntrys.ToViewModelList(
x => x.Active == true,
x => x.Include(y => y.Job).Include(y.User));

Entity Framework. Where clause with parameter from query as variable

Task: use different where clause in one query
Here is example (it is not real query, just to illustrate the problem)
var events = ctx.Events; // ctx - EntityFramework context
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// how to get this clause from variable
.Where(ee => ee.State == gg.Key)
.Take(2)
})
.ToList();
Next code did not work, the problem is that where expression use parameter from query gg.Key
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// 1
// how to get this clause from variable
//.Where(ee => ee.State == gg.Key)
// 2
// try to take out where expression from query
.Where(_buildExpression(gg.Key))
.Take(2)
})
.ToList();
// method
static Expression<Func<Event, bool>> _buildExpression(string state)
{
return ee => ee.State == state;
}
// exeption
An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.SqlServer.dll
Additional information: variable 'gg' of type 'System.Linq.IGrouping`2[System.String,Entities.Event]' referenced from scope '', but it is not defined
Example of getting where expression from variable, but does not depend on gg.Key (wrong)
Expression<Func<Event, bool>> whereClause = (ee) => (ee.State == "test");
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// 1
// how to get this clause from variable
//.Where(ee => ee.State == gg.Key)
// 2
// try to take out where expression from query
//.Where(_buildExpression(gg.Key))
// 3
// whereClause from variable, but does not depend on gg.Key
.Where(whereClause)
.Take(2)
})
.ToList();
How to take where сlause from variable with depend on gg.Key?
p.s. the query is just example of the problem. The code below does not solve the problem of real query:
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = gg.Take(2)
})
.ToList();
Solution by OP.
Thanks to Ivan Stoev comment.
Expression<Func<Event, string, bool>> whereClause = (ee, state) => (ee.State == state);
var events = ctx.Events;
var res = events
.AsExpandable() // <= add this
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
.Where(ee => whereClause.Invoke(ee, gg.Key)) // <= Invoke expression
.Take(2)
})
.ToList();
This was made possible by LinqKit