I have a traditional ASP.NET WebForms application. I want to migrate it over to ASP.NET MVC 3. One of the central components of my WebForms application is a heavy use of the ProfileCommon instance that is automatically generated. Unfortunately, that object isn't available in ASP.NET MVC 3 applications.
I have created a ProfileCommon class using the advice found in the following thread: Implementing Profile Provider in ASP.NET MVC
My ProfileCommon class looks like the following:
public class ProfileCommon : ProfileBase
{
public virtual string FirstName
{
get { return ((string)(this.GetPropertyValue("FirstName"))); }
set { this.SetPropertyValue("FirstName", value); }
}
public virtual string LastName
{
get { return ((int)(this.GetPropertyValue("LastName"))); }
set { this.SetPropertyValue("LastName", value); }
}
public static new ProfileCommon Create(string username)
{
return ProfileBase.Create(username) as ProfileCommon;
}
public virtual ProfileCommon GetProfile(string username)
{
return Create(username) as ProfileCommon;
}
}
In my web.config, I have:
<profile inherits="MyCompany.Namespace1.ProfileCommon" defaultProvider="CustomTableProfileProvider">
<providers>
<clear/>
<add name="CustomTableProfileProvider" type="MyCompany.Namespace2.SqlTableProfileProvider" connectionStringName="MyDatabaseCS" table="MyTable" applicationName="MyApplication" />
</providers>
</profile>
I then have the following code in my method in my controller that looks like this:
ProfileCommon userProfile = (ProfileCommon)(ProfileCommon.Create(username));
string greeting = "Hello " + userProfile.FirstName;
When the second line gets executed (the greeting), I get an exception. The exception says:
System.Configuration.Provider.ProviderException: The CustomProviderData attribute is required on each provider property in the web.config file.
I do not understand what that means or what I need to do. I'm slightly confused because the individual in the previous aforementioned thread says everything worked. But it doesn't for me. Can someone please help me correct this error?
Thank you!
Related
I'm learning APB framework. ABP can automagically configure the application services as API Controllers by convention. The documentation says it is possible to fully customize it.
In the example they provide, I would like to change an action name of one of the following endpoints:
/api/app/book to /api/app/books.
But unfortunately I cannot find how to do it.
I tried to change the ActionName of the corresponding service method:
public class BookAppService :
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
}
[ActionName("books"), HttpGet]
public override Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{
return base.GetListAsync(input);
}
}
But the resulting endpoint is not what I want:
Any idea how to do it ?
in abp.io (ABP VNext) , you can diable or configure ConventionalControllers in HttpModules config file of your Project .
Edit this Method :
private void ConfigureConventionalControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(SystemApplicationModule).Assembly);
});
}
I have the following method in my apiController:
public IEnumerable<something> GetData(DataProvider dataProvider)
{
return dataProvider.GetData();
}
What I need is to invoke this method from javascript and pass it a parameter of DataProvider derived type. I can handle this by passing string, e.g. "FirstProvider" and than write N number of if's in GetData() method to create an instance of proper type.
But is there some way that I can write in web.config file something like:
<DataProviders>
<type = FirstDataProvider, alias = "FirstProvider">
<type = SecondDataProvider, alias = "SecondProvider">
</DataProviders>
Change getData method to:
public IEnumerable<something> GetData(string dataProviderAlias)
{
// get provider type by it's alias from web congfig,
// then instantiated and call:
return dataProvider.GetData();
}
And then find and instantiate the type by it's alias?
Note: I accepted the answer below cause it's pointed me in a right direction, but msdn says that IConfigurationSectionHandler is deprecated.
So I used ConfigurationSection, ConfigurationElementCollection, ConfigurationElement classes instead to build custom config section.
You can store arbitrary data in web.config in the appSettings element:
<configuration>
<appSettings>
<add key="FirstAlias" value="FirstProvider" />
<add key="SecondAlias" value="SecondProvider" />
</appSettings>
</configuration>
And you can then read the values using:
String firstAlias = System.Configuration.ConfigurationManager.AppSettings["FirstAlias"];
String secondAlias = System.Configuration.ConfigurationManager.AppSettings["SecondAlias"];
It's built-in. It's supported. It's where you're supposed to store custom data.
First of all, you can only store valid xml in web.config. <type = FirstDataProvider, alias = "FirstProvider"> is not valid xml.
Second, there are a lot of moving pieces. Please follow the steps carefully -
web.config
Make sure you enter the proper namespace for DataProviders. type="YOUR_APPLICATION.DataProviders".
<configuration>
<configSections>
<section name="DataProviders" type="WebApplication2010.DataProviders"
requirePermission="false"/>
</configSections>
<DataProviders>
<Provider type="FirstDataProvider" alias="FirstProvider"/>
<Provider type="SecondDataProvider" alias="SecondProvider"/>
</DataProviders>
....
</configuration>
Code
public class DataProviders : IConfigurationSectionHandler
{
private static bool _initialized;
public static List<Provider> _providers;
public object Create(object parent, object configContext, XmlNode section)
{
XmlNodeList providers = section.SelectNodes("Provider");
_providers = new List<Provider>();
foreach (XmlNode provider in providers)
{
_providers.Add(new Provider
{
Type = provider.Attributes["type"].Value,
Alias = provider.Attributes["alias"].Value,
});
}
return null;
}
public static void Init()
{
if (!_initialized)
{
ConfigurationManager.GetSection("DataProviders");
_initialized = true;
}
}
public static IEnumerable<Provider> GetData(string dataProviderAlias)
{
return _providers.Where(p => p.Alias == dataProviderAlias);
}
}
public class Provider
{
public string Type { get; set; }
public string Alias { get; set; }
}
Global.asax
For good design practice, you want to read data from web.config only once, and store them in static variables. Therefore, you want to initialize inside Application_BeginRequest of Global.asax.
public class Global : System.Web.HttpApplication
{
void Application_BeginRequest(object sender, EventArgs e)
{
DataProviders.Init();
}
}
Usage
var providers = DataProviders.GetData("FirstProvider").ToList();
Well, I'm not sure if I understand what you want to achieve, but to implement your idea you need a custom section handler.
http://msdn.microsoft.com/en-us/library/2tw134k3(v=vs.100).aspx
In case that you want to create a database connection for specific dataprovider, see this similar question:
ASP.NET: How to create a connection from a web.config ConnectionString?
In my case, I needed to store two byte[] variables in my Web.Config file. Since it must be valid XML data, I simply stored the contents of the arrays like so:
<appSettings>
<add key="Array1" value="0,1,2,3,4,5,6,7,8,9" />
<add key="Array2" value="0,1,2,3,4,5,6,7,8,9" />
</appSettings>
I then call a function that reads this into a C# string, split it into a string[], and parse each string element as a byte into a resulting byte[] and return it.
I have a ASP.NET MVC page with FluentSecurity. I have it set up using Ninject according to this article. I have a DenyAnonymousAccessPolicyViolationHandler that works well. I added a RequireRolePolicyViolationHandler.
In my setup, I have
configuration.For<SettingsController>().RequireRole(CMSRoles.Admin);
If I navigate to the SettingsController with a user without the required role, the RequireRolePolicyViolationHandler does not get called. Instead I am redirected to the LogOn page as defined in web.config.
Am I missing something? According to the FluentSecurity documentation it should work.
EDIT: I have a custom RoleProvider registered and I use it with FluentSecurity:
configuration.GetAuthenticationStatusFrom(() => HttpContext.Current.User.Identity.IsAuthenticated);
configuration.GetRolesFrom(() => Roles.GetRolesForUser(HttpContext.Current.User.Identity.Name));
EDIT: I created a minimal sample app: https://dl.dropboxusercontent.com/u/73642/MvcApplication1.zip. If you go to /Logged that you are redirected to the login page so the DenyAnonymousAccessPolicyViolationHandler works. You can login with any username and password you want. The go to Settings and you see that you are redirected to the login page instead of RequireRolePolicyViolationHandler beeing executed.
Here's how I have it set up, hope this helps:
In App_Start/NinjectWebCommon.cs I bind the policy handlers:
kernel.Bind<IPolicyViolationHandler>().To<DenyAnonymousAccessPolicyViolationHandler>();
kernel.Bind<IPolicyViolationHandler>().To<RequireRolePolicyViolationHandler>();
I also configure Fluent Security like this (using Ninject Service Locator):
var locator = new NinjectServiceLocator(kernel);
ServiceLocator.SetLocatorProvider(() => locator);
SecurityConfigurator.Configure(
configuration =>
{
configuration.GetAuthenticationStatusFrom(() => HttpContext.Current.User.Identity.IsAuthenticated);
configuration.GetRolesFrom(SecurityHelpers.UserRoles);
//HomeController and other configurations
configuration.For<HomeController>().Ignore();
configuration.ResolveServicesUsing(ServiceLocator.Current.GetAllInstances);
}
);
GlobalFilters.Filters.Add(new HandleSecurityAttribute(), 0);
Then for each policy, I have an implementation of IPolicyViolationHandler
public class RequireRolePolicyViolationHandler : IPolicyViolationHandler
{
public ActionResult Handle(PolicyViolationException exception)
{
//Make sure you're redirecting to the desired page here. You should put a stop here to debug it and see if it's being hit.
return new HttpUnauthorizedResult(exception.Message);
}
}
I have a working solution using Custom Membership/Role Providers and Fluent Security. I posted what I think is the core configuration. Hope this helps.
EDIT: Added how to get roles.
public static class SecurityHelpers
{
public static IEnumerable<object> UserRoles()
{
var currentUser = HttpContext.Current.User.Identity.Name;
var roles = Roles.Providers["MemberAccountRoleProvider"]; //Custom Role Provider Name
return currentUser != null ? roles.GetRolesForUser(currentUser).Cast<object>().ToArray() : null;
}
}
EDIT 2:
I looked at your code and it's working fine. Add this to your code so that you can redirect to where you want. Right now you're just returning an Http results:
public class RequireRolePolicyViolationHandler : IPolicyViolationHandler
{
public ActionResult Handle(PolicyViolationException exception)
{
//return new HttpUnauthorizedResult(exception.Message);
return
new RedirectToRouteResult(
new RouteValueDictionary(new { action = "Test", controller = "Account"})); //Created a view for testing
}
}
When I try to get the settings page I'm hitting the RequireRolePolicyViolationHandler.
I'm currently trying to use the same DbContext (I have two databases, of identical structure) in my application. I'm not quite sure what I'm doing wrong, but here's my current code - hopefully it should be pretty obvious what I'm trying to do. I'm using EF Database First (which the error at the bottom seems not to suggest).
My context factory code:
public class HOLContextFactory
{
public static HOLDbEntities Create()
{
return new HOLDbEntities(); // Works
}
public static HOLDbQuoteEntities CreateQuote()
{
return new HOLDbQuoteEntities(); // Gives error
}
}
public partial class HOLDbQuoteEntities : HOLDbEntities
{
public HOLDbQuoteEntities()
: base("HOLDbQuoteEntities") // This should send "HOLDbQuoteEntities" as the base connection string?!
// Also tried "name=HOLDbQuoteEntities"
{
}
}
Web.config connection strings:
<add name="HOLDbEntities" connectionString="metadata=res://*/HOLDbContext.csdl|res://*/HOLDbContext.ssdl|res://*/HOLDbContext.msl;provider=System.Data.SqlClient;provider connection string=<connstringdetails>" providerName="System.Data.EntityClient" />
<add name="HOLDbQuoteEntities" connectionString="metadata=res://*/HOLDbContext.csdl|res://*/HOLDbContext.ssdl|res://*/HOLDbContext.msl;provider=System.Data.SqlClient;provider connection string=<connstringdetails>" providerName="System.Data.EntityClient" /> // using diff database - same structure
Error I'm getting when using "HOLDbQuoteEntities" :
Code generated using the T4 templates for Database First and Model
First development may not work correctly if used in Code First mode.
To continue using Database First or Model First ensure that the Entity
Framework connection string is specified in the config file of
executing application. To use these classes, that were generated from
Database First or Model First, with Code First add any additional
configuration using attributes or the DbModelBuilder API and then
remove the code that throws this exception**
Entity Framework needs to use the actual entities object:
public class HOLContextFactory
{
public static HOLDbEntities Create()
{
// default connection string
return new HOLDbEntities();
}
public static HOLDbEntities CreateQuote()
{
// specified connection string
return new HOLDbEntities ("HOLDbQuoteEntities");
}
}
public partial class HOLDbEntities
{
public HOLDbEntities(string connectionString)
: base(connectionString)
{
}
}
}
I've done the same thing in one of my project. I am creating my entity context using metadata=res://*/
Try this:
<add name="HOLDbEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string=<connstringdetails>" providerName="System.Data.EntityClient" />
<add name="HOLDbQuoteEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string=<connstringdetails>" providerName="System.Data.EntityClient" />
Today I experienced a weird problem while trying to remotely debug an application built for the .NET 4.0 runtime.
The application resides on a network share and executed by a remote machine. However the application crashes each time during load because of a SecurityException raised by a permission demand in the System.Configuration.ConfigurationManager.GetSection() method. I have not checked if other permission demands in the base class library also cause a security exception but in all cases this shouldn't be happening with the new CLR.
The application is running in full trust (checked it while debugging and as usual this must be always true for intranet applications in CLR 4.0) so I am clueless how a permission demand can cause an exception in this case. When built against the 3.5 SP1 runtime (which first introduced full trust for network shared apps by default) everythings runs as expected.
I pasted the sample code below. Any help is greatly appreciated.
using System;
using System.Configuration;
namespace ConsoleApplication1
{
public sealed class AssetsSection : ConfigurationSection
{
private static readonly ConfigurationProperty s_propPath;
private static readonly ConfigurationPropertyCollection s_properties;
static AssetsSection()
{
s_propPath = new ConfigurationProperty("path", typeof(String));
s_properties = new ConfigurationPropertyCollection()
{
s_propPath
};
}
public static AssetsSection Get()
{
return (AssetsSection) ConfigurationManager.GetSection("test/assets");
}
protected override ConfigurationPropertyCollection Properties
{
get
{
return s_properties;
}
}
public String Path
{
get
{
return (String) base[s_propPath];
}
set
{
base[s_propPath] = value;
}
}
}
class Program
{
static void Main(String[] args)
{
Console.WriteLine(AssetsSection.Get().Path);
Console.ReadLine();
}
}
}
And the App.config file;
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="test">
<section name="assets" type="ConsoleApplication1.AssetsSection, ConsoleApplication1"/>
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
</startup>
<test>
<assets path="..\Assets"/>
</test>
</configuration>
Try loading the configuration first and open your section on that:
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
AssetsSection configSection = (AssetsSection)config.GetSection("test/assets");
I ran into the same issue with .NET 4 and this works for me.
This is due to a known bug in .NET 4.0 when running the application from a network share.
The follow code fails with a SecurityException. Note that it only fails when you have defined a custom type for the section like in this example AssetsSection:
ConfigurationManager.GetSection("test/assets");
One fix is the solution suggestion by Timo to use a different API. Another solution is to apply the patch provided by Microsoft.
The bug and the related hotfix is filed under KB2580188.
If you add your own class to map the section like this:
[XmlRoot("Interface")]
public class MySectionClass
{
[XmlAttribute()]
public string MyAttr1
{
get;
set;
}
public string MyAttr2
{
get;
set;
}
}
You can use this code:
ConfigurationSection configSection =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).
GetSection("MySection");
XmlSerializer xs = new XmlSerializer(typeof(MySectionClass));
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(configSection.SectionInformation.GetRawXml());
XmlNodeReader xnr = new XmlNodeReader(xdoc.DocumentElement);
MySectionClass section = (MySectionClass)xs.Deserialize(xnr);
I'm speculating here, but I suspect it's your configuration file that's not trusted.
In your case, your configuration file is referencing a type ConsoleApplication1.AssetsSection that does not have a strong name that could be used as evidence.
Can you provide more details and the exact error message.