I have a c# project with EF and repository pattern.
For single database everything work's fine but I have different model which are related to different database like User model data are come from control panel database and other model are also come from different database.
Here I use common repository pattern for the project. Now how can I send the database connection string to the repository when I initialize the model?
Here is my repository pattern :
public class Repository<C,T> : IRepository<T> where T : class where C : DbContext, new()
{
private C _context = new C();
public C context
{
get
{
//if (_context == null)
//{
// _context = new C();
// _context.Database.Connection.ConnectionString = "the new connectionstring";
//}
//return dataContext;
return _context;
}
set { _context = value; }
}
private IDbSet<T> _entities;
private IDbSet<T> Entities
{
get
{
if (_entities == null)
_entities = context.Set<T>();
return _entities;
}
}
And this is my Service class
public class UserService: Repository<DyeingContext,User> , IUserRepository
{
public UserService()
{
DyeingContext d = new DyeingContext(DataBase.ControlPanal);
}
//private readonly IRepository<User> _useRepository = new Repository<User>(new DyeingContext(DataBase.ControlPanal));
}``
and here is my context class
public partial class DyeingContext:DbContext
{
public DyeingContext(string pDbName):base(GetTheContext(pDbName))
{
}
public DyeingContext():base()
{
//throw new NotImplementedException();
}
public static string GetTheContext(string pDbName)
{
return ConnectionSettings.GetConnectionStringByDbName(pDbName);
}
}
I cant get the connection string in here
DyeingContext d = new DyeingContext(DataBase.ControlPanal);
it says
database connection through exception of type system.argumentexception
Is there a way to pass the multiple connections strings to the repository?
How and where I should initialize my connection string and how I pass it through rerpository?
I am not sure if you received your answer from the above comments. But here is how i would implement that.
1- Create a dbContext concrete class for each database. Both inheriting from EF DbContext.
2- In config file, create a connection string key for each database. This key is then supplied to each dbContext class as ctr param. As follows:
public DB1Context()
: base("db1") // connection string key
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
public DB2Context()
: base("db2") // connection string key
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
3- Each database must have its own repositories. For instance, AccountRepository inherits from the base repository. Its db context is supplied on contructor init as follows.
public class AccountRepository : DataRepositoryBase<Account>, IAccountRepository
{
private DB1Context db1Context = new DB1Context();
public AccountRepository()
{
this.DBContext = db1Context;
}
}
This way, you can communicate with multiple databases through its repositories. The same engine / service can inject repositories from different databases.
Related
I am working on adding Entity Framework to our web app, asp.net MVC 5, but I am having a hardtime saving changes and adding to the database. I set up UnitOfWork with a generic BaseRepository, and I have tried a few things attempting to get this to work. first, I thought I could inject, with AutoFac, my repo in UnitOfWork like so
public UnitOfWork(IServiceItem serviceItem
, ITechServiceItem techServiceItem
, ITechnicianTime technicianTime
, ISproc sproc
, IRepairOrder repairOrder
, ICustomer customer
, IRepairOrderStatus repairOrderStatus
, IRepairOrderUnit repairOrderUnit
, IFiles files
, IPartInventory partInventory
, IRepairOrderItems repairOrderItems
)
{
RepairOrderItems = repairOrderItems;
PartInventory = partInventory;
Files = files;
RepairOrderUnit = repairOrderUnit;
RepairOrderStatus = repairOrderStatus;
RepairOrder = repairOrder;
Customer = customer;
Sproc = sproc;
ServiceItem = serviceItem;
TechServiceItem = techServiceItem;
TechnicianTime = technicianTime;
}
and my BaseRepo is like
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DataDbContext _db;
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DataDbContext _db;
internal void GetData()
{
if (_db == null)
{
string accountNumber = HttpContext.Current.User.Identity.GetCompanyAccountNumber();
var connectionToken = ConfigurationManager.AppSettings["LoginSplitToken"];
_db = new DataDbContext(ConfigurationManager.ConnectionStrings["NameOfConnString"].ConnectionString.Replace(connectionToken, accountNumber));
}
}
public TEntity Get(int id)
{
return _db.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return _db.Set<TEntity>().ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _db.Set<TEntity>().Where(predicate);
}
public void Add(TEntity entity)
{
_db.Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
_db.Set<TEntity>().AddRange(entities);
}
public void Remove(TEntity entity)
{
_db.Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
_db.Set<TEntity>().RemoveRange(entities);
}
public int CompleteData()
{
return _db.SaveChanges();
}
public TEntity Get(int id)
{
return _db.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return _db.Set<TEntity>().ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _db.Set<TEntity>().Where(predicate);
}
public void Add(TEntity entity)
{
_db.Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
_db.Set<TEntity>().AddRange(entities);
}
public void Remove(TEntity entity)
{
_db.Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
_db.Set<TEntity>().RemoveRange(entities);
}
public int CompleteData()
{
return _db.SaveChanges();
}
}
and my StartUp.Configuration
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
HttpConfiguration config = GlobalConfiguration.Configuration;
// REGISTER DEPENDENCIES
builder.RegisterType<EverLogicDbContext>().AsSelf().InstancePerRequest();
builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest();
builder.RegisterType<ApplicationSignInManager>().AsSelf().InstancePerRequest();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).InstancePerRequest();
builder.Register(c => HttpContext.Current.User).InstancePerRequest();
builder.Register(c => app.GetDataProtectionProvider()).InstancePerRequest();
builder.RegisterType<ApplicationUserStore>().As<IUserStore<EverLogicMamber, int>>()
.WithParameter(new TypedParameter(typeof(ISecurityOfWork), new SecurityOfWork(new SecurityDbContext())))
.InstancePerRequest();
//Database
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
builder.RegisterType<SecurityOfWork>().As<ISecurityOfWork>().InstancePerRequest();
//Service
builder.RegisterType<TechnicianTimeService>().As<ITechnicianTimeService>().InstancePerRequest();
builder.RegisterType<PartService>().As<IPartService>().InstancePerRequest();
builder.RegisterType<TechServiceItemService>().As<ITechServiceItemService>().InstancePerRequest();
//Repo
builder.RegisterType<Company>().As<ICompany>().InstancePerRequest();
builder.RegisterType<Views>().As<IViews>().InstancePerRequest();
builder.RegisterType<RepairOrderItems>().As<IRepairOrderItems>().InstancePerRequest();
builder.RegisterType<PartInventory>().As<IPartInventory>().InstancePerRequest();
builder.RegisterType<Files>().As<IFiles>().InstancePerRequest();
builder.RegisterType<TechDashboardService>().As<ITechDashboardService>().InstancePerRequest();
builder.RegisterType<RepairOrderUnit>().As<IRepairOrderUnit>().InstancePerRequest();
builder.RegisterType<RepairOrderStatus>().As<IRepairOrderStatus>().InstancePerRequest();
builder.RegisterType<Customer>().As<ICustomer>().InstancePerRequest();
builder.RegisterType<ServiceItem>().As<IServiceItem>().InstancePerRequest();
builder.RegisterType<RepairOrder>().As<IRepairOrder>().InstancePerRequest();
builder.RegisterType<Sproc>().As<ISproc>().InstancePerRequest();
builder.RegisterType<TechServiceItem>().As<ITechServiceItem>().InstancePerRequest();
builder.RegisterType<TechnicianTime>().As<ITechnicianTime>().InstancePerRequest();
// REGISTER CONTROLLERS SO DEPENDENCIES ARE CONSTRUCTOR INJECTED
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(config);
builder.RegisterWebApiModelBinderProvider();
var container = builder.Build();
// REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
ConfigureAuth(app);
}
But with this set up, the database does not update or add new entitys.
Then i tryed removing Dependcy injection from UnitOfWork and set UnitOfWork up like
protected DataDbContext _db;
public UnitOfWork(DataDbContext context)
{
GetData();
RepairOrderItems = new RepairOrderItems(_db);
PartInventory = new PartInventory(_db);
Files = new Files(_db);
RepairOrderUnit = new RepairOrderUnit(_db);
RepairOrderStatus = new RepairOrderStatus(_db);
RepairOrder = new RepairOrder(_db);
Customer = new Customer(_db);
Sproc = new Sproc(_db);
ServiceItem = new ServiceItem(_db);
TechServiceItem = new TechServiceItem(_db);
TechnicianTime = new TechnicianTime(_db);
}
internal void GetData()
{
if (_db == null)
{
string accountNumber = HttpContext.Current.User.Identity.GetCompanyAccountNumber();
var connectionToken = ConfigurationManager.AppSettings["LoginSplitToken"];
_db = new DataDbContext(ConfigurationManager.ConnectionStrings["NameOfConnString"].ConnectionString.Replace(connectionToken, accountNumber));
}
}
and moving SaveChanges from the BaseRepo to UnitOfWork, but still nothing is saving or adding to the database.
What am i missing????
TL;DR the problem is that all your repositories are using separate, independent DbContexts, so the DbContext injected into your UnitOfWork has no pending changes when you call SaveChanges on it, so that's why you aren't seeing any change to the database.
In order for the Unit of Work to function correctly, your UnitOfWork class, and all the repository classes which your code needs to perform data persistence, must all share the same DbContext instance. In your code, it's clear that each repository has a factory method to create it's own, independent DbContext instance.
Remove the GetData() factory method from your BaseRepository class, and instead, require an instance of your EverLogicDbContext instance to injected to the constructor of BaseRepository by AutoFac. This will require that all your Repository subclasses also need to have a constructor accepting this same EverLogicDbContext.
As per your last edit, the UnitOfWork class must accept the same, shared EverLogicDbContext that the repositories use. Since you've tagged with asp.net-mvc then RequestPerInstance lifetime scope is correct for your scenario.
Your UnitOfWork class needs to control the SaveChanges(Async) method, so remove the CompleteData method from the BaseRepository class.
As you already seem to have done, the DbContext needs to be registered InstancePerRequest:
builder.RegisterType<EverLogicDbContext>().AsSelf().InstancePerRequest();
If all this is tied together correctly:
AutoFac will create an instance of your concrete DbContext the first time it is needed during processing of each Request.
All Repositories will then share the same DbContext instance for the lifetime of the Request, and the DbContext will track interim changes made by your services.
The UnitOfWork injected into your main "business logic" (e.g. Controller, or Orchestrator / Handler) will then be able to Commit the actions taken by simply calling SaveChangesAsync on the shared DbContext. This will all happen under a single database connection, so will be a lightweight transaction.
As per other comments above, IMO Entity Framework is a already high level framework with transactional support built-in, so there's little point in over-engineering a "UnitOfWork" pattern if all the ACID activity will be conducted against the same Database (and can be wrapped into the same DbContext).
I have a unit of work with the repository pattern with simple injector implemented and I need to change my connection string dynamically. Currently the connection string is taken from the web config. I need the connection string be taken from the database.
So I will have a database with the ASP.Net Identity and the connections strings (and other configurations needed for my application) and then a database depending on the client.
My repositories and Unit of work are as follows.
public abstract class DataRepositoryBase<TEntity, TContext> : IDataRepository<TEntity>
where TEntity : class, IObjectStateEntity, new()
where TContext : class, IDbSimpleContextAsync
{
protected DataRepositoryBase(TContext context)
{
Context = context;
}
public virtual TContext Context { get; }
public IEnumerable<TEntity> Get()
{
return Context.Get<TEntity>();
}
public TEntity Get(object id)
{
return Context.Find<TEntity>(id);
}
}
public class SomeRepository : DataRepositoryBase<SomeObject, IContext>, ISomeRepository
{
public SomeRepository (IContext context) : base(context)
{
}
}
public abstract class UnitOfWorkBase : IUnitOfWork
{
private IDbSimpleContextAsync _dbContext;
protected UnitOfWorkBase(IDbSimpleContextAsync dbContext)
{
_dbContext = dbContext;
}
public int SaveChanges()
{
return _dbContext.SaveChanges();
}
public Task<int> SaveChangesAsync()
{
return _dbContext.SaveChangesAsync();
}
}
public class UnitOfWork : UnitOfWorkBase, IUnitOfWork
{
private ISomeRepository _someRepository
private readonly IContext _dbContext;
public UnitOfWork(IContext dbContext) : base(dbContext)
{
_dbContext = dbContext;
}
public ISomeRepository SomeRepository => _someRepository ?? (_someRepository = new SomeRepository(_dbContext));
}
public class BookingBusiness : IBookingBusiness
{
protected IAllotmentUnitOfWork UnitOfWork { get; }
public AllotmentBusinessBase(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
...
business methods here
...
}
So my idea is when reaching business, I query the configuration database for the connection string for the current user (the current unit of work injected points to that database), and somehow use that connection to instantiate a new unit of work for to connect to the correct database. Any ideas how i can achieve this using my current setup?
You should prevent injecting objects into the object graph that change based on runtime information. The question here is whether or not the connection string is still a constant value (won't change after the application started), or can change from request to request (for instance, when each user gets its own connection string).
In case the connection string is a constant, the solution is simple: Just request the connection string at start-up and use it indefinitely, just as you already are doing currently.
If your connection string isn't a constant value from the config file, but runtime information, it and its consuming DbContext should not be injected anymore directly into the object graph. Instead, you should define an abstraction that allows requesting the correct DbContext based on runtime information, such as logged in user.
So instead of injecting an IContext into SomeRepository and UnitOfWork, inject an IContextProvider, which can be defined as follows:
public interface IContextProvider
{
IContext Context { get; }
}
Your DataRepositoryBase can use IContextProvider as follows:
public IEnumerable<TEntity> Get()
{
return this.contextProvider.Context.Get<TEntity>();
}
public TEntity Get(object id)
{
return this.contextProvider.Context.Find<TEntity>(id);
}
The part left is to define an implementation for IContextProvider that can load the right connection string from the database, and create and cache a DbContext based on that connection string. Considering the limited amount of information given, this is only something you will know how to do.
I'm developing multitenant application. I use separate databases for each tenant. UserData are for each tenant in separate database TOO.
My problem is how can i create admin account for each tenant in "custom" database independently on DI. In MVC 5 was possible to instantiate UserManager base on UserStore(connection string). But UserManager in mvc6 depends on HttpContext... No documentation found...
Exist please some way how to do it??? I need in mvc 6 something like this in mvc 5:
UserStore<TenantUser> store = new UserStore<TenantUser>(new TenantDbContext("CONNECTION STRING")); //!!! NO POSSIBLE CREATE USER IN CUSTOM DATABASE
UserManager<TenantUser> t = new UserManager<TenantUser>(store);
t.CreateAsync(user, password);
Update:
public class TenantDbContext : IdentityDbContext<TenantUser, TenantRole, Guid>
{
private string _connectionString { get; set; }
private readonly IHttpContextAccessor _contextAccessor;
private readonly ApplicationDbContext _applicationDbContext;
//THIS SUB UNCOMENT ONLY IF CREATE MIGRATIONS (dnx ef...)
/*
public TenantDbContext(DbContextOptions<TenantDbContext> options) : base(options)
{
this._connectionString = "CONNECTION STRING";
}
*/
public TenantDbContext(DbContextOptions<TenantDbContext> options, IHttpContextAccessor contextAccessor, ApplicationDbContext applicationDbContext) : base(options) {
_contextAccessor = contextAccessor;
_applicationDbContext = applicationDbContext;
TenantResolver resolver = new TenantResolver(_contextAccessor, _applicationDbContext);
string con = resolver.GetConnectionString();
if (con != string.Empty)
{
this._connectionString = con; }
else
{
this._connectionString = "CONNECTION STRING"; //Development connection string
}
}
public TenantDbContext() //Posibility to create TenantDbContext migration and development database with no connectionString in constructor
{
//this._connectionString = "CONNECTION STRING";
}
public TenantDbContext(string ConnectionString)
{
this._connectionString = ConnectionString;
}
public static TenantDbContext Create(string ConnectionString)
{
return new TenantDbContext(ConnectionString);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString);
}
I hope that I correctly understand you. Let us we forget about the performance and the caching of multiple connections, which already opened to the databases. I suppose that you have multiple databases, which have the same schema. You need to access the databases using (sharing) the same database context.
I can suggest you two solutions.
The first solution consists from registering one context and reopening it if the one opened co change the connection string.
Let us you have TenantDbContext, which could be opened with different destination databases. For example with
#"Server=(localdb)\mssqllocaldb;Database=TenantDb1;Trusted_Connection=True;"
or
#"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;"
First of all you remove OnConfiguring like
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=TenantDb;Trusted_Connection=True;");
}
which could exist in the definition of TenantDbContext and you use the following code in ConfigureServices of Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
var connection1 = #"Server=(localdb)\mssqllocaldb;Database=TenantDb1;Trusted_Connection=True;";
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<TenantDbContext>(options => options.UseSqlServer(connection1));
services.AddMvc();
...
}
In the way you inject TenantDbContext with one from the database (TenantDb1). Let us the TenantDbContext contains some entity set like Blog for example. Thus your can define some MVC controller in the following way
public class TenantsController : Controller
{
private TenantDbContext _context;
public TenantsController (TenantDbContext context)
{
_context = context;
}
public IActionResult Index() {
var con = _context.Database.GetDbConnection();
// now the con uses either TenantDb2 or TenantDb2
// con.ConnectionString can be used to get or set the
// connection string
string needConStr = #"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;";
if (con.ConnectionString != needConStr) { // can be compared more carefully
_context.Database.CloseConnection();
con.ConnectionString = needConStr;
}
// get some data from the TenantDbContext
var blog = _context.Blog.ToList();
return View(blog);
}
}
The second solution don't need to inject any TenantDbContext using DependencyInjection. Instead of that you need just add one simple constructor to TenantDbContext:
public TenantDbContext(DbContextOptions optionsBuilder): base (optionsBuilder)
{
}
Such simple constructor will allows you to create the context at any time when you need it:
public class TenantsController : Controller
{
public IActionResult Index() {
var contextOptions = new DbContextOptionsBuilder();
contextOptions.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;");
var context = new BloggingContext(contextOptions.Options);
context.Database.OpenConnection();
// get some data from the TenantDbContext
var blog = context.Blog.ToList();
return View(blog);
}
}
I used all the connection strings directly in the code. You can easy modify the above code to get all connection strings from the config file appsettings.json.
Solved.
1. CreateCustomUserStore
public class TenantUserStore : UserStore<TenantUser, TenantRole, TenantDbContext, Guid>
{
public TenantUserStore(TenantDbContext context, IdentityErrorDescriber describer = null): base(context, describer)
{
}
}
And here is code how to instantiate UserManager with custom database:
IUserStore<TenantUser> CustomStore = new TenantUserStore(new TenantDbContext(coonection), null);
UserManager<TenantUser> manager = new UserManager<TenantUser>(CustomStore, _optionsAccessor, _passwordHasher, _userValidators,
_passwordValidators, _keyNormalizer, _errors, _services, _logger, _contextAccessor);
And DI used only for rest of UserManager Constructor:
public class TenantsController : Controller
{
private readonly IHttpContextAccessor _contextAccessor;
private readonly IOptions<IdentityOptions> _optionsAccessor;
private readonly IPasswordHasher<TenantUser> _passwordHasher;
private readonly IEnumerable<IUserValidator<TenantUser>> _userValidators;
private readonly IEnumerable<IPasswordValidator<TenantUser>> _passwordValidators;
private readonly ILookupNormalizer _keyNormalizer;
private readonly IdentityErrorDescriber _errors;
private readonly IServiceProvider _services;
private readonly ILogger<UserManager<TenantUser>> _logger;
public TenantsController(IHttpContextAccessor contextAccessor,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TenantUser> passwordHasher,
IEnumerable<IUserValidator<TenantUser>> userValidators,
IEnumerable<IPasswordValidator<TenantUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<TenantUser>> logger
)
{
_optionsAccessor = optionsAccessor;
_passwordHasher = passwordHasher;
_userValidators = userValidators;
_passwordValidators = passwordValidators;
_keyNormalizer = keyNormalizer;
_errors = errors;
_services = services;
_logger = logger;
_contextAccessor = contextAccessor;
}
I want to pass the UserID (Forms Authentication) to the DB Context class which will call an overload of SaveChanges(). This over loaded function would first calla stored proc to set DBSession context for auditing and then will call the base SaveChanges().
The question here is what is a good approach to send the User id to this DBContext class (I have made UserId as a property of this class) from the Controller without explicitly calling it every time.
I want something which would be just fired every time and pass on the UserId to DBContext class without writing code for it each time.
public partial class DataContext : DbContext, IDataContext
{
public DataContext()
: base("name=DataContext")
{
}
public DataContext(DbConnection existingConnection, bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection)
{
}
public string UserID { get; set; }
public override int SaveChanges()
{
SetDbSession();
return base.SaveChanges();
}
private void SetDbSession()
{
//Open a connection to the database so the session is set up
this.Database.Connection.Open();
//Set the db session
//ExecuteSqlCommand ot to be used as it will close the connection
using (var cmd = this.Database.Connection.CreateCommand())
{
var parm = cmd.CreateParameter();
parm.ParameterName = "#userId";
parm.Value = UserID ;
cmd.CommandText = "SetDbSession";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(parm);
cmd.ExecuteNonQuery();
};
}
This is the DB Context class, all I want is to pass UserId into it, in a way which doesn't make it cumbersome to be used across.
You could inject it into the constructor:
public partial class DataContext : DbContext, IDataContext
{
private string _userID;
public DataContext(string userID)
: base("name=DataContext")
{
_userID = userID;
}
public DataContext(string userID, DbConnection existingConnection, bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection)
{
_userID = userID;
}
public override int SaveChanges()
{
SetDbSession();
return base.SaveChanges();
}
private void SetDbSession()
{
//Open a connection to the database so the session is set up
this.Database.Connection.Open();
//Set the db session
//ExecuteSqlCommand ot to be used as it will close the connection
using (var cmd = this.Database.Connection.CreateCommand())
{
var parm = cmd.CreateParameter();
parm.ParameterName = "#userId";
parm.Value = _userID;
cmd.CommandText = "SetDbSession";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(parm);
cmd.ExecuteNonQuery();
};
}
When creating your DB Context in a controller:
var context = new DataContext(User.Identity.Name);
If you're using ASP.NET MVC and Autofac, one way would be to inject HttpContextBase to your service layers through an abstraction.
public class MyHttpAbstraction :IMyHttpAbstraction {
public MyHttpAbstraction (HttpContextbase httpContextBase) { _httpContextBase = httpContextBase; }
public string GetUserId() {
return _httpContextBase.User.Identity.GetUserId();
}
}
and in your type registrations
builder.Register(c => new HttpContextWrapper(HttpContext.Current)).As<HttpContextBase>().InstancePerHttpRequest();
This way the concrete implementations of your service can depend on MyHttpAbstraction rather than know HttpContextBase directly. You can string along this dependency all the way down to your IDataContext. If you want to go crazy, feel free to abstract DbContext and IDataContext out and create an intermediary class that takes DbContext as a dependency and implements IDataContext:
public class MyDataContext : IDataContext {
public MyDataContext(DbContext context, IMyHttpAbstraction myHttpAbstraction) { _context = context; _myHttpAbstraction = myHttpAbstraction; }
//...feel free to implement
}
Not sure if this is what you're looking for.
I have an MVC intranet application which uses EF 6. I have setup the DataAccess project in a separate class library which has EF 6 referenced. I have an entity which implements an interface:
public interface IAuditable
{
DateTime CreatedDateTime { get; set; }
string CreatedBy { get; set; }
}
public class Collection : IAuditable
{
// Properties
}
However, in the SaveChanges method I obviously don't have access to HttpContext.Current.User.Identity.Name as it is in a separate class library, so I was wondering how one would set this in SaveChanges?
public override int SaveChanges()
{
var addedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added);
foreach (var dbEntityEntry in addedEntries)
{
var entity = dbEntityEntry.Entity as IAuditable;
if (entity != null)
{
entity.CreatedDateTime = DateTime.Now;
// how do I set entity.CreatedBy = HttpContext.Current.User.Identity.Name?
}
}
return base.SaveChanges();
}
Edit
Following on from #CodeCaster solution, I have the following:
[BreezeController]
public class BreezeController : ApiController
{
private readonly BTNIntranetRepository _repository;
public BreezeController(BTNIntranetRepository repository)
{
_repository = repository;
_repository.LoggedInUser = HttpContext.Current.User.Identity.Name;
}
// Methods
}
But HttpContext.Current.User is null
This can be solved in many ways.
You're not really showing relevant code, but you can for example give the library class you expose a public string LoggedInUser (or ActingUser or give it a name) property which you set when instantiating it:
public class SomeController : Controller
{
private IDataSource _dataSource;
public SomeController(IDataSource dataSource)
{
_dataSource = dataSource;
_dataSource.LoggedInUser = HttpContext.Current.User.Identity.Name
}
}
You can then simply use that property in your IDataSource.SaveChanges() method:
public override int SaveChanges()
{
// ...
entity.CreatedBy = this.LoggedInUser;
}