I have a custom ConfigurationSection that contains a custom ConfigurationElementCollection with custom ConfigurationElement instances. The <add> and <clear> tags work fine in the configuration file, but using <remove> generates the following exception during my unit test:
System.Configuration.ConfigurationErrorsException: Unrecognized attribute 'name'. Note that attribute names are case-sensitive.
The configuration file is pretty simple. Here's the inner-part of the test section in question:
<exceptionHandling>
<policies>
<clear />
<add name="default" shouldLog="true" shouldShow="true"/>
<add name="initialization" shouldLog="true" shouldShow="true"/>
<add name="security" shouldLog="true" shouldShow="true"/>
<add name="logOnly" shouldLog="true" shouldShow="false"/>
<add name="unhandled" shouldLog="true" shouldShow="true"/>
<add name="test" shouldLog="false" shouldShow="false"/>
<remove name="test"/>
</policies>
</exceptionHandling>
The following is the relevant code for the configuration classes (some has been elided for brevity).
public sealed class ExceptionHandlingSection : ConfigurationSection
{
[ConfigurationProperty("policies", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(PolicyElementCollection), AddItemName = "add", RemoveItemName = "remove", ClearItemsName = "clear")]
public PolicyElementCollection Policies => (PolicyElementCollection)base["policies"];
}
public sealed class PolicyElementCollection : ConfigurationElementCollection
{
// pretty boiler plate
}
public sealed class PolicyElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get => (string)this["name"];
set => this["name"] = value;
}
// other properties
}
What needs to be done to get <remove> to work as shown in the test configuration file?
The answer to this turns out to be really simple.
I read a few things talking about XML attributes, but fundamentally it's looking for a key property. This can be assigned by a property of the ConfigurationPropertyAttribute. To get the <remove> tag to work all I need to do is change my ConfigurationElement class as follows:
public sealed class PolicyElement : ConfigurationElement
{
[ConfigurationProperty("name", IsKey = true, IsRequired = true)]
public string Name
{
get => (string)this["name"];
set => this["name"] = value;
}
// other properties
}
Related
I am trying to change SQL Database type from MSSQL to MySQL inside .NET web app.
I followed this:
https://dev.mysql.com/doc/connector-net/en/connector-net-entityframework60.html
and updating Web.config files.
<configuration>
<configSections>
<section name="AdWordsApi" type="System.Configuration.DictionarySectionHandler" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
...
</configuration>
...
<entityFramework codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.EntityFramework">
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
<provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.EntityFramework, Version=8.0.27.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</providers>
</entityFramework>
<connectionStrings>
<add name="DefaultConnection" providerName="MySql.Data.MySqlClient" connectionString="Server=XYZ;Database=XYZ;Uid=XYZ;Password=XYZ;" />
</connectionStrings>
When trying to Enable-Migrations I am getting:
Checking if the context targets an existing database...
System.InvalidOperationException: The DbConfiguration type 'MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.EntityFramework' specified in the application config file could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See http://go.microsoft.com/fwlink/?LinkId=260883 for more information. ---> System.TypeLoadException: Nie można załadować typu 'MySql.Data.Entity.MySqlEFConfiguration' z zestawu 'MySql.Data.EntityFramework'.
w System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMarkHandle stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName, ObjectHandleOnStack type)
w System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName)
w System.Type.GetType(String typeName, Boolean throwOnError)
w System.Data.Entity.Infrastructure.DependencyResolution.DbConfigurationLoader.TryLoadFromConfig(AppConfig config)
--- Koniec śladu stosu wyjątków wewnętrznych ---
w System.Data.Entity.Infrastructure.DependencyResolution.DbConfigurationLoader.TryLoadFromConfig(AppConfig config)
w System.Data.Entity.Infrastructure.DependencyResolution.DbConfigurationManager.EnsureLoadedForAssembly(Assembly assemblyHint, Type contextTypeHint)
w System.Data.Entity.Infrastructure.Design.Executor.ScaffoldInitialCreateInternal(DbConnectionInfo connectionInfo, String contextTypeName, String contextAssemblyName, String migrationsNamespace, Boolean auto, String migrationsDir)
w System.Data.Entity.Infrastructure.Design.Executor.ScaffoldInitialCreate.<>c__DisplayClass0_0.<.ctor>b__0()
w System.Data.Entity.Infrastructure.Design.Executor.OperationBase.<>c__DisplayClass4_0`1.b__0()
w System.Data.Entity.Infrastructure.Design.Executor.OperationBase.Execute(Action action)
The DbConfiguration type 'MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.EntityFramework' specified in the application config file could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See http://go.microsoft.com/fwlink/?LinkId=260883 for more information.```
I din't update any class, only configuration files so far. My context class looks like this:
namespace Repository.Models
{
public class AppContext : IdentityDbContext, IAppContext
{
public AppContext()
: base("DefaultConnection")
{
}
public static AppContext Create()
{
return new AppContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
...
}
}
I tried modifying above class into with no luck:
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class AppContext : DbContext, IAppContext
{
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
...
public AppContext()
: base()
{
}
// Constructor to use on a DbConnection that is already opened
public AppContext(DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
//modelBuilder.Entity<Car>().MapToStoredProcedures();
}
}
1- You Need to Install The NuGet Package MySql.Data.Entity
2- Update the Configuration Type of DbContextFile:
[DbConfigurationType(typeof(MySqlEfConfiguration))]
public class AppDbContext : DbContext{.....}
3- Change the Connection String provider name to: "MySql.Data.MySqlClient"
providerName="MySql.Data.MySqlClient"
4- Test With Migrations
As I See, the problem is that you need to modify the DbContext Class
I am trying to create my own Html helper that will allow me to reuse some functionality across any web application. If I wanted to reuse this control in a single wep app I could create a .cshtml file and call it via the Html.Partial("") method and pass in a Model.
However as I have a class library project for my custom Html helpers I am creating the html with a string builder like this simplified version
StringBuilder htmlBuilder = new StringBuilder("<div class='myClass'>")
foreach(var item in MyItems)
{
htmlBuilder.Append($"item : {item.Name}");
}
htmlBuilder.append("</div>");
This makes it a pain to maintain especially as my control gets more features.
Is there a recommended way to leverage the razor engine where I can write the html in a .cshtml file with a model and then generate the html instead of using a string builder?
Yes. You can use a templated HTML helper to separate your view (HTML elements) from your model.
However, the downside is that you generally must put the templates either in the /Views/Shared/DisplayTemplates folder or a /DisplayTemplates folder inside of the view folder that represents the current controller. In the latter case, you can only use the template inside of that specific folder. It is possible to make a custom view engine that will pull the default templates as resources of a DLL file - see the MvcSiteMapProvider project for an example view engine implementation.
Example Templated HTML Helper
public class MyHelperModel
{
public string Title { get; set; }
public string Body { get; set; }
}
// Extension Methods for HTML helper
public static class MyHelperExtensions
{
public static MvcHtmlString MyHelper(this HtmlHelper helper, string title, string body)
{
return MyHelper(helper, title, body, null);
}
public static MvcHtmlString MyHelper(this HtmlHelper helper, string title, string body, string templateName)
{
// Build the model
var model = BuildModel(title, body);
// Create the HTML helper for the model
return CreateHtmlHelperForModel(helper, model)
.DisplayFor(m => model, templateName);
}
private static MyHelperModel BuildModel(string title, string body)
{
// Map to model
return new MyHelperModel
{
Title = title,
Body = body
};
}
private static HtmlHelper<TModel> CreateHtmlHelperForModel<TModel>(this HtmlHelper helper, TModel model)
{
return new HtmlHelper<TModel>(helper.ViewContext, new ViewDataContainer<TModel>(model));
}
}
public class ViewDataContainer<TModel>
: IViewDataContainer
{
public ViewDataContainer(TModel model)
{
ViewData = new ViewDataDictionary<TModel>(model);
}
public ViewDataDictionary ViewData { get; set; }
}
MyHelperModel.cshtml
The default conventions use a display template with the same name as the model when no templateName argument is passed (or it is null). Therefore, this will be our default HTML helper format. Note that you could instead just hard-code the HTML elements into the helper in the default case instead of using a template (or going the extra mile of creating a view engine).
As mentioned above, this should be in the /Views/Shared/DisplayTemplates/ folder, but you could make a custom view engine to pull the default template from a DLL.
#model MyHelperModel
<h3>#Model.Title</h3>
<p>#Model.Body</p>
CustomHtmlHelperTemplate.cshtml
Here is a named template that can be used within the application to change the HTML elements applied to the HTML helper.
As mentioned above, this should be in the /Views/Shared/DisplayTemplates/ folder, but you could make a custom view engine to pull the default template from a DLL.
#model MyHelperModel
<h1>#Model.Title</h1>
<p><i>#Model.Body</i></p>
Usage
#Html.MyHelper(
"This is the default template",
"This is what happens when we don't pass a template name to the HTML helper.")
#Html.MyHelper(
"This is a custom template",
"This is a custom template with different HTML elements than the default template.",
"CustomHtmlHelperTemplate")
NOTE: To ensure the helpers are available in the views, you need to add the namespaces in the /Views/Web.config file at <system.web.webPages.razor><pages><namespaces>.
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<!-- Add your namespaces here -->
<add namespace="MyProject.HtmlHelperNamespace" />
<add namespace="MyProject.HtmlHelperNamespace.Models" />
</namespaces>
</pages>
</system.web.webPages.razor>
You can maintain and generate at runtime the output of a Razor View / PartialView (cshtml), using this code:
public static string GetViewPageHtml(Controller controller, object model, string viewName)
{
ViewEngineResult result = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
if (result.View == null)
throw new Exception(string.Format("View Page {0} was not found", viewName));
controller.ViewData.Model = model;
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (System.Web.UI.HtmlTextWriter output = new System.Web.UI.HtmlTextWriter(sw))
{
ViewContext viewContext = new ViewContext(controller.ControllerContext, result.View, controller.ViewData, controller.TempData, output);
result.View.Render(viewContext, output);
}
}
return sb.ToString();
}
You call it like this (from a Controller)
string result = GetViewPageHtml(this, viewModel, "~/Views/Home/Index.cshtml");
Using Spring Data REST with JPA in version 2.1.0.
How can I configure the pagination in order to have the page argument starting at index 1 instead of 0 ?
I have tried setting a custom HateoasPageableHandlerMethodArgumentResolver with an mvc:argument-resolvers, but that doesn't work:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver">
<property name="oneIndexedParameters" value="true"/>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
Note that this behaviour is perfectly coherent with the documentation for mvc:argument-resolver that says:
Using this option does not override the built-in support for
resolving handler method arguments. To customize the built-in support
for argument resolution configure RequestMappingHandlerAdapter
directly.
But how can I achieve this ? If possible, in a clean and elegant way ?
The easiest way to do so is to subclass RepositoryRestMvcConfiguration and include your class into your configuration:
class CustomRestMvcConfiguration extends RepositoryRestMvcConfiguration {
#Override
#Bean
public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
HateoasPageableHandlerMethodArgumentResolver resolver = super.pageableResolver();
resolver.setOneIndexedParameters(true);
return resolver;
}
}
In your XML configuration, replace:
<bean class="….RepositoryRestMvcConfiguration" />
with
<bean class="….CustomRestMvcConfiguration" />
or import the custom class instead of the standard one in your JavaConfig file.
I have configured the RequestMappingHandlerAdapter using a BeanPostProcessor, however I believe that's neither clean, nor elegant. That looks more like a hack. There must be a better way ! I'm giving the code below just for reference.
public class RequestMappingHandlerAdapterCustomizer implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter)bean;
List<HandlerMethodArgumentResolver> customArgumentResolvers = adapter.getCustomArgumentResolvers();
if(customArgumentResolvers != null) {
for(HandlerMethodArgumentResolver customArgumentResolver : customArgumentResolvers) {
if(customArgumentResolver instanceof HateoasPageableHandlerMethodArgumentResolver) {
HateoasPageableHandlerMethodArgumentResolver hateoasPageableHandlerMethodArgumentResolver = (HateoasPageableHandlerMethodArgumentResolver)customArgumentResolver;
hateoasPageableHandlerMethodArgumentResolver.setOneIndexedParameters(true);
}
}
}
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
<beans ...>
<bean class="util.spring.beanpostprocessors.RequestMappingHandlerAdapterCustomizer" />
</beans>
I use to do it using a customizer, which is something that they keep adding for more and more components with every new version:
#Bean
public PageableHandlerMethodArgumentResolverCustomizer pageableResolverCustomizer() {
return resolver -> resolver.setOneIndexedParameters(true);
}
You can put this in any #Configuration class, but ideally you should put it (with any other customization) in one that implements RepositoryRestConfigurer.
At the moment I'm writing a windows service.
I'm already using EntityFramework with MSSQL database which is working perfectly. Now I have to use MySql parallely. But I can't manage to get it running ... I would like to avoid using the app.config and configure EntityFramework via the constructor of my class derived from DbContext.
I have a SqlContext class:
public class SqlContext : DbContext
{
public IDbSet<ServiceauftragSource> ServiceauftragSource { get; set; }
public SqlContext(string connectionString)
: base(connectionString)
{
}
public SqlContext(DbConnection connection)
: base(connection, true)
{
this.Configuration.LazyLoadingEnabled = true;
}
}
In the constructor of my UnitOfWork I try to create my SqlContext:
public SqlUnitOfWork()
{
const string connStr = "server=127.0.0.1;uid=myuser;pwd=mypw;database=mydb;";
MySqlConnection conn = new MySqlConnection(connectionString);
this.Context = new SqlContext(conn);
}
This didn't work. I get the following message when trying to access the database:
Unable to determine the DbProviderFactory type for connection of type 'MySql.Data.MySqlClient.MySqlConnection'. Make sure that the ADO.NET provider is installed or registered in the application config.
Neither did:
public SqlUnitOfWork()
{
this.SetConnectionString();
this.Context = new SqlContext(connectionString);
}
private void SetConnectionString()
{
this.connectionString = "Data Source=" + debugDatabaseServer + ";Initial Catalog=" + debugDatabaseName
+ ";User ID=" + debugDatabaseUsername + ";Password=" + debugDatabasePassword
+ ";Trusted_Connection=False;Persist Security Info=True;";
}
I'm not sure why but I think this is because I haven't told my context its provider (according to other threads on SO it has to be MySql.Data.MySqlClient). But where and how to?
References of the project:
Update
I tried to use my App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="MySqlContext" providerName="MySql.Data.MySqlClient" connectionString="server=localhost;
port=3306;database=***;uid=***;password=***"/>
</connectionStrings>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices,
MySql.Data.Entity.EF6" />
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
</configuration>
When accessing the database I get the following error:
Object reference not set to an instance of an object.
I think this is because my service can't access the app.config after it's being installed. I ensured that app.config is MyService.exe.config after build ...
I've been using MySQL with EF for quite some time pretty flawlessly, what yo need to do is add the following line above your DBContext
[DbConfigurationType(typeof (MySql.Data.Entity.MySqlEFConfiguration))]
so in your case the snippet would look like
[DbConfigurationType(typeof (MySql.Data.Entity.MySqlEFConfiguration))]
public class SqlContext : DbContext
{
public IDbSet<ServiceauftragSource> ServiceauftragSource { get; set; }
public SqlContext(string connectionString)
: base(connectionString)
{
}
public SqlContext(DbConnection connection)
: base(connection, true)
{
this.Configuration.LazyLoadingEnabled = true;
}
}
as for your connection string, i tend to store mine in the web config and then string format for whatever db,server or user i need to create a context for - hope that helps!
NB Im presuming you have your refs to MySQL.data and EF6 as well!
I have seen several posts about this but have not been able to create a usable solution from the responses. Perhaps due to a lack of understanding.
The hosting provided requires that an identical code base be used on staging and production, including connection string.
How do I switch the connection string for DbContext?
I understand I can do something like this:
public FooEntities() : base("ApplicationServices") { }
But this is not dynamic - it merely sets it at runtime.
So how would I actually CHOOSE the connection string at runtime?
Yes public FooEntities() : base("ApplicationServices") { }
FooEntities inheriting from ObjectContext
You could also write
public FooEntities() : base(YourStaticMethodToGetConnectionString()) { }
Then you could pull the connection string from the web.config based on some environment setting
I'm reviewing this right now because I will dev local and then deploy to cloud. Therefore, want to dynamically switch connection strings being used by data context. My plan is to configure the needed connection strings in standard "connectionStrings" section of Web.config, and then place logic in the DbContext constructor, like:
public partial class MyApplicationDbContext : DbContext
{
public MyApplicationDbContext()
: base("name=cloud")
{
Database.Connection.ConnectionString =
ConnectionStringHelpers.GetHostBasedConnectionString();
}
// abbreviated..
}
Alternate:
public partial class MyApplicationDbContext : DbContext
{
public MyApplicationDbContext()
: base(ConnectionStringHelpers.GetHostBasedConnectionString())
{
}
// abbreviated..
}
Helper:
public class ConnectionStringHelpers
{
public static string GetHostBasedConnectionString()
{
return GetConnectionStringByName(GetHostBasedConnectiongStringName());
}
public static string GetHostBasedConnectiongStringName()
{
switch (System.Net.Dns.GetHostName())
{
case "myHostname": return "local"; // My local connection
case "ip-ABCD123": return "cloud"; // Cloud PaaS connection
default: return "cloud";
}
}
public static string GetConnectionStringByName(string name)
{
return ConfigurationManager.ConnectionStrings[name].ConnectionString;
}
}
And in my Web.config file:
<connectionStrings>
<add name="local" connectionString="..." providerName="System.Data.SqlClient" />
<add name="cloud" connectionString="..." providerName="System.Data.SqlClient" />
</connectionStrings>