I am trying to write a test to verify that X providers are loaded in a certain scenario.
I can resolve the IConfiguration object, how can I tell how many IConfigurationProviders are in it?
In my startup, I use totally bog standard Configuration in my Program
IConfiguration configuration = null;
var builder = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
configuration = hostContext.Configuration;
var startup = new Startup(hostContext.Configuration, hostContext.HostingEnvironment);
//startup.ConfigureServices(services);
})
.ConfigureAppConfiguration((hostContext, config) =>
{
config.AddAzureKeyVaultsFromConfig();
});
builder.Build();
return configuration;
When I'm debugging, I can see my list of Providers when I hover over configuration.
What I would want to do is see that there are 4 providers. I would also want to check and see what type the providers are.
If I use Enumerate() it just gives me a flattened list of all of the values and discards which provider it came from.
The object you have here is actually an IConfigurationRoot, and that happens to also implement IConfiguration. So if you treat it as such, you can access the Providers property. For example:
IConfigurationRoot configuration = null;
// snip the rest of your code
var providerCount = configuration.Providers.Count();
Related
I tried in vain to mock a top-level (not part of any section) configuration value (.NET Core's IConfiguration). For example, neither of these will work (using NSubstitute, but it would be the same with Moq or any mock package I believe):
var config = Substitute.For<IConfiguration>();
config.GetValue<string>(Arg.Any<string>()).Returns("TopLevelValue");
config.GetValue<string>("TopLevelKey").Should().Be("TopLevelValue"); // nope
// non generic overload
config.GetValue(typeof(string), Arg.Any<string>()).Returns("TopLevelValue");
config.GetValue(typeof(string), "TopLevelKey").Should().Be("TopLevelValue"); // nope
In my case, I also need to call GetSection from this same config instance.
You can use an actual Configuration instance with in-memory data.
//Arrange
var inMemorySettings = new Dictionary<string, string> {
{"TopLevelKey", "TopLevelValue"},
{"SectionName:SomeKey", "SectionValue"},
//...populate as needed for the test
};
IConfiguration configuration = new ConfigurationBuilder()
.AddInMemoryCollection(inMemorySettings)
.Build();
//...
Now it is a matter of using the configuration as desired to exercise the test
//...
string value = configuration.GetValue<string>("TopLevelKey");
string sectionValue = configuration.GetSection("SectionName").GetValue<string>("SomeKey");
//...
Reference: Memory Configuration Provider
I do not have idea about NSubstitute, but this is how we can do in Moq.
Aproach is same in either cases.
GetValue<T>() internally makes use of GetSection().
You can Mock GetSection and return your Own IConfigurationSection.
This includes two steps.
1). Create a mock for IConfigurationSection (mockSection) & Setup .Value Property to return your desired config value.
2). Mock .GetSection on Mock< IConfiguration >, and return the above mockSection.Object
Mock<IConfigurationSection> mockSection = new Mock<IConfigurationSection>();
mockSection.Setup(x=>x.Value).Returns("ConfigValue");
Mock<IConfiguration> mockConfig = new Mock<IConfiguration>();
mockConfig.Setup(x=>x.GetSection(It.Is<string>(k=>k=="ConfigKey"))).Returns(mockSection.Object);
Mock IConfiguration
Mock<IConfiguration> config = new Mock<IConfiguration>();
SetupGet
config.SetupGet(x => x[It.Is<string>(s => s == "DeviceTelemetryContainer")]).Returns("testConatiner");
config.SetupGet(x => x[It.Is<string>(s => s == "ValidPowerStatus")]).Returns("On");
IConfiguration.GetSection<T> must be mocked indirectly. I don't fully understand why because NSubstitute, if I understand correctly, creates its own implementation of an interface you're mocking on the fly (in memory assembly). But this seems to be the only way it can be done. Including a top-level section along with a regular section.
var config = Substitute.For<IConfiguration>();
var configSection = Substitute.For<IConfigurationSection>();
var configSubSection = Substitute.For<IConfigurationSection>();
configSubSection.Key.Returns("SubsectionKey");
configSubSection.Value.Returns("SubsectionValue");
configSection.GetSection(Arg.Is("SubsectionKey")).Returns(configSubSection);
config.GetSection(Arg.Is("TopLevelSectionName")).Returns(configSection);
var topLevelSection = Substitute.For<IConfigurationSection>();
topLevelSection.Value.Returns("TopLevelValue");
topLevelSection.Key.Returns("TopLevelKey");
config.GetSection(Arg.Is<string>(key => key != "TopLevelSectionName")).Returns(topLevelSection);
// GetValue mocked indirectly.
config.GetValue<string>("TopLevelKey").Should().Be("TopLevelValue");
config.GetSection("TopLevelSectionName").GetSection("SubsectionKey").Value.Should().Be("SubsectionValue");
I could imagine in some rare scenarios it is needed but, in my humble opinion, most of the time, mocking IConfiguration highlight a code design flaw.
You should rely as much as possible to the option pattern to provide a strongly typed access to a part of your configuration. Also it will ease testing and make your code fail during startup if your application is misconfigured (instead than at runtime when the code reading IConfiguration is executed).
If you really(really) need to mock it then I would advice to not mock it but fake it with an in-memory configuration as explained in #Nkosi's answer
While Nkosi's answer works great for simple structures, sometimes you want to be able to have more complex objects (like arrays) without repeating the whole section path and to be able to use the expected types themselves. If you don't really care too much about performance (and should you in your unit tests?) then this extension method might be helpful.
public static void AddObject(this IConfigurationBuilder cb, object model) {
cb.AddJsonStream(new MemoryStream(Encoding.UTF8.GetString(JsonConvert.SerializeObject(model))));
}
And then use it like this
IConfiguration configuration = new ConfigurationBuilder()
.AddObject(new {
SectionName = myObject
})
.Build();
Use SetupGet method to mock the Configuration value and return any string.
var configuration = new Mock<IConfiguration>();
configuration.SetupGet(x => x[It.IsAny<string>()]).Returns("the string you want to return");
We need to mock IConfiguration.GetSection wichs is executed within GetValue extension method.
You inject the IConfiguration:
private readonly Mock<IConfiguration> _configuration;
and the in the //Arrange Section:
_configuration.Setup(c => c.GetSection(It.IsAny())).Returns(new Mock().Object);
It worked like a charm for me.
I've found this solution to work reliably for me for my XUnit C# tests & corresponding C# code:
In Controller
string authConnection = this._config["Keys:AuthApi"] + "/somePathHere/Tokens/jwt";
In XUnit Test
Mock<IConfiguration> mockConfig = new Mock<IConfiguration>();
mockConfig.SetupGet(x => x[It.Is<string>(s => s == "Keys:AuthApi")]).Returns("some path here");
Please checkout the below codes
using Microsoft.Extensions.DependencyInjection;
IServiceProviderFactory<IServiceCollection> serviceProviderFactory =
new DefaultServiceProviderFactory(new ServiceProviderOptions {
ValidateOnBuild = true,
ValidateScopes = true
});
IServiceCollection oldServiceCollection = new ServiceCollection();
IServiceCollection newServiceCollection =
serviceProviderFactory.CreateBuilder(oldServiceCollection);
Assert.IsTrue(oldServiceCollection == newServiceCollection);
I wanted to create a newServiceCollection base on the oldServiceCollection (then modify the newServiceCollection). However (big surprise) the ServiceProviderFactory, despite being a "Factory" with "Create.." methods, it did not create anything...
The newServiceCollection IS the oldServiceCollection. (If I modified the newServiceCollection then the oldServiceCollection will be modified as well).
I think that "DefaultServiceProviderFactory" (of Microsoft) is at fault here, do anybody know a better implementation which can help me clone the oldServiceCollection to make a newServiceCollection?
This is by design. The DefaultServiceProviderFactory isn't useful and only exists for other DI Containers to intercept the creation of IServiceCollection instances.
To make a copy, you'll have to iterate the old collection and add all ServiceDescriptors to the new one:
var newServiceCollection = new ServiceCollection();
foreach (var descriptor in oldServiceCollection)
{
newServiceCollection.Add(descriptor);
}
Hello i am having a hard time configuring my cosmos db to botframework.Before when using memory storage it is working fine. I am reading this and this as a guide. I included the errors with comments within the codes. Can anyone help me with this. I would greatly appreciate the help. I have been researching this for 3 days already. Thank you!
public class Startup
{
private const string CosmosServiceEndpoint = "xxxxxxxxxxx";
private const string CosmosDBKey = "xxxxxxxxxxx";
private const string CosmosDBDatabaseName = "xxxxxxxxxxx";
private const string CosmosDBCollectionNameConState = "conversationState";
private const string CosmosDBCollectionNameUserState = "userState";
private ILoggerFactory _loggerFactory;
private bool _isProduction = false;
public Startup(IHostingEnvironment env)
{
_isProduction = env.IsProduction();
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddBot<BasicBot>(options =>
{
ILogger logger = _loggerFactory.CreateLogger<BasicBot>();
var secretKey = Configuration.GetSection("botFileSecret")?.Value;
var botFilePath = Configuration.GetSection("botFilePath")?.Value;
if (!File.Exists(botFilePath))
{
throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}");
}
BotConfiguration botConfig = null;
try
{
botConfig = BotConfiguration.Load(botFilePath ?? #".\echo-with-counter.bot", secretKey);
}
catch
{
var msg = #"Error reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.
- You can find the botFilePath and botFileSecret in the Azure App Service application settings.
- If you are running this bot locally, consider adding a appsettings.json file with botFilePath and botFileSecret.
- See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration.
";
logger.LogError(msg);
throw new InvalidOperationException(msg);
}
services.AddSingleton(sp => botConfig);
var environment = _isProduction ? "production" : "development";
var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment);
if (service == null && _isProduction)
{
service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == "development").FirstOrDefault();
logger.LogWarning("Attempting to load development endpoint in production environment.");
}
if (!(service is EndpointService endpointService))
{
throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
}
options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
options.OnTurnError = async (context, exception) =>
{
logger.LogError($"Exception caught : {exception}");
await context.SendActivityAsync("Sorry, it looks like something went wrong.");
};
// The Memory Storage used here is for local bot debugging only. When the bot
// is restarted, everything stored in memory will be gone.
// IStorage dataStore = new MemoryStorage();
// error : COSMOSDBSTORAGE DOES NOT CONTAIN CONSTRUCTOR TAKES 4 ARGUMENTS
//IStorage dataStoreConversationState =
// new CosmosDbStorage(
// uri,
// "** auth key **",
// "helloworldbot",
// "conversationstate");
var uri = new Uri(CosmosServiceEndpoint);
IStorage dataStoreConversationState =
new CosmosDbStorage(new CosmosDbStorageOptions
{
AuthKey = CosmosDBKey,
CollectionId = CosmosDBCollectionNameConState,
CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
DatabaseId = CosmosDBDatabaseName,
});
IStorage dataStoreUserState =
new CosmosDbStorage(new CosmosDbStorageOptions
{
AuthKey = CosmosDBKey,
CollectionId = CosmosDBCollectionNameUserState,
CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
DatabaseId = CosmosDBDatabaseName,
});
//error : THE NON GENERIC TYPE "CONVERSATIONsTATE" CANNOT BE USED WITH TYPED ARGUMENTS
options.Middleware.Add(new ConversationState<BasicState>(dataStoreConversationState));
options.Middleware.Add(new UserState<BasicUserState>(dataStoreUserState));
}
There's a good chance that the reason this isn't working for you is because both of those links mention you need to create a New Collection in your CosmosDB resource in Azure. Microsoft recently updated the CosmosDB resource to require that new collections are made with Partition Keys, which aren't yet supported in Bot Framework. There's currently a Design Change Request to add this ability, but it's being stalled by the C# Cosmos SDK.
In the meantime, start by making the Cosmos resource in Azure and DO NOT make a database or collection. ONLY make the Cosmos resource. The bot framework SDK is set up to make a new DB and collection if the one you specify doesn't exist, and it can make one without partitions...so let the bot do the work here.
I used the second link you posted to change the Simple Prompt bot sample to work with Cosmos. Note: The endpoint and key are the default ones for the CosmosDB Emulator, which you can use to test locally, if you prefer.
Here is my startup.cs:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Integration;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.BotBuilderSamples
{
/// <summary>
/// The Startup class configures services and the app's request pipeline.
/// </summary>
public class Startup
{
private const string CosmosServiceEndpoint = "https://localhost:8081";
private const string CosmosDBKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
private const string CosmosDBDatabaseName = "bot-cosmos-sql-db";
private const string CosmosDBCollectionName = "bot-storage";
private static readonly CosmosDbStorage _myStorage = new CosmosDbStorage(new CosmosDbStorageOptions
{
AuthKey = CosmosDBKey,
CollectionId = CosmosDBCollectionName,
CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
DatabaseId = CosmosDBDatabaseName,
});
private ILoggerFactory _loggerFactory;
private bool _isProduction = false;
public Startup(IHostingEnvironment env)
{
_isProduction = env.IsProduction();
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
/// <summary>
/// Gets the configuration that represents a set of key/value application configuration properties.
/// </summary>
/// <value>
/// The <see cref="IConfiguration"/> that represents a set of key/value application configuration properties.
/// </value>
public IConfiguration Configuration { get; }
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> specifies the contract for a collection of service descriptors.</param>
/// <seealso cref="IStatePropertyAccessor{T}"/>
/// <seealso cref="https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection"/>
/// <seealso cref="https://learn.microsoft.com/en-us/azure/bot-service/bot-service-manage-channels?view=azure-bot-service-4.0"/>
public void ConfigureServices(IServiceCollection services)
{
services.AddBot<SimplePromptBot>(options =>
{
var secretKey = Configuration.GetSection("botFileSecret")?.Value;
var botFilePath = Configuration.GetSection("botFilePath")?.Value;
if (!File.Exists(botFilePath))
{
throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}");
}
// Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection.
var botConfig = BotConfiguration.Load(botFilePath ?? #".\simple-prompt.bot", secretKey);
services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. botFilePath: {botFilePath}"));
// Retrieve current endpoint.
var environment = _isProduction ? "production" : "development";
var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment);
if (!(service is EndpointService endpointService))
{
throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
}
options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
// Creates a logger for the application to use.
ILogger logger = _loggerFactory.CreateLogger<SimplePromptBot>();
// Catches any errors that occur during a conversation turn and logs them.
options.OnTurnError = async (context, exception) =>
{
logger.LogError($"Exception caught : {exception}");
await context.SendActivityAsync("Sorry, it looks like something went wrong.");
};
// Memory Storage is for local bot debugging only. When the bot
// is restarted, everything stored in memory will be gone.
//IStorage dataStore = new MemoryStorage();
// For production bots use the Azure Blob or
// Azure CosmosDB storage providers. For the Azure
// based storage providers, add the Microsoft.Bot.Builder.Azure
// Nuget package to your solution. That package is found at:
// https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/
// Uncomment the following lines to use Azure Blob Storage
// //Storage configuration name or ID from the .bot file.
// const string StorageConfigurationId = "<STORAGE-NAME-OR-ID-FROM-BOT-FILE>";
// var blobConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId);
// if (!(blobConfig is BlobStorageService blobStorageConfig))
// {
// throw new InvalidOperationException($"The .bot file does not contain an blob storage with name '{StorageConfigurationId}'.");
// }
// // Default container name.
// const string DefaultBotContainer = "<DEFAULT-CONTAINER>";
// var storageContainer = string.IsNullOrWhiteSpace(blobStorageConfig.Container) ? DefaultBotContainer : blobStorageConfig.Container;
// IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.ConnectionString, storageContainer);
// Create Conversation State object.
// The Conversation State object is where we persist anything at the conversation-scope.
var conversationState = new ConversationState(_myStorage);
options.State.Add(conversationState);
});
services.AddSingleton(sp =>
{
// We need to grab the conversationState we added on the options in the previous step.
var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
if (options == null)
{
throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the State Accessors");
}
var conversationState = options.State.OfType<ConversationState>().FirstOrDefault();
if (conversationState == null)
{
throw new InvalidOperationException("ConversationState must be defined and added before adding conversation-scoped state accessors.");
}
// The dialogs will need a state store accessor. Creating it here once (on-demand) allows the dependency injection
// to hand it to our IBot class that is create per-request.
var accessors = new SimplePromptBotAccessors(conversationState)
{
ConversationDialogState = conversationState.CreateProperty<DialogState>("DialogState"),
};
return accessors;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
app.UseDefaultFiles()
.UseStaticFiles()
.UseBotFramework();
}
}
}
Here's a diff, so you can easily see the code differences.
Here's a screenshot of it working:
It looks like your code also stores the userState and conversationState in separate collections. I think that works...but the "conventional" method is to only create one instance of CosmosDbStorage. The bot will store userState and conversationState in separate documents within the collection. Note that in addition to the above code, you'll likely need something like, var userState = new UserState(_myStorage), since your code also uses userState and the above code does not.
Additionally, and in line with Drew's answer, I think the code from that tutorial you linked might be causing some issues, simply because it's out of date. The best thing to do, would be finding a relevant sample from the GitHub Repo and using that as a guide. Basic Bot is a good one with conversationState and userState functionality.
Your code makes it seem like maybe you were writing some code using the pre-release bits at one point or maybe copied from someone who was. ConversationState itself is no longer a piece of middleware, nor is it a generic class any longer.
So you no longer create a ConversationState<T> for each piece of state you want to maintain. Instead you create a single ConversationState, which acts as a sort of scoping "bucket" if you will, and then you create many properties within that "bucket" using the CreateProperty<T> API.
Like this:
var conversationState = new ConversationState(myStorage);
var myBasicStateProperty conversationState.CreateProperty<BasicState>("MyBasicState");
Now, as I also said, it is no longer middleware. Instead, what is returned from CreateProperty<T> is an IStatePropertyAccessor<T> which you can then pass into whatever needs to use it (e.g. your bot). Likewise you would also pass the ConversationState itself into the bot so it can ultimately call SaveChangesAsync on it at the end of the turn. Alternatively you can configure the AutoSaveStateMiddleware which will take care of saving the state for you at the end of every turn, but you lose control over the ability to deal with exceptions that arise during the call to SaveChangesAsync (e.g. network partition, data concurrency, etc).
I'm using SolrNet with Autofac DI in my web application like this:
var solrNetModule = new SolrNetModule(ConfigurationManager.ConnectionStrings["solr"].ConnectionString);
solrNetModule.HttpWebRequestFactory = new BasicAuthHttpWebRequestFactory("****", "****");
builder.RegisterModule(solrNetModule);
My queries are quite long and sometimes end up with a 404 in Jetty, I'm pretty sure this happens because the length of the URL.
I've been reading about the SolrPostConnection and that sounds like a proper solution to my problem, but I'm having trouble implementing it in my Autofac setup.
I know the Autofac SolrNetModule integration internally uses
builder.RegisterInstance(solrConnectionInstance).As<ISolrConnection>();
But I can't figure out how to override it or .Register Autofac to use the SolrPostConnection instead.
Looks like you need to override registration for interface ISolrConnection. The main problem is that SolrNetModule doesn't provide mapping of SolrConnection to itself. Try out the example below. I added another registration of SolrConnection to make it available for injecting to contructor of PostSolrConnection without dependency loops
[Test]
public void SolrRegistrationOverride()
{
// Arrange
var builder = new ContainerBuilder();
var serverUrl = ConfigurationManager.ConnectionStrings["solr"].ConnectionString;
var httpWebRequestFactory = new BasicAuthHttpWebRequestFactory("****", "****");
var solrNetModule = new SolrNetModule(serverUrl);
solrNetModule.HttpWebRequestFactory = httpWebRequestFactory;
builder.RegisterModule(solrNetModule);
builder.RegisterType<SolrConnection>().AsSelf()
.WithParameter(new NamedParameter("serverURL", serverUrl))
.WithProperty("HttpWebRequestFactory", httpWebRequestFactory).AsSelf()
.SingleInstance();
builder.RegisterType<PostSolrConnection>().As<ISolrConnection>()
.WithParameters(new Parameter[]
{
new ResolvedParameter((prm, сtx) => prm.Name == "conn", (prm, ctx) => ctx.Resolve<SolrConnection>()),
new NamedParameter("serverUrl", serverUrl)
});
var container = builder.Build();
// Act
var conn = container.Resolve<ISolrConnection>();
// Assert
Assert.IsInstanceOf<PostSolrConnection>(conn);
}
Hope it helps.
I'm new to NHibernate and I'm trying to configure it based on the book 'Learning NHibernate 4'. However, I'm stuck with how to configure it. I have a class called Connection but when I try to use it, NHibernate tells me it can't find 'HbmMapping'.
class Connection
{
public Connection()
{
var cfg = new Configuration();
cfg.DataBaseIntegration(x =>
{
x.Dialect<PostgreSQLDialect>();
x.Driver<NpgsqlDriver>();
x.ConnectionString = "Server=127.0.0.1; Port=5433; User Id=smartwarehouse; Password=$smart#2018;Database=warehouse;";
x.ConnectionReleaseMode = ConnectionReleaseMode.OnClose;
x.LogSqlInConsole = true;
x.LogFormattedSql = true;
}).AddMapping(GetMappings());
}
// here is Hbm dosn't find from library
private HbmMapping GetMappings()
{
}
}
it gives me other two options to use like here
This is probably a better resource for this issue. You typically tell it where your mappings are at an assembly level...
.AddFromAssemblyOf<YourEntity>();
...so that when you add/remove mappings, you don't need to change your code.
For example, my SessionProvider has a bit like this:
Config = new NHibernateConfig();
Config.Configure(); // read config default style
Fluently
.Configure(Config)
.Mappings(
m => m.FluentMappings.AddFromAssemblyOf<UserMap>()
...
I don't have .hbm files as I use derivatives of ClassMap. However, as long as the type you specify in the AddFromAssemblyOf method is in the same assembly as your .hbm files, then it should work. So something like:
Fluently
.Configure(Config)
.Mappings(
m => m.HbmMappings.AddFromAssemblyOf<ATypeInYourMappingAssembly>()