Global routing prefix in Core 3.1 API - asp.net-core-3.1

I have attribute [Route("api/v{version:apiVersion}/[controller]")] on every controller and this is redundant.
Is there a way to set the api/v{version:apiVersion}/ part globally somewhere so I can just specify what the controller should be?
I've tried using a base controller, MapControllerRoute, and UsePathBase.

You should be able to create an attribute that implements IRouteTemplateProvider
/// <summary>
/// Class to ensure that we use our default naming convension for controller routes
/// </summary>
public class DefaultRoutingAttribute : Attribute, IRouteTemplateProvider {
public string Template => "api/v{version:apiVersion}/[controller]";
/// <summary>
/// Order is 2 to allow explicitly overriding the default route
/// </summary>
public int? Order => 2;
public string Name { get; set; }
}
and then use it like
[ApiController]
[DefaultRouting]
public class YourController : ControllerBase {}
I have not tested exactly your route with the apiversion but it works well with a simpler route in my application.

Related

How to use DataProtectionKeys with multiple applications?

I'm running with the code below and I can see that one key is created in the table [DataProtectionKeys]
services.AddDataProtection()
.SetApplicationName(dataProtectionSettings.ApplicationName)
.ProtectKeysWithCertificate(serviceCertificate)
.UnprotectKeysWithAnyCertificate(serviceCertificate)
.PersistKeysToDbContext<DataProtectionContext>();
I'm using this database context (am I missing something?):
class DataProtectionContext : DbContext, IDataProtectionKeyContext
{
/// <summary>
/// A recommended constructor overload when using EF Core with dependency injection.
/// </summary>
/// <param name="options"></param>
public DataProtectionContext(DbContextOptions<DataProtectionContext> options)
: base(options) { }
/// <summary>
/// This maps to the table that stores keys.
/// </summary>
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
}
But, if I change to ApplicationName string value to something else, I don't see that a new key is created
Any idea why or how to fix it so this database table can support multiple application

Identity with efcore for customer and staff

I currently design table for customer and staff for my ecommerce app and I am using asp.net core identity. I want to know if I should use 1 table user (aka aspnetuser) for staff and customer or should I separate them and use user id as foreign key? If separating them 2 new table with foreign key is user id, how can I use user manager for creating account for staff and customer?
Thanks.
You can extend the base IdentityUser class in order to create a table with additional fields, like:
public class MyIdentityUser : IdentityUser<string>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Type { get; set; }
...
where the Type field could be Staff or Customer.
Or you can use one or more additional table and use the Id as defined in IdentityUser class from Microsoft.ASpNetCore.Identity:
public class IdentityUser<TKey> where TKey : IEquatable<TKey>
{
/// <summary>
/// Initializes a new instance of <see cref="IdentityUser{TKey}"/>.
/// </summary>
public IdentityUser() { }
/// <summary>
/// Initializes a new instance of <see cref="IdentityUser{TKey}"/>.
/// </summary>
/// <param name="userName">The user name.</param>
public IdentityUser(string userName) : this()
{
UserName = userName;
}
/// <summary>
/// Gets or sets the primary key for this user.
/// </summary>
[PersonalData]
public virtual TKey Id { get; set; }
...
you can define the type of your Id field, like my previous example, and use the same type on a related field (like IdentityUserId) in your custom table/tables.
If you extend the base IdentityUser class you need to create a derived context with this declaration, like this:
namespace MyProject.Infrastructure.Contexts
{
public class MyContext : IdentityDbContext<MyIdentityUser>
{
...

How are Views injected into the UI using PRISM and MEF?

I have already searched some tutorials and even looked pluralsite Introduction to PRISM. However, most examples based on using unity containers and the some lack of information on how to implement this feature with Mef container.
My simple helloworld module is based on web tutorial. My code is the same except I’m stuck only on HelloModule and using Mef, not Unity as tutorial shows:
The main my problem how to initialize my view with my view model. The only working way I have found via experimenting is to initialize view-model in View constructor:
HelloView.xaml.cs
namespace Hello.View
{
[Export]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
Model = new HelloViewModel(this);
}
public IHelloViewModel Model
{
//get { return DataContext as IHelloViewModel; }
get { return (IHelloViewModel)DataContext; }
set { DataContext = value; }
}
}
}
And standard module initialization code:
[ModuleExport(typeof(HelloModule), InitializationMode=InitializationMode.WhenAvailable)]
public class HelloModule : IModule
{
IRegionManager _regionManager;
[ImportingConstructor]
public HelloModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
_regionManager.Regions[RegionNames.ContentRegion].Add(ServiceLocator.Current.GetInstance<HelloView>());
}
}
However, can someone tell the correct way how to this things, I this it must be done in Module initialization section.
MatthiasG shows the way to define modules in MEF. Note that the view itself does not implement IModule. However, the interesting part of using MEF with PRISM is how to import the modules into your UI at startup.
I can only explain the system in principle here, but it might point you in the right direction. There are always numerous approaches to everything, but this is what I understood to be best practice and what I have made very good experiences with:
Bootstrapping
As with Prism and Unity, it all starts with the Bootstrapper, which is derived from MefBootstrapper in Microsoft.Practices.Prism.MefExtensions. The bootstrapper sets up the MEF container and thus imports all types, including services, views, ViewModels and models.
Exporting Views (modules)
This is the part MatthiasG is referring to. My practice is the following structure for the GUI modules:
The model exports itself as its concrete type (can be an interface too, see MatthiasG), using [Export(typeof(MyModel)] attribute. Mark with [PartCreationPolicy(CreationPolicy.Shared)] to indicate, that only one instance is created (singleton behavior).
The ViewModel exports itself as its concrete type just like the model and imports the Model via constructor injection:
[ImportingConstructor]
public class MyViewModel(MyModel model)
{
_model = model;
}
The View imports the ViewModel via constructor injection, the same way the ViewModel imports the Model
And now, this is important: The View exports itself with a specific attribute, which is derived from the 'standard' [Export] attribute. Here is an example:
[ViewExport(RegionName = RegionNames.DataStorageRegion)]
public partial class DataStorageView
{
[ImportingConstructor]
public DataStorageView(DataStorageViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
The [ViewExport] attribute
The [ViewExport] attribute does two things: Because it derives from [Export] attribute, it tells the MEF container to import the View. As what? This is hidden in it's defintion: The constructor signature looks like this:
public ViewExportAttribute() : base(typeof(UserControl)) {}
By calling the constructor of [Export] with type of UserControl, every view gets registered as UserControl in the MEF container.
Secondly, it defines a property RegionName which will later be used to decide in which Region of your Shell UI the view should be plugged. The RegionName property is the only member of the interface IViewRegionRegistration. The attribute class:
/// <summary>
/// Marks a UserControl for exporting it to a region with a specified name
/// </summary>
[Export(typeof(IViewRegionRegistration))]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[MetadataAttribute]
public sealed class ViewExportAttribute : ExportAttribute, IViewRegionRegistration
{
public ViewExportAttribute() : base(typeof(UserControl)) {}
/// <summary>
/// Name of the region to export the View to
/// </summary>
public string RegionName { get; set; }
}
Importing the Views
Now, the last crucial part of the system is a behavior, which you attach to the regions of your shell: AutoPopulateExportedViews behavior. This imports all of your module from the MEF container with this line:
[ImportMany]
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
This imports all types registered as UserControl from the container, if they have a metadata attribute, which implements IViewRegionRegistration. Because your [ViewExport] attribute does, this means that you import every type marked with [ViewExport(...)].
The last step is to plug the Views into the regions, which the bahvior does in it's OnAttach() property:
/// <summary>
/// A behavior to add Views to specified regions, if the View has been exported (MEF) and provides metadata
/// of the type IViewRegionRegistration.
/// </summary>
[Export(typeof(AutoPopulateExportedViewsBehavior))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class AutoPopulateExportedViewsBehavior : RegionBehavior, IPartImportsSatisfiedNotification
{
protected override void OnAttach()
{
AddRegisteredViews();
}
public void OnImportsSatisfied()
{
AddRegisteredViews();
}
/// <summary>
/// Add View to region if requirements are met
/// </summary>
private void AddRegisteredViews()
{
if (Region == null) return;
foreach (var view in _registeredViews
.Where(v => v.Metadata.RegionName == Region.Name)
.Select(v => v.Value)
.Where(v => !Region.Views.Contains(v)))
Region.Add(view);
}
[ImportMany()]
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
}
Notice .Where(v => v.Metadata.RegionName == Region.Name). This uses the RegionName property of the attribute to get only those Views that are exported for the specific region, you are attaching the behavior to.
The behavior gets attached to the regions of your shell in the bootstrapper:
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
ViewModelInjectionBehavior.RegionsToAttachTo.Add(RegionNames.ElementViewRegion);
var behaviorFactory = base.ConfigureDefaultRegionBehaviors();
behaviorFactory.AddIfMissing("AutoPopulateExportedViewsBehavior", typeof(AutoPopulateExportedViewsBehavior));
}
We've come full circle, I hope, this gets you an idea of how the things fall into place with MEF and PRISM.
And, if you're still not bored: This is perfect:
Mike Taulty's screencast
The way you implemented HelloView means that the View has to know the exact implementation of IHelloViewModel which is in some scenarios fine, but means that you wouldn't need this interface.
For the examples I provide I'm using property injection, but constructor injection would also be fine.
If you want to use the interface you can implement it like this:
[Export(typeof(IHelloView)]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
}
[Import]
public IHelloViewModel Model
{
get { return DataContext as IHelloViewModel; }
set { DataContext = value; }
}
}
[Export(typeof(IHelloViewModel))]
public class HelloViewModel : IHelloViewModel
{
}
Otherwise it would look like this:
[Export(typeof(IHelloView)]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
}
[Import]
public HelloViewModel Model
{
get { return DataContext as HelloViewModel; }
set { DataContext = value; }
}
}
[Export]
public class HelloViewModel
{
}
One more thing: If you don't want to change your Views or provide several implementations of them, you don't need an interface for them.

EF Code First Multilevel Inheritance Issue

I have an inheritance hierarchy setup that I am mapping to a DB via TPT in Code first. For the most part the hierarchy is one level deep, but sometimes it it two. My base class looks like this:
public class AuditEvent
{
public int AuditEventID;
//other stuff
};
Then I have a bunch of other classes that look like this (with different names and properties):
public class PageRequest : AuditEvent
{
/// <summary>
/// Page Request Id (Primary Key)
/// </summary>
public Int64 PageRequestID { get; set; }
/// <summary>
/// Screen (page) being requested
/// </summary>
public string Screen { get; set; }
/// <summary>
/// Http Method
/// </summary>
public string HttpMethod { get; set; }
/// <summary>
/// Confirmation Logs linked to this page request
/// </summary>
public virtual List<ConfirmationLog> ConfirmationLogs { get; set; }
}
This specific class (PageRequest) is a parent to one other class called ConfirmationLog, which looks like this:
/// <summary>
/// Object used to log confirmations to the auditing database
/// </summary>
public class ConfirmationLog : PageRequest
{
/// <summary>
/// Confirmation ID
/// </summary>
public long ConfirmationID { get; set; }
/// <summary>
/// Confirmation number
/// </summary>
public string ConfirmationNum { get; set; }
/// <summary>
/// Web action ID (automated alert or transaciton confirmation number)
/// </summary>
public int WebActionID { get; set; }
}
I'm configuring the mappings using configuration classes and the fluent API, like so:
/// <summary>
/// Configuration class for PageRequest
/// </summary>
public class PageRequestConfiguration : EntityTypeConfiguration<PageRequest>
{
/// <summary>
/// Default constructor
/// </summary>
public PageRequestConfiguration()
{
//Table
ToTable("PageRequests");
//primary key
HasKey(a => a.PageRequestID);
//Properties
Property(a => a.PageRequestID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(a => a.Screen).IsRequired().HasMaxLength(100);
Property(a => a.HttpMethod).IsRequired().HasMaxLength(10);
}
}
/// <summary>
/// Confirmation Log configuration class. Configures the confirmation log class for the db model
/// </summary>
public class ConfirmationLogConfiguration : EntityTypeConfiguration<ConfirmationLog>
{
/// <summary>
/// Default constructor
/// </summary>
public ConfirmationLogConfiguration()
{
//Map to Table
ToTable("ConfirmationLogs");
//Primary Key
HasKey(a => a.ConfirmationID);
//required fields
Property(a => a.ConfirmationID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(a => a.PageRequestID).IsRequired();
Property(a => a.ConfirmationNum).IsRequired().HasMaxLength(12);
Property(a => a.WebActionID).IsRequired();
}
}
I then create a rather large LINQ query based on this hierarchy. I'll spare that query because it's composed in about 10 steps, and I don't think that's the source of my problem. The problem is, when I run the query, the SQL generated for some reason thinks that the column AuditEventID (the primary key for the base class), exists on the ConfirmationLogs table (the grandchild table). The ConfirmationLogs table has a foreign key to it's parent table (PageRequests), which then has the foreign key to it's parent table (AuditEvents).
My question is, did I set this hierarchy up wrong? Does the "grandchild" table need the foreign key to both it's parent and grandparent for this to function? (if it does I find that unfortunate).
I'm positive that the inheritance relationship is throwing things off because if I don't make ConfirmationLogs a child of PageRequests and configure the relationship to PageRequests with HasRequired()/WithMany(), things work fine.
Any help would be appreciated.
Update
So, after further investigation I think there is a general problem with the way I'm trying to use inheritance. I should note that I'm trying to map code first to an existing database. In the database, I have my AuditEvent table, and a bunch of "child" tables like PageRequest. Page request has it's own primary key called PageRequestID, as well as a foreign key called AuditEventID. The other child tables are setup the same way. In my Configuration class for PageRequest (listed above), I'm trying to map this by using the HasKey function to say that the PageRequestID is the primary key, and assuming that EF will know about the foreign key AuditEventID by convention and inheritance. I should also note that I can write to the DB using the model just fine. If I want to write a PageRequest, I create PageRequest object, populate all the required fields as defined by both the PageRequest and AuditEvent base class, and save through the context. EF creates the AuditEvent record, and the pageRequest record with the FK back to AuditEvent.
What makes me think I'm not using inheritance right is that I allowed EF to create my database for me, using the model and mapping I've created. For the PageRequest table (and all other child tables), EF actually created a primary key called AuditEventID (even though my configuration is telling it to do otherwise). This key is also labeled as a foreign key, and the column that I want to create as a primary key (PageRequestID in this example) is just configured as being required (non-nullable). So it appears that EF taking the primary key from my BASE class and using that as a primary key AND foreign key in my child classes, almost like the concept of the AuditEventID is spread between the parent and child tables. Is there a way to change this behavior?
You are saying this didn't work, and it still expected an AuditRequestID in the table that had the ConfirmationLog object? I'm looking at the reference: Specifying Not to Map a CLR Property to a Column in the Database in http://msdn.microsoft.com/en-us/data/jj591617#1.6
public ConfirmationLogConfiguration()
{
//Map to Table
ToTable("ConfirmationLogs");
//Primary Key
HasKey(a => a.ConfirmationID);
//required fields
Property(a => a.ConfirmationID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(a => a.PageRequestID).IsRequired();
Property(a => a.ConfirmationNum).IsRequired().HasMaxLength(12);
Property(a => a.WebActionID).IsRequired();
Ignore(a => a.AuditEventID);
}
Good luck.

How to refresh data in Entity Framework 4.1 code first

I have the following context:
public class DataContext : DbContext
{
/// <summary>
/// Gets or sets Addresses.
/// </summary>
public DbSet<Address> Addresses { get; set; }
/// <summary>
/// Gets or sets Users.
/// </summary>
public DbSet<Users> Users { get; set; }
}
I my application user may change data in say user data and then he may want to cancel changes. The best way to do this, is to refresh the DataContext from the database. But DbContext has no Refresh method. How can I refresh my DataContext?
You can reload the entity from the database as follows.
context.Entry(user).Reload();
Or you can try out the methods described in this question.