User claim update not effected in ASP.NET Identity? - asp.net-web-api

i need to update the user claim in web api after the user logged in.
but after updating the user claim it will still return previous values.
bellow code used to update active user group after the user logged in.
/// <summary>
/// The class AppUser
/// </summary>
public class AppUser : ClaimsPrincipal
{
/// <summary>
/// Initializes a new instance of the <see cref="AppUser"/> class.
/// </summary>
/// <param name="principal">The principal.</param>
public AppUser(ClaimsPrincipal principal)
: base(principal)
{
}
/// <summary>
/// Gets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name
{
get
{
return this.FindFirst(ClaimTypes.Name).Value;
}
}
/// <summary>
/// Gets the name of the user.
/// </summary>
/// <value>
/// The name of the user.
/// </value>
public string UserName
{
get
{
return this.FindFirst("UserName").Value;
}
}
/// <summary>
/// Gets the active group.
/// </summary>
/// <value>
/// The active group.
/// </value>
public string ActiveGroup
{
get
{
return ((ClaimsIdentity)this.Identity).FindFirst("ActiveGroup").Value;
}
}
/// <summary>
/// Gets the email.
/// </summary>
/// <value>
/// The email.
/// </value>
public string Email
{
get
{
return this.FindFirst("Email").Value;
}
}
}
/// <summary>
/// The class BaseController
/// </summary>
public class BaseController : ApiController
{
/// <summary>
/// Gets the current user.
/// </summary>
/// <value>
/// The current user.
/// </value>
public AppUser CurrentUser
{
get
{
return new AppUser(this.User as ClaimsPrincipal);
}
}
}
public class AccountController : BaseController
{
[HttpPost]
[Route("UpdateUserGroup")]
public int UpdateUserGroup(string userGroup)
{
var user = User as ClaimsPrincipal;
var identity = user.Identity as ClaimsIdentity;
identity.RemoveClaim(identity.FindFirst("ActiveGroup"));
identity.AddClaim(new Claim("ActiveGroup", this.GetRoleNameByPresenter(userGroup)));
return 1;
}
}

The problem is that the claims are used in the authentication process and are part of the authentication token/cookie. If you want to remove a claim from the current user then you need to make sure the client get a new token/cookie.
If you're running for example bearer tokens with your api then you need to generate a new token and return that token to the client from your UpdateUserGroup(). The client then need to use the new token the next time it makes a request to the api.

Related

ASP.NET Core—access Configuration from static class

I want a simple static class that accesses the Configuration object. All the config info is already read in from the appsettings.json file in the Startup class. I just need an easy way to access it. Is this possible?
namespace MyNamespace
{
public static class Config
{
public string Username => Configuration["Username"];
public string Password => Configuration["Password"];
}
}
Anywhere else in the app:
string username = Config.Username;
string password = Config.Password;
A slightly shorter version based on the same principle as above...
public Startup(IConfiguration configuration)
{
Configuration = configuration;
StaticConfig = configuration;
}
public static IConfiguration StaticConfig { get; private set; }
To use in another static class:
string connString = Startup.StaticConfig.GetConnectionString("DefaultConnection");
create the ConfigurationHelper static class in the service layer, so it can be used in other layers without circular dependency.
public static class ConfigurationHelper
{
public static IConfiguration config;
public static void Initialize(IConfiguration Configuration)
{
config = Configuration;
}
}
initialize the ConfigurationHelper inside the ConfigureServices method in the Startup class.
ConfigurationHelper.Initialize(Configuration);
Use it wherever you want including your static classes
e.g: ConfigurationHelper.config.GetSection("AWS:Accesskey").Value;
After much research, this works (in ASPNetCore 2.2) for accessing the appsettings.json config from a static class but for some reason appsettings.development.json no longer loads properly but it might be something else in my project messing that up. The reloadOnChange does work. As a bonus it also has IHostingEnvironment and IHttpContextAccessor. While this works, I have recently decided to switch back to a more DI approach to follow the paradigm shift as others have mentioned.
So here is one of many ways to access some DI stuff (including the configuration) in a static class:
AppServicesHelper.cs:
public static class AppServicesHelper
{
static IServiceProvider services = null;
/// <summary>
/// Provides static access to the framework's services provider
/// </summary>
public static IServiceProvider Services
{
get { return services; }
set
{
if (services != null)
{
throw new Exception("Can't set once a value has already been set.");
}
services = value;
}
}
/// <summary>
/// Provides static access to the current HttpContext
/// </summary>
public static HttpContext HttpContext_Current
{
get
{
IHttpContextAccessor httpContextAccessor = services.GetService(typeof(IHttpContextAccessor)) as IHttpContextAccessor;
return httpContextAccessor?.HttpContext;
}
}
public static IHostingEnvironment HostingEnvironment
{
get
{
return services.GetService(typeof(IHostingEnvironment)) as IHostingEnvironment;
}
}
/// <summary>
/// Configuration settings from appsetting.json.
/// </summary>
public static MyAppSettings Config
{
get
{
//This works to get file changes.
var s = services.GetService(typeof(IOptionsMonitor<MyAppSettings>)) as IOptionsMonitor<MyAppSettings>;
MyAppSettings config = s.CurrentValue;
return config;
}
}
}
}
Startup.cs:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddHttpContextAccessor();//For HttpContext.
// Register the IOptions object
services.Configure<MyAppSettings>(Configuration.GetSection(nameof(MyAppSettings)));
//Explicitly register the settings object by delegating to the IOptions object so that it can be accessed globally via AppServicesHelper.
services.AddSingleton(resolver => resolver.GetRequiredService<IOptionsMonitor<MyAppSettings>>().CurrentValue);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...
AppServicesHelper.Services = app.ApplicationServices;
//...
}
Controller:
public class MyController: Controller
{
public MyController()
{
}
public MyAppSettings Config => AppServicesHelper.Config;
public async Task<IActionResult> doSomething()
{
testModel tm = await myService.GetModel(Config.Setting_1);
return View(tm);
}
}
Another class library:
public static class MyLibraryClass
{
public static string GetMySetting_ => AppServicesHelper.Config.Setting_1;
public static bool IsDev => AppServicesHelper.HostingEnvironment.IsDevelopment();
}
MyAppSettings.cs is any class that maps to a MyAppSettings section in appsettings.json:
public class MyAppSettings
{
public string Setting_1 {get;set;}
}
appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"MyAppSettings": {
"Setting_1": "something"
}
}
I've similar problems, and after reading the document from Microsoft. I've resolved it:
Create an static ApplicationSettings class
public static class ApplicationSettings
{
public const string User = "UserOptions";
public static UserOptions UserOptions { get; set; } = new UserOptions();
// other options here...
}
Create an options class
public class UserOptions
{
public string Username { get; set; };
public string Password { get; set; };
}
Init the Options class in Program.cs
ConfigurationManager configuration = builder.Configuration;
configuration.GetSection(ApplicationSettings.User).Bind(ApplicationSettings.UserOptions);
Update your appsettings.json
{
"UserOptions": {
"Username": "input_your_username_here",
"Password": "input_your_password_here"
}
}
You can call it anywhere in your project now
public void MethodA()
{
string username = ApplicationSettings.UserOptions.Username;
string password = ApplicationSettings.UserOptions.Password;
}
public static void MethodB()
{
string username = ApplicationSettings.UserOptions.Username;
string password = ApplicationSettings.UserOptions.Password;
}
Hope this could help you well.
I agree with mcbowes, it's in the docs, but the first example looks more like what you need...want:
public class Program
{
public static IConfigurationRoot Configuration { get; set; }
public static void Main(string[] args = null)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
Console.WriteLine($"option1 = {Configuration["option1"]}");
// Edit:
IServiceCollection services = new ServiceCollection();
services.AddOptions();
services.Configure<HelloWorldOptions>(_configuration.GetSection("HelloWorld"));
// And so on...
}
}
Try avoid using a static class and use DI
namespace MyNamespace {
public interface IConfig {
string Username { get; }
string Password { get; }
}
public class Config : IConfig {
public Config(IConfiguration configuration) {
_configuration = configuration;
}
readonly IConfiguration _configuration;
public string Username => _configuration["Username"];
public string Password => _configuration["Password"];
}
}
The setup DI in StartUp class
public class Startup {
public void ConfigureServices(IServiceCollection services) {
//...
services.AddTransient<IConfig, Config>();
...
}
}
And use it like so
public class TestUsage {
public TestUsage(IConfig config) {
_config = config;
}
readonly IConfig _config;
public string Username => _config.Username;
public string Password => _config.Password;
}
You can use Signleton pattern to access your configurations from anywhere
public class ConnectionStrings
{
private ConnectionStrings()
{
}
// property with getter only will not work.
public static ConnectionStrings Instance { get; protected set; } = new ConnectionStrings();
public string DatabaseConnection { get; set; }
}
and in your startup class
public class Startup
{
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
configuration.GetSection("ConnectionStrings").Bind(ConnectionStrings.Instance);
}
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app)
{
}
}
This has already been said but I'm going to say it.
I believe .Net Core wants developers to get values through Dependency Inject. This is what I've noticed from my research but I am also speculating a bit. As developers, we need to follow this paradigm shift in order to use .Net Core well.
The Options Pattern is a good alternative to the static config. In your case, it'll look like this:
appsettings.json
{
"Username": "MyUsername",
"Password": "Password1234"
}
SystemUser.cs
public class SystemUser
{
public string Username { get; set; } = "";
public string Password { get; set; } = "";
}
Startup.cs
services.Configure<SystemUser>(Configuration);
And to use the SystemUser class, we do the following.
TestController.cs
public class TestController : Controller
{
private readonly SystemUser systemUser;
public TestController(IOptionsMonitor<SystemUser> systemUserOptions)
{
this.systemUser = systemUserOptions.CurrentValue;
}
public void SomeMethod()
{
var username = this.systemUser.Username; // "MyUsername"
var password = this.systemUser.Password; // "Password1234"
}
}
Even though we are not using a static class, I think this is the best alternative that fits your needs. Otherwise, you might have to use a static property inside the Startup class which is a scary solution imo.
Personally I like the method used in this link
Essentially it just adding a static field to your options class.
public class WeblogConfiguration
{
public static WeblogConfiguration Current;
public WeblogConfiguration()
{
Current = this;
}
}
Then in any static class you can do:
WeblogConfiguration.Current
Simple and very straight forward
If you are using environment variables as your configuration, you can access the environment variable directly rather than via the configuration object.
using System;
namespace My.Example
{
public static class GetPaths
{
private static readonly string MyPATH =
Environment.GetEnvironmentVariable("PATH");
private static readonly string MySpecialPath =
Environment.GetEnvironmentVariable("PREFIX_SpecialPath");
...
}
}
I think you could use extension function, something like this
public static string ConfigToSomeThing(this IConfiguration config, int value)
{
return config[value.ToString()] ?? "";
}
Then any place , just injection IConfiguration and use extension method
_systemConfiguration.ConfigToSomeThing(123);
I just created below class:
/// <summary>
///
/// </summary>
public static class ConfigurationManager
{
/// <summary>
///
/// </summary>
public sealed class ConfigurationManagerAppSettings
{
/// <summary>
///
/// </summary>
internal ConfigurationManagerAppSettings() { }
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string this[string key] => (TheConfiguration ?? throw new Exception("Set ConfigurationManager.TheConfiguration in Startup.cs")).GetSection($"AppSettings:{key}").Value;
}
/// <summary>
///
/// </summary>
public static IConfiguration? TheConfiguration { get; set; }
/// <summary>
///
/// </summary>
public static readonly ConfigurationManagerAppSettings AppSettings = new ConfigurationManagerAppSettings();
}
and below is my code:
public class Startup
{
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
ConfigurationManager.TheConfiguration = Configuration;
I've used this approach and it seems to work well. In your static class, add a public IConfiguration property.
namespace MyNamespace
{
public static class Config
{
public static IConfiguration Configuration { get; set; }
public string Username => Configuration["Username"];
public string Password => Configuration["Password"];
}
}
In Startup.cs constructor, use dependency injection to get the configuration, and then assign it to the public member of your static class.
public class Startup
{
public Startup(IConfiguration configuration)
{
MyNamespace.Config.Configuration = configuration;
}
// The rest of the startup code...
}
I was getting same issue and I wanted to access app setting in static class so I put this solution. Write this below code in static class in my code class name was EncryptionUtility
private static string _saltKey = string.Empty;
private static IConfiguration configuration;
public static void AppSettingsConfigure(IConfiguration _config)
{
configuration = _config;
_saltKey = Convert.ToString(configuration["Security:EncyptPassword"]);
}
Call Appsettingconfiure from the program.cs file. Here, EncryptionUtility is static class or you can only write static method as your requirement. I had put this line above app.Run();
EncryptionUtility.AppSettingsConfigure(app.Services.GetRequiredService<IConfiguration>());
appsettings.json :
{
"Security": {
"EncyptPassword": "Password"
}
}
Here is a way to obtain the configuration values from a NET.Core page without having to reference these statically but then still being able to pass them to other static functions called from the non-static class.
At the top of your non-static class add this:
private readonly IConfiguration _configuration;
Then in the constructor function bring in the existing configuration as input to the function:
IConfiguration configuration
Then assign the configuration to your read only variable inside the constructor function:
_configuration = configuration;
Here is an example of what it should look like:
public class IndexModel : PageModel
{
private readonly IConfiguration _configuration;
public IndexModel(IConfiguration configuration)
{
_configuration = configuration;
}
}
After this you can reference the configuration in any function in the class by referencing _configuration and can even then pass this on to other static functions that you call from other classes:
public async Task OnGetAsync()
{
AnotherClass.SomeFunction(_configuration);
}
Then in the called static class I can make use of the configuration values:
public static string SomeFunction(IConfiguration configuration)
{
string SomeValue = configuration.GetSection("SomeSectionOfConfig")["SomeValue"];
}
I have a class that calls some stored procedures for viewing and amending data and passes parameter values from appsettings.json using this approach.
Consider using the instructions here for ASP.NET Core Configuration.
You can create a class to store your configuration settings and then access the values, something like this:
_config.UserName
In Startup - ConfigureServices:
services.Configure<Config>(Configuration.GetSections("General"));
Then just inject your object wherever you need as:
IOptions<Config> config
The IConfiguration is Injectable anywhere within the Project. But in the case of static class, the option I am using and maybe only approach...
var Configuration = new ConfigurationBuilder()
.AddUserSecrets<Startup>()
.Build();
And, you can add required section, such in this code block above, I added 'UserSecrets'.

Random "Missing type map configuration or unsupported mapping." Error in Automapper

Please see this post for the solution.
Ok, I finally figured it out. The:
AppDomain.CurrentDomain.GetAssemblies()
piece of my code sometimes does not get my mapping assembly so while it is missing I get an error. Replacing this code by forcing the app to find all assemblies solved my problem.
My Entity:
/// <summary>
/// Get/Set the name of the Country
/// </summary>
public string CountryName { get; set; }
/// <summary>
/// Get/Set the international code of the Country
/// </summary>
public string CountryCode { get; set; }
/// <summary>
/// Get/Set the coordinate of the Country
/// </summary>
public Coordinate CountryCoordinate { get; set; }
/// <summary>
/// Get/Set the cities of the country
/// </summary>
public virtual ICollection<City> Cities
{
get
{
if (_cities == null)
{
_cities = new HashSet<City>();
}
return _cities;
}
private set
{
_cities = new HashSet<City>(value);
}
}
My DTO:
public Guid Id { get; set; }
public string CountryName { get; set; }
public string CountryCode { get; set; }
public string Lattitude { get; set; }
public string Longtitude { get; set; }
public List<CityDTO> Cities { get; set; }
My Configuration
// Country => CountryDTO
var countryMappingExpression = Mapper.CreateMap<Country, CountryDTO>();
countryMappingExpression.ForMember(dto => dto.Lattitude, mc => mc.MapFrom(e => e.CountryCoordinate.Lattitude));
countryMappingExpression.ForMember(dto => dto.Longtitude, mc => mc.MapFrom(e => e.CountryCoordinate.Longtitude));
In Global.asax Application_Start I have:
Bootstrapper.Initialise();
And in Bootstrapper I have:
public static class Bootstrapper
{
private static IUnityContainer _container;
public static IUnityContainer Current
{
get
{
return _container;
}
}
public static void Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
private static IUnityContainer BuildUnityContainer()
{
_container = new UnityContainer();
_container.RegisterType(typeof(BoundedContextUnitOfWork), new PerResolveLifetimeManager());
_container.RegisterType<ICountryRepository, CountryRepository>();
_container.RegisterType<ITypeAdapterFactory, AutomapperTypeAdapterFactory>(new ContainerControlledLifetimeManager());
_container.RegisterType<ICountryAppService, CountryAppServices>();
EntityValidatorFactory.SetCurrent(new DataAnnotationsEntityValidatorFactory());
var typeAdapterFactory = _container.Resolve<ITypeAdapterFactory>();
TypeAdapterFactory.SetAdapter(typeAdapterFactory);
return _container;
}
}
Where my adapter is:
public class AutomapperTypeAdapter : ITypeAdapter
{
public TTarget Adapt<TSource, TTarget>(TSource source)
where TSource : class
where TTarget : class, new()
{
return Mapper.Map<TSource, TTarget>(source);
}
public TTarget Adapt<TTarget>(object source) where TTarget : class, new()
{
return Mapper.Map<TTarget>(source);
}
}
And AdapterFactory is:
public AutomapperTypeAdapterFactory()
{
//Scan all assemblies to find an Auto Mapper Profile
var profiles = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => t.BaseType == typeof(Profile));
Mapper.Initialize(cfg =>
{
foreach (var item in profiles)
{
if (item.FullName != "AutoMapper.SelfProfiler`2")
cfg.AddProfile(Activator.CreateInstance(item) as Profile);
}
});
}
So I randomly get a "Missing type map configuration or unsupported mapping." error pointing:
public TTarget Adapt<TTarget>(object source) where TTarget : class, new()
{
return Mapper.Map<TTarget>(source);
}
While this error occurs randomly it is hard to debug and see what happens. I have searched a lot with no proper solution.
The error goes like:
Missing type map configuration or unsupported mapping.
Mapping types: Country -> CountryDTO
MyApp.Domain.BoundedContext.Country ->
MyApp.Application.BoundedContext.CountryDTO
Destination path: List`1[0]
Source value: MyApp.Domain.BoundedContext.Country
My project is an MVC 3 project with Automapper 2.2 and Unity IoC..
I will appreciate any idea, advice or solution and thanks for your answers.
If you use Mapper.AssertConfigurationIsValid(); you would get bit more detailed info:
Unmapped members were found. Review the types and members below. Add a
custom mapping expression, ignore, add a custom resolver, or modify
the source/destination type
In any case, you have to have mapped all properties of destination model. You were missing CityDTO and Id. Here:
Mapper.CreateMap<City, CityDTO>();
Mapper.CreateMap<Country, CountryDTO>()
.ForMember(dto => dto.Id, options => options.Ignore())
.ForMember(dto => dto.Longtitude, mc => mc.MapFrom(e => e.CountryCoordinate.Longtitude))
.ForMember(dto => dto.Lattitude, mc => mc.MapFrom(e => e.CountryCoordinate.Lattitude));
Maybe you would need some additional mapping on City-CityDTO, as you did not specify them.
For me this error had to do with where I put my CreateMap<>() call. I had put it in the static initializer for my DTO. When I moved the CreateMap<>() call to somewhere less cute, everything worked fine.

Ninject UnitOfWork confusion

I use Ninject all the time with my MVC 3 applications, but I'm trying to change the Pattern for my Data Objects to use UnitOfWork and I'm having trouble figuring out how to get Ninject to handle this properly.
I know my implementation of classes work when they are constructed manually like this in my console application:
IDatabaseFactory factory = new DatabaseFactory();
IUnitOfWork worker = new UnitOfWork(factory);
IBlogCategoryDao dao = new BlogCategoryDao(factory);
IBlogCategoryService service = new BlogCategoryService(dao);
BlogCategory category = service.GetById(id);
try
{
if (category != null)
{
service.Delete(category);
worker.Commit();
Console.WriteLine("Category deleted successfully!");
}
else
{
Console.WriteLine("Entity doesn't exist.");
}
}
catch (Exception ex)
{
Console.WriteLine("Error deleting category: {0}", ex.Message);
}
In my MVC 3 application I'm using the Ninject.MVC3 NuGet package, and this is in the RegisterServices method.
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IDatabaseFactory>().To<DatabaseFactory>();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind<IBlogCategoryDao>().To<BlogCategoryDao>();
kernel.Bind<IBlogDao>().To<BlogDao>();
kernel.Bind<IBlogCategoryService>().To<BlogCategoryService>();
kernel.Bind<IBlogService>().To<BlogService>();
}
While this works for the most part, Get requests, all POST requests (Insert, Update, Delete) don't get executed. There is no exception thrown and when I step through it, it goes through the SaveChanges() method without a problem and returns back up the stack, but nothing is executed. So I know I must be missing something with my Ninject configuration.
Here's my Unit of Work class.
public class UnitOfWork : IUnitOfWork
{
private Database _database; <-- DbContext derived class
private readonly IDatabaseFactory _databaseFactory;
public UnitOfWork(IDatabaseFactory databaseFactory)
{
this._databaseFactory = databaseFactory;
}
public Database Database
{
get
{
return _database ?? (_database = _databaseFactory.Get());
}
}
public void Commit()
{
Database.Commit();
}
}
Here's the DatabaseFactory class:
public class DatabaseFactory : Disposable, IDatabaseFactory
{
private Database _database;
public DatabaseFactory()
{
}
public virtual Database Get()
{
if (_database == null)
{
_database = DataObjectFactory.CreateContext();
}
return _database;
}
protected override void DisposeCore()
{
if (_database != null)
{
_database.Dispose();
}
}
}
And my DataObjectFactory class:
public static class DataObjectFactory
{
private static readonly string _connectionString;
/// <summary>
/// Static constructor. Reads the connectionstring from web.config just once.
/// </summary>
static DataObjectFactory()
{
string connectionStringName = ConfigurationManager.AppSettings.Get("ConnectionStringName");
_connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
}
/// <summary>
/// Creates the Context using the current connectionstring.
/// </summary>
/// <remarks>
/// Gof pattern: Factory method.
/// </remarks>
/// <returns>Action Entities context.</returns>
public static Database CreateContext()
{
return new Database(_connectionString);
}
}
This is a similar pattern as used in the EFMVC CodePlex application, but I don't use AutoFac.
Any thoughts on this are appreciated.
Thanks.
I just do this:
kernel.Bind<IUnitOfWork>.To<EFUnitOfWork>().InRequestScope();
EFUnitOfWork.cs
public class EFUnitOfWork : DbContext, IUnitOfWork
{
// your normal DbContext plus your IUnitOfWork members that delegate to EF context
}
Since EF already implements a form of Unit Of Work, this allows you to use a more generic interface for it, and inject it easily.
Also, you can implement the EF constructors for connection strings and just pass them to base constructors. Then you can use the Ninject .WithConstructorArgument() to configure the connection string using your AppSettings code.

DBContext.Set<T> does not contain definition of Set<T>

I am trying to implement an abstract repository pattern as described in THIS post. I'm getting the error message
'C' does not contain a definition for 'Set' and no extension method
'Set' accepting a first argument of type 'C' could be found (are you
missing a using directive or an assembly reference?)
where C is the DBContext
namespace Rental.Data.Entity.Repository
{
public abstract class GenericRepo<C, T> :
IGenericRepo<T> where T : class where C : RentalContainer, new()
{
private C _DBContext = new C();
protected C DBContext
{
get { return _DBContext; }
set { _DBContext = value; }
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = _DBContext.Set<T>(); <-- here is gives the error
return query;
}
yet another update
public partial class RentalContainer : ObjectContext
{
#region Constructors
/// <summary>
/// Initializes a new RentalContainer object using the connection string found in the 'RentalContainer' section of the application configuration file.
/// </summary>
public RentalContainer() : base("name=RentalContainer", "RentalContainer")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new RentalContainer object.
/// </summary>
public RentalContainer(string connectionString) : base(connectionString, "RentalContainer")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
ObjectContext does not have a Set method. It has CreateObjectSet method
public abstract class GenericRepo<C, T> : IGenericRepo<T>
where T : class
where C : RentalContainer, new()
{
private C _DBContext = new C();
protected C DBContext
{
get { return _DBContext; }
set { _DBContext = value; }
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = _DBContext.CreateObjectSet<T>();
return query;
}
}
Add reference to EntityFramework.dll
http://msdn.microsoft.com/en-us/library/gg679544(v=vs.103).aspx
Press ctrl + . and use Microsoft.EntityFrameworkCore; I had to add. But I got this error when I accidentally created an empty class named DbContext.
The solution for me is to delete the empty DbContext class and add the correct using line to DbContext. (using Microsoft.EntityFrameworkCore;)
Make sure your DataContext class extends DBContext

How to persist an Edit in MVC3?

public ActionResult Edit(int id)
{
var productBrand = brandRepo.FindProductBrand(id);
ProductBrandModel model = Mapper.Map<ProductBrand, ProductBrandModel>(productBrand);
return View(model);
}
[HttpPost]
public ActionResult Edit(ProductBrandModel model)
{
if (ModelState.IsValid)
{
var productBrand = brandRepo.FindProductBrand(model.BrandId);
productBrand.Name = model.Name;
//How to persist that information?
}
}
I have a EF generate class ProductBrand and a model for views called ProductBrandModel.
How would I persist the information of an edit using Entity Framework? Should my brandRepo have a void method called SaveChanges where in it I would go:
public void SaveChanges()
{
dbEntities.SaveChanges();
}
As you correctly assume, you have to commit your changes to the database using the .SaveChanges() method. In your case, brandRepo.SaveChanges() would delegate to dbEntities.SaveChanges().
As a side note: In simple cases a separate repository class only introduces complexity without really providing any benefit. Entity Framework's DbContext pretty much resembles a simple repository itself, so you don't need one on top.
Of course, for the sake of testability an indirection layer might make sense.
Without the repository your code could look somewhat like this:
public ActionResult Edit(int id)
{
var productBrand = dbEntities.ProductBrands.Find(x => x.BrandId = id);
ProductBrandModel model = Mapper.Map<ProductBrand, ProductBrandModel>(productBrand);
return View(model);
}
[HttpPost]
public ActionResult Edit(ProductBrandModel model)
{
if (ModelState.IsValid)
{
var productBrand = dbEntities.ProductBrands.Find(x => x.BrandId = id);
// or something similar, I don't know the inner workings of your
// brandRepo.FindProductBrand(id)
productBrand.Name = model.Name;
dbEntities.SaveChanges();
}
}
I like to have a save method in my repository in conjunction with an entity framework helper method I got from the net. The SaveCustomer is my repository class method and below it is the helper class. In your case you would pass your model into
brandRepository.SaveProdctBrand(productBrand)
(helps to spell out the names for good naming conventions and fxcop rules)
public void SaveCustomer(Customer customer)
{
using (var ctx = new WebStoreEntities())
{
if (customer.CustomerId > 0)
{
//It's an existing record, update it.
ctx.Customers.AttachAsModified(customer);
ctx.SaveChanges();
}
else
{
//its a new record.
ctx.Customers.AddObject(customer);
ctx.SaveChanges();
}
}
}
The helper class is as follows
public static class EntityFrameworkExtensions
{
/// <summary>
/// This class allows you to attach an entity.
/// For instance, a controller method Edit(Customer customer)
/// using ctx.AttachAsModified(customer);
/// ctx.SaveChanges();
/// allows you to easily reattach this item for udpating.
/// Credit goes to: http://geekswithblogs.net/michelotti/archive/2009/11/27/attaching-modified-entities-in-ef-4.aspx
/// </summary>
public static void AttachAsModified<T>(this ObjectSet<T> objectSet, T entity) where T : class
{
objectSet.Attach(entity);
objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
}
/// <summary>
/// This marks an item for deletion, but does not currently mark child objects (relationships).
/// For those cases you must query the object, include the relationships, and then delete.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objectSet"></param>
/// <param name="entity"></param>
public static void AttachAsDeleted<T>(this ObjectSet<T> objectSet, T entity) where T : class
{
objectSet.Attach(entity);
objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Deleted);
}
public static void AttachAllAsModified<T>(this ObjectSet<T> objectSet, IEnumerable<T> entities) where T : class
{
foreach (var item in entities)
{
objectSet.Attach(item);
objectSet.Context.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
}
}
}

Resources