ASP.NET Web API - App.config does not work? - asp.net

I have simple self-host Web API application. I installed EnityFramework6 package and added following lines in App.config under <enityframework> section:
<contexts>
<context type="simple_api.MyContext, simple_api">
<databaseInitializer type="simple_api.MyInitializer, simple_api" />
</context>
</contexts>
Initializer class is like following:
public class MyInitializer : System.Data.Entity.DropCreateDatabaseIfModelChanges<Context>
{
protected override void Seed(Context context)
{
Console.log("My seed method");
var persons = new List<Person> { };
var john = new Person() { Firstname = "John", Lastname = "Doe" };
context.Persons.Add(john);
context.SaveChanges();
}
}
I enabled and created migration, but the problem is that running it does not trigger my Seed method:
PM> Update-Database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201805210804206_initial].
Applying explicit migration: 201805210804206_initial.
Running Seed method.
I also tried changing App.config, but It seems to be totally ignored, because <context type="foobar, nomatterwhatishere"> does not trigger any warning nor error.
What can be the problem?
--
By the way, when I configured log4net, file was ignored also and I have to call log4net.Config.XmlConfigurator.Configure(). Maybe there is similar thing for EntityFramework?

I was wrong: config file works, but Seed method for DropCreateDatabaseIfModelChanges is not triggered. I replaced it with DropCreateDatabaseAlways and after querying model it throwed exception:
Failed to set database initializer of type 'simple_api.MyInitializer, simple_api' for DbContext type 'simple_api.MyContext, simple_api' specified in the application configuration. See inner exception for details.
After some debugging I figured out that namespace is simple_api, but assembly name is simple-api, so configuration should be as follows:
...
<entityframework>
<contexts>
<context type="simple_api.MyContext, simple-api">
<databaseInitializer type="simple_api.MyInitializer, simple-api" />
</context>
</contexts>
...
</entityframework>
...
Now everything is working, but I am not sure why Seed was
not called for DropCreateDatabaseIfModelChanges.

Related

ASP.NET Web API 2 - ReadAsMultipartAsync alternative/how to

I'm using Web API 2.2 to receive an HTTP post with form data and files, I'm doing the following:
Synchronous version:
[HttpPost]
public IHttpActionResult CreateTicket()
{
string root = HttpContext.Current.Server.MapPath("~/UserFiles/Temp/");
var provider = new MultipartFormDataStreamProvider(root);
// Timeouts
Request.Content.ReadAsMultipartAsync(provider).Wait(TimeSpan.FromSeconds(20));
...
}
Async version:
[HttpPost]
public async Task<HttpResponseMessage> CreateTicket()
{
string root = HttpContext.Current.Server.MapPath("~/UserFiles/Temp/");
var provider = new MultipartFormDataStreamProvider(root);
// Works
await Request.Content.ReadAsMultipartAsync(provider);
...
// Error because: HttpContext.Current == null
}
Both versions of this code work in IIS 10, but fail on production (IIS 8.5). The first version timeouts and the second works but HttpContext.Current is not available which makes other parts of my code fail.
How can I make either the upload synchronous (preferred) or make the async code have HttpContext.Current available?
UPDATE:
Just discovered that the problem (in the second code fragment) only happens if the application does not have the following in the web.config:
<httpRuntime targetFramework="4.6.1" />
Still investigating the side effects to see if I can apply that safely or need to change the code.
I've finally figured it out by creating a small app and trying to reproduce it. As Dmitry and Paulo have pointed out, it should work. However, it should work for any new project and in my case the project is 10 years old and has lots of legacy configurations.
TL;DR: The async/await keywords do not work very well (the HttpContext.Current will be null after calling await) if this setting is not present in the web.config:
<httpRuntime targetFramework="4.6.1" />
That is a shortcut for a bunch of settings, including this one (which is the one I care here):
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
</configuration>
Everything is explained in detail here: https://blogs.msdn.microsoft.com/webdev/2012/11/19/all-about-httpruntime-targetframework/
For reference, it says:
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
Enables the new await-friendly asynchronous pipeline that was
introduced in 4.5. Many of our synchronization primitives in earlier
versions of ASP.NET had bad behaviors, such as taking locks on public
objects or violating API contracts. In fact, ASP.NET 4’s
implementation of SynchronizationContext.Post is a blocking
synchronous call! The new asynchronous pipeline strives to be more
efficient while also following the expected contracts for its APIs.
The new pipeline also performs a small amount of error checking on
behalf of the developer, such as detecting unanticipated calls to
async void methods.
Certain features like WebSockets require that this switch be set.
Importantly, the behavior of async / await is undefined in ASP.NET
unless this switch has been set. (Remember: setting <httpRuntime
targetFramework="4.5" /> is also sufficient.)
If that settings is not present at all, then version 4.0 is assumed and it works in 'quirks'-mode:
If there is no <httpRuntime targetFramework> attribute present in Web.config, we assume that the application wanted 4.0 quirks behavior.
For retrieving files in ASP.NET Core try using IFileProvider instead of HttpContext - see File Providers in ASP.NET Core documentation for more details about configuring and injecting it via DI.
If that is the POST controller action to upload multiple files and receive other data - you can do it this way. Below for demo purposes I use View but data can just go from anywhere as API POST request.
View
#model MyNamespace.Models.UploadModel
<form asp-controller="MyController" asp-action="Upload" enctype="multipart/form-data" method="post">
<input asp-for="OtherProperty">
<input name="Files" multiple type="file">
<button type="submit" class="btn btn-success">Upload</button>
</form>
Model - note that files are passed as IFormFile objects
public class UploadModel
{
public List<IFormFile> Files { get; set; }
public string OtherProperty { get; set; }
}
Controller
[HttpGet]
public IActionResult Upload()
{
return View(new UploadModel());
}
[HttpPost]
public async Task<IActionResult> Index(UploadModel model)
{
var otherProperty = model.OtherProperty;
var files = new Dictionary<string, string>();
foreach (IFormFile file in model.Files)
{
using (var reader = new StreamReader(file.OpenReadStream()))
{
string content = await reader.ReadToEndAsync();
files.Add(file.Name, content);
// Available file properties:
// file.FileName
// file.ContentDisposition
// file.ContentType
// file.Headers
// file.Length
// file.Name
// You can copy file to other stream if needed:
// file.CopyTo(new MemoryStream()...);
}
}
}

ProfileCommon.GetProfile(string)' hides inherited member '.UserProfile.GetProfile(string)'. Use the new keyword if hiding was intended

I am using aspnet membership profile and Inherited the profileBase class to a class name UserProfile, where I have defined the method GetProfile. Everything working fine But while build getting the above warning.
Please help how to remove the warning.
Below the sample codes.
public static UserProfile GetProfile(string userName)
{
return (UserProfile)Create(userName);
}
public static UserProfile GetProfile(string username, bool authenticated)
{
return (UserProfile)Create(username, authenticated);
}
public static UserProfile Current()
{
return ((UserProfile)(HttpContext.Current.Profile));
}
<!-- Profile configuration -->
<profile enabled="true" defaultProvider="EFProfileProvider" inherits="Jan.DB.Provider.UserProfile" automaticSaveEnabled="true">
<providers>
<clear/>
<add name="EFProfileProvider" type="Jan.DB.Provider.EFProfileProvider" connectionStringName="JanEntities" applicationName=""/>
</providers>
</profile>
While migrating from simple membership to Identity we had made some mistake. Although we have removed all the membership related codes. One thing we have done is how profile is handled. I mean in idenity customization we have used the old UserProfile.cs of membership with some customization. But as we are using
in web.config so at runtime Asp.net memebrship automatically creates the ProfileCommon.cs and for that we are getting that warning.
so can avoid that warning by cleaning of the customization and moved to how identity handles the profile information.

You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class

I can't make WebSecurity object work anywhere except what's already been generated in AccountController.cs file. Account controller has [InitializeSimpleMembership] attribute set at the top. Login functions don't complain about calling WebSecurity.Login(...), for example. I added a child action to AccountController:
[ChildActionOnly]
[AllowAnonymous]
public ActionResult NavBar()
{
NavBarViewModel viewModel = new NavBarViewModel();
viewModel.LinkItems = new List<NavBarLinkItem>();
if (Request.IsAuthenticated)
{
SimpleRoleProvider roleProvider = new SimpleRoleProvider();
if (roleProvider.IsUserInRole(User.Identity.Name, "User"))
{
viewModel.LinkItems.Add(new NavBarLinkItem()
{ Title = "Create Project", Action = "Create", Controller = "Project" });
}
}
viewModel.LinkItems.Add(new NavBarLinkItem() { Title="Help", Action="Index", Controller="Help" });
return PartialView("_NavBar", viewModel);
}
Left as is, the code crashes on "if (roleProvider.IsUserInRole(User.Identity.Name, "User"))" line with the subject error message. So I go into InitialzeSimpleMembershipAttribute.cs file and copy/paste this line at the top of my function:
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
... and get an error message that WebSecurity.InitializeDatabaseConnection should only be called once. This makes sense, because there is an attribute at the top of the controller definition that should've called this function already (and it seems it does that just fine). So to be safe, I change above call to:
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId",
"UserName", autoCreateTables: true);
}
... and get back the original error message, that WebSecurity.InitializeDatabaseConnection should be called before blah blah blah. Any insight into this madness would be greatly appreciated
There's a better explanation here:
http://odetocode.com/blogs/scott/archive/2012/09/24/perils-of-the-mvc4-accountcontroller.aspx
Here's all you have to do:
Remove [InitializeSimpleMembership] from the top of AccountController
Copy the WebSecurity.InitializeDatabaseConnection(...) call from /Filters/InitializeSimpleMembershipAttribute.cs (line 39) to /AppStart/AuthConfig.cs
Feel free to remove InitializeSimpleMembershipAttribute.cs from your project
You don't have to add the InitializeDatabaseConnection() call to AuthConfig.RegisterAuth() but it seems like the logical place and keeps your Global.asax cleaner.
What you are essentially doing is extracting the initialization call from the original attribute and explicitly calling it on Application_Start. Everything else in the attribute is just conditional checking in case you aren't using (or don't need) SimpleMembership.
Adding the [InitializeSimpleMembership] (as mentioned and found in the AccountController) to the top of the controller I needed to access WebSecurity from did the trick for me. Not sure however if its the intended implementation method though...
[InitializeSimpleMembership]
public class DataController : Controller
{ ... }
I found this on the interwebs: http://forums.asp.net/t/1718361.aspx/1
Basically, don't use SimpleRoleProvider type. There is a Roles object available that allows simple calls like this:
if (Request.IsAuthenticated)
{
if( Roles.IsUserInRole(User.Identity.Name, "User"))
{
viewModel.LinkItems.Add(new NavBarLinkItem()
{ Title = "Create Project", Action = "Create", Controller = "Campaign" });
}
}
First you have to set your role and membership provider in the web.config:
<authentication mode="Forms">
<forms loginUrl="/Account/Login" slidingExpiration="true" timeout="60" />
</authentication>
<membership defaultProvider="p1">
<providers>
<add name="p1" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData"/>
</providers>
</membership>
<roleManager enabled="true" defaultProvider="p1">
<providers>
<add name="p1" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>
When you create a new instance of SimpleRoleProvider, use the non-null constructor, and supply the default RoleProvider set in the web.config as an argument:
SimpleRoleProvider srp = new SimpleRoleProvider(Roles.Provider);
The solution is the same in the case of SimpleMembershipProvider:
SimpleMembershipProvider msp = new SimpleMembershipProvider(Membership.Provider);
In my case I had to disable Anonymous Authentication within the IIS Authentication setting.
Then I had to enable Forms and Windows Authentication. This will of course depend on whichever authentication you require for your app.
Once I did that the error went away and the app worked as expected.

Mysql syntax error while creating Database for Entity Framework

I'm playing around with asp.net for the first time. I want to use it with a MySQL database because this is what is offered by my hosting service and I don't want to upgrade/change services. I'm using visual web developer 2010 express. I created an MVC 4 project from the default template. The template created the ASP.NET Simple Membership objects which is what I'm trying to get working. The project builds and runs correctly when using the default database connection string. When I change the web.config file to point to MySQL I get the following error when I attempt to navigate to any of the pages in the account folder.
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'IDENTITY,
RoleName nvarc' at line 2
When I open the MySQL work bench and connect to the local server I notice that the database has been created. If I drop the DB and run the app again it gets recreated. I'm note sure if it was created correctly or if the entire database was created but there is something there.
Obviously there is an issue with the SQL syntax that is created by the Entity Framework. Do I need to add something to the web.config file to tell it what syntax it should use when creating the queries?
I've been searching for an answer to this for the past two days. any help pointing in the right direction would be appreciated.
I'm using mysql server version 5.5.27. and connector 6.5.4.0
here is the mysql part of my web.config file:
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient"/>
<add name="MySQL Data Provider"
invariant="MySql.Data.MySqlClient"
description=".Net Framework Data Provider for MySQL"
type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=aspnet-MyWebPage-20120817115958;Integrated Security=SSPI" providerName="System.Data.SqlClient" />
<add name="myDatabaseConnection" connectionString="server=localhost;Port=3306;uid=root;pwd=****;database=myDatabase;" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
Edit adding code
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
Database.SetInitializer<UsersContext>(null);
try
{
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("LocalMySqlServer", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
public class UsersContext : DbContext
{
public UsersContext()
: base("LocalMySqlServer")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
}
Try to modify the source of the SMP and remove the syntax specific to ms sql server.
The role provider is still defaulting to the standard ASP one which is expecting a SQLServer DB on the end of the connection, "Identity" is SQLServerese for "autoinc".
You can set the default providers in the web.config like this:-
<configuration>
<system.web>
<profile defaultProvider="MySQLProfileProvider"></profile>
<roleManager defaultProvider="MySQLRoleProvider"></roleManager>
</system.web>
</configuration>

Orchard/MVC WCF Service Url with Area

Bertrand created a blog post to specify how to use IoC in WCF Modules for Orchard.
In 1.1, you can create a SVC file using the new Orchard host factory:
<%# ServiceHost Language="C#" Debug="true"
Service="MyModule.IMyService, MyAssembly"
Factory="Orchard.Wcf.OrchardServiceHostFactory, Orchard.Framework" %>
Then register your service normally as an IDependency but with service and operation contract attributes:
using System.ServiceModel;
namespace MyModule {
[ServiceContract]
public interface IMyService : IDependency {
[OperationContract]
string GetUserEmail(string username);
}
}
My question is that all of Orchard's modules are really area's. So how can you build a route that hits the svc file created in the area/module?
Should you use the full physical path to get to the svc file (tried that and it caused a web.config issue since it was bridging a site and area).
http://localhost/modules/WebServices/MyService.svc
Or do you create a ServiceRoute with WebServiceHostFactory/OrchardServiceHostFactory?
new ServiceRoute("WebServices/MyService", new OrchardServiceHostFactory(), typeof(MyService))
Whatever I try I get a 404 when trying to hit the resource. I was able to get this working using a wcf Application project and setting WCF as a stand alone application, my issues started when trying to bring it into Orchard/MVC.
UPDATE
Thanks for the help Piotr,
This is the steps I took to implement the service.
Routes.cs
new RouteDescriptor { Priority = 20,
Route = new ServiceRoute(
"Services",
new WebServiceHostFactory(),
typeof(MyService)) }
If I use OrchardServiceHostFactory() instead of WebServiceHostFactory() I get the following error.
Operation is not valid due to the current state of the object.
Orchard Root Web.Config
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
MyService
[ServiceContract]
public interface IMyService : IDependency
{
[OperationContract]
string GetTest();
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
class MyService : IMyService
{
public string GetTest()
{
return "test";
}
}
I couldn't get the service working by just modifying the module's web.config. I get the following error
ASP.NET routing integration feature requires ASP.NET compatibility.
UPDATE 2
Orchard Root Web.Config
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<!-- ... -->
</system.serviceModel>
Routes.cs
public IEnumerable<RouteDescriptor> GetRoutes() {
return new[] {
new RouteDescriptor { Priority = 20,
Route = new ServiceRoute(
"Services",
new OrchardServiceHostFactory(),
typeof(IMyService))
}
};
}
This works, the key here is that you must call typeof on the object that is referencing IDependency, WorkContextModule.IsClosingTypeOf cant handle the object that consumes the dependancy, it must take the Interface that it is directly called by.
As you stated, Orchard modules are areas in ASP.NET MVC terms, so the URL you provided is incorrect and should be:
http://localhost/Your.Orchard.Module/WebServices/MyService.svc
Where localhost is the virtual directory under which your app runs and /WebServices is a folder in the root of your module.
You can also create a service route programatically without problem. This article tells how to add new routes in Orchard. You can just assign a ServiceRoute to the Route property of a RouteDescriptor instead of a default MVC route (as shown in docs).
The question about adding ServiceRoute in area-enabled ASP.NET MVC app was asked before, check it out as it may help you out.
Btw - You may also check this SO question about prefixed service routes.
HTH

Resources