How to change Database connection string runtime in Mobile Service TableController API - database

I have develop mobile app in xamarin forms and successfully login with Azure B2C. I Create a Web api using Microsoft.Azure.Mobile.Server TableController.
and it works fine.
I want to change database connection string runtime.
for example,
if (UserName == "ABC")
connectionstring = "..."
else if(UserName == "XYZ")
connectionstring = "..."
How to change Database connection string runtime in Mobile Service TableController API ?.

Freinds, I Got the solution.may be it is useful to someone.
In my MobileServiceClient API Startup.Mobile.cs, dbcontext is intialize as below
public partial class Startup
{
public async static void ConfigureMobileApp(IAppBuilder app)
{
GlobalVars.ConnectionString = #"Data Source = tcp:stronginventoryapidbserver.database.windows.net, 1433; Initial Catalog = StrongInventoryAPI_db; User ID = strong; Password=Tapsid_het1;";
Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationContext, StrongInventoryAPI.Migrations.Configuration>());
app.UseWebApi(config);
}
}
then, i create a ConnectionMstController to change the connection string. Below is Initailize method of Controller.
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
GlobalVars.SID = ((ClaimsPrincipal)User).FindFirst(ClaimTypes.NameIdentifier).Value;
if ( GlobalVars.SID.Contains("2e3c2d2a-14e7-4e03-bc5e-b9f6b2b09617"))
GlobalVars.ConnectionString = #"Data Source = tcp:xxxxxxxxapidbserver.database.windows.net, 1433; Initial Catalog = Drona_db; User ID = strong; Password=xxxxxx;";
ApplicationContext context = new ApplicationContext(GlobalVars.ConnectionString);
DomainManager = new EntityDomainManager<ConnectionMst>(context, Request);
}
Remember to add contructor in your context in my case Application Context. my ApplicationConext is,
public class ApplicationContext : DbContext
{
// You can add custom code to this file. Changes will not be overwritten.
//
// If you want Entity Framework to alter your database
// automatically whenever you change your model schema, please use data migrations.
// For more information refer to the documentation:
// http://msdn.microsoft.com/en-us/data/jj591621.aspx
//private const string connectionStringName = "Name=MyDbConnection";
public ApplicationContext() : base(GlobalVars.ConnectionString)
{
}
//start
public ApplicationContext(string ConnectionString) //connection string change in ConnectionMstController
: base(ConnectionString)
{
}
//end
public DbSet<ScheduleMst> ScheduleMsts { get; set; }
public DbSet<AccMst> AccMsts { get; set; }
public DbSet<ConnectionMst> ConnectionMsts { get; set; }
public DbSet<ItemGrpMst> ItemSubGrpMsts { get; set; }
public DbSet<ItemSubGrpMst> ItemSubSubGrpMsts { get; set; }
public DbSet<LotMst> LotMsts { get; set; }
public DbSet<UnitMst> UnitMsts { get; set; }
public DbSet<ItemMst> ItemMsts { get; set; }
public DbSet<SaleOrdMst> SaleOrdMsts { get; set; }
public DbSet<SaleOrdDet> SaleOrdDets { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}

Related

Asp.net core Tables for Identity not being created in the database

When I see at other sample projects, the number of tables created for supporting Identity in the db is great (such ones as AspNetRoles, AspNetUserClaims, etc..), but in my case when I make the migration and the update only the User table has been created. What is the reason?
Here is my code in the startup, in the dbcontext and my class user:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSession();
services.AddMemoryCache();
services.AddDbContext<ApplicationDbContext>( options =>
options.UseSqlServer(Configuration["Data:photoarchiver:ConnString"]));
services.AddIdentity<User, IdentityRole > (
opts => {
opts.Password.RequireDigit = false;
opts.Password.RequiredLength = 7;
opts.Password.RequireLowercase = true;
opts.Password.RequireUppercase = false;
opts.Password.RequireNonAlphanumeric = false;
}).AddEntityFrameworkStores<ApplicationDbContext>();
}
Class DbContext:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<User>().HasMany(u => u.Photos).WithOne(i => i.User);
modelBuilder.Entity<Photo>().ToTable("Photos");
modelBuilder.Entity<Photo>().HasOne(i => i.User).WithMany(u => u.Photos);
modelBuilder.Entity<Category>().ToTable("Categories");
}
public DbSet<Photo> Photos { get; set; }
public DbSet<Category> Categories { get; set; }
}
Class User:
public class User : IdentityUser
{
public virtual List<Photo> Photos { get; set; }
[Required]
public string DisplayName { get; set; }
public string Notes { get; set; }
[Required]
public DateTime CreatedDate { get; set; }
}
To get all the AspNetRoles, etc tables "for free" you need to change your ApplicationDbContext to extend from IdentityDbContext<User> instead of just DbContext. IdentityDbContext<T> is found in the Microsoft.AspNetCore.Identity.EntityFrameworkCore namespace. You can see from the source code https://github.com/aspnet/Identity/blob/master/src/EF/IdentityDbContext.cs, IdentityDbContext will bring in the required DbSetproperties.
As you correctly identified in the comment to your question, you will need to call base.OnModelCreating(builder) and re-make your migration files.

CreateDatabaseIfNotExists tries to initialize an existing database

I am using CreateDatabaseIfNotExists when creating a database, but it just doesn't drop the database and then it starts initializing it.
There is my DbContext.
public class AppDbContext : DbContext
{
public AppDbContext() : base("Valtrends")
{
InitializeDbContext();
}
public DbSet<FactorType> FactorTypes { get; set; }
public DbSet<ComplexType> ComplexTypes { get; set; }
public DbSet<Value> Values { get; set; }
public DbSet<DataLoader.Entities.Version> Versions { get; set; }
public DbSet<DefaultPlotData> DefaultPlotData { get; set; }
public DbSet<GraphBucket> GraphBuckets { get; set; }
public DbSet<XfactorFrom> XfactorsFrom { get; set; }
public DbSet<XfactorTo> XfactorsTo { get; set; }
public DbSet<DistributionData> DistributionData { get; set; }
public DbSet<Bin> Bins { get; set; }
public DbSet<DefaultPlotSettings> DefaultPlotSettings { get; set; }
private void InitializeDbContext()
{
Database.SetInitializer(new AppDbInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<FactorType>().HasMany(m => m.CompatibilityListXY).WithMany();
}
}
}
And there is the the first part of my AppDbInitializer.
public class AppDbInitializer : CreateDatabaseIfNotExists<AppDbContext>//DropCreateDatabaseAlways<AppDbContext>
{
public override void InitializeDatabase(AppDbContext context)
{
base.InitializeDatabase(context);
var jsonImporter = new JsonImporter();
List<DataLoader.Entities.Version> versions = (jsonImporter.ImportFromJson<List<DataLoader.Entities.Version>>("Versions.json"));
context.Versions.AddRange(versions);
context.SaveChanges();
After SaveChanges I get a violation of primary keys exception on the entity version.
I am using CreateDatabaseIfNotExists when creating a database, but it just doesn't drop the database and then it starts initializing it
It never drops an existing database. As the name implies, it checks if database exists, and if yes, it does nothing, otherwise it creates and initializes it.
Also note that the InitializeDatabase method of the IDatabaseInitializer is always called, it's the class that implements it responsibility what actual action to perform.
In your case you incorrectly assume that the base method will always create the new database, which is not the case. If you want to add a code that executes only when a new database is created, then you should override the Seed method instead:
public class AppDbInitializer : CreateDatabaseIfNotExists<AppDbContext>
{
protected override void Seed(AppDbContext context)
{
var jsonImporter = new JsonImporter();
List<DataLoader.Entities.Version> versions = (jsonImporter.ImportFromJson<List<DataLoader.Entities.Version>>("Versions.json"));
context.Versions.AddRange(versions);
context.SaveChanges(); // you don't need this, it's automatically called after `Seed` call
}
}

how to display values from SQL database when an IP address is matched

..What Method I'm using..
I'm creating UWP app connecting to a SQL Server database via WebService.
..What I need help with..
I want to display values from the Database when the IP address matches the PC the App is run on.
..What I have so far..
Right now I have the code beyond to grab PC's local HostName show below. Might need to move it to Appx, have not decided. I also have Values displaying on the View called DevicePage. The DevicePageViewModel is making the call to the WebService
CodeBeyond>>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
foreach (HostName localHostName in NetworkInformation.GetHostNames())
{
if (localHostName.IPInformation != null)
{
if (localHostName.Type == HostNameType.Ipv4)
capturedHostName.Text = localHostName.ToString();
break;
}
}
}
ViewModel>>
var uriD = new Uri("http://localhost:2463/api/Devices");
HttpClient client = new HttpClient();
try
{
var JsonResponseD = await client.GetStringAsync(uriD);
var devicesResult = JsonConvert.DeserializeObject<List<Device>>(JsonResponseD);
Devices = devicesResult;
}
catch
{
MessageDialog dialog = new MessageDialog("Unable to Access WebService at this Time!");
await dialog.ShowAsync();
}
client.Dispose();
EntityFramework Model>>
public class Device
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int HostName { get; set; }
public string RouterName { get; set; }
public string DriveModel { get; set; }
public string DriveSN { get; set; }
public string OldDriveSN { get; set; }
public string Server { get; set; }
public string IP { get; set; }
public string Gateway { get; set; }
public string Hardware { get; set; }
}
What you are looking for is
Device device = devicesResult.Find(data => data.IP == capturedHostName.Text);
This should get you the Device Class with IP from your localHostName.

MVC Invalid object name 'dbo.Staffs' error

I got the error Invalid object name 'dbo.Staffs'. but I'm not sure why. I actually deleted and recreated my database with EF because previously I had other errors. But I'm quite sure I recreated it correctly because I've done it in the same way for other programs and it works fine.
.edmx database diagram
Controller
private StaffPortalDBEntities1 db = new StaffPortalDBEntities1();
SqlConnection cnn = new SqlConnection(ConfigurationManager.ConnectionStrings["StaffPortalDBConnectionString"].ConnectionString);
[Authorize]
public ActionResult Index()
{
var userEmail = User.Identity.Name;
var model = db.Staffs.Where(i => i.Email == userEmail).Include("Histories").Include("CurrentApplications").FirstOrDefault();
return View(model);
}
I got the error is for the line var model = db.Staffs.Where(i => i.Email == userEmail).Include("Histories").Include("CurrentApplications").FirstOrDefault();
Generated Staff class
public partial class Staff
{
public Staff()
{
this.Histories = new HashSet<History>();
this.CurrentApplications = new HashSet<CurrentApplication>();
}
public int StaffID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public Nullable<decimal> AllocatedLeave { get; set; }
public Nullable<int> BalanceLeave { get; set; }
public virtual ICollection<History> Histories { get; set; }
public virtual ICollection<CurrentApplication> CurrentApplications { get; set; }
}
Try this:
var model = db.Staffs.Where(i => i.Email == userEmail).Include(x=>x.Histories).Include(x=>x.CurrentApplications).FirstOrDefault();

Showing specific data based on the currently logged in user

I have a question that deals with the logistics of returning rows of data in a SQL database (Entity Framework) based on the user that is logged in; I have mainly focused on desktop C# applications and while making the switch to ASP.NET MVC 4 I'm having a bit of difficulty when it comes to figuring this out (I've searched around and none of the answers seem to provide exactly what I'm looking for):
I would like to use the authorization built in to ASP.NET (MVC4), and allow users to post data about their websites (site category, url, age, etc.) with a form, and have the form store the data (using Entity Framework) to a database (called PrimaryDomainsDb) that is tied to their Id in the UserProfile table.
When the user clicks a button to show their list of domains, how can I make the application pull their list of domains (relevant rows of data) while ignoring other users rows?
Again, I'm mainly looking for the logistics and concepts (using foreign keys, for example) and psuedocode rather than actually spoonfeeding me a bunch of code.
If anyone has any best practice ideas (i.e. link the UserProfile to the PrimaryDomainDb this way, and use EF to call the rows matching their Id this way to return the rows to the View), it would be much appreciated.
Some sample code:
I currently have my PrimaryDomain code first set up like this (this doesn't have the decorators that specify min/max length, etc.):
public class PrimaryDomain
{
public virtual int Id { get; set; }
public virtual string SiteName { get; set; }
public virtual string SiteURL { get; set; }
public virtual SitePlatforms SitePlatform { get; set; }
public virtual decimal? SiteDA { get; set; }
public virtual decimal? SitePA { get; set; }
public virtual string SiteAge { get; set; }
public virtual DateTime? LastStatusUpdate { get; set; }
public virtual string SiteIP { get; set; }
}
And I have a User class that is different than the one provided by ASP.NET WebSecurity, that looks like this: (also, I know that "password" should not be in string formatting, this is just for initial set-up purposes - and password should probably be removed altogether and handled by WebSecurity, I think).
public class User
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual string Email { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string MozAccessID { get; set; }
public virtual string MozKey { get; set; }
public virtual decimal AccuountBalance { get; set; }
public virtual PrivateProxy PrivateProxies { get; set; }
public virtual PrimaryDomain PrimaryDomains { get; set; }
}
When pulling the data for Views I run everything through a repository using direct injection:
public interface IUserDataSource
{
IQueryable<User> Users { get; }
IQueryable<PrimaryDomain> PrimaryDomains { get; }
void Save();
}
This is my UserDb class, which is fed in whenever the code calls for an IUserDataSource (via direct injection):
public class UserDb : DbContext, IUserDataSource
{
public UserDb()
: base("DefaultConnection")
{
}
public DbSet<User> Users { get; set; }
public DbSet<PrimaryDomain> PrimaryDomains { get; set; }
IQueryable<User> IUserDataSource.Users
{
get { return Users; }
}
IQueryable<PrimaryDomain> IUserDataSource.PrimaryDomains
{
get { return PrimaryDomains; }
}
void IUserDataSource.Save()
{
SaveChanges();
}
}
And this is, for example, how I would pass the PrimaryDomains model to the View:
public class NetworkController : Controller
{
//
// GET: /Network/
private IUserDataSource _db;
public NetworkController(IUserDataSource db)
{
_db = db;
}
public ActionResult ListDomains()
{
var allDomains = _db.PrimaryDomains;
return View(allDomains);
}
}
But instead of pulling the entire PrimaryDomains list from the data source, I would like to add a way to reference the currently logged in user id to make the application only show the domains for that specific user, not all domains, and when adding a new domain via the form to reference the User Id and add it into the table as well.
My original question may have caused some confusion as to what I'm trying to achieve; It's my fault for posing the wrong way of going about what I'm trying to do. After much research and learning, I've found that exactly what I'm looking for is a multi-tenant data architecture approach.
This is probably what you are looking for. If I understood you correctly you want to use WebSecurity to login or register users but you want to use entity framework to store some user-specific data. Code below connects WebSecurity tables with your database CodeFirst created using EntityFramework.
You create class below (from tutorial).
[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()
{
try
{
if(!WebSecurity.Initialized)
WebSecurity.InitializeDatabaseConnection("ConnectionString", "DbUsers", "UserId", "Email", 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);
}
}
}
}
It creates necessary tables for registering and logging your users. The magic is in second, third and fourth parameter. It is respectively table, userId column and userName column from YOUR database that you can create by EntityFramework. WebSecurity uses that table along with other self-generated tables to manage your users and let them register, login and so on.
Then in your code first you simply create table
public class DbUser
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[MaxLength(40)]
public string Email { get; set; }
[MinLength(3)]
[MaxLength(30)]
[Required]
public string FirstName { get; set; }
[MinLength(3)]
[MaxLength(50)]
[Required]
public string LastName { get; set; }
}
Then you can simply query data from controller. In example below I use UserId stored by WebSecurity membership to retrieve account info from database.
public ActionResult AccountInfo()
{
if (FormsAuthentication.CookiesSupported == true && Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
var userId = WebSecurity.CurrentUserId;
var userInfo = context.Users.FirstOrDefault(x => x.UserId == userId);
userInfo.Password = "";
return View(userInfo);
}
else
{
ModelState.AddModelError("", "Wystąpił bląd autoryzacji, zaloguj się jeszcze raz.");
return RedirectToAction("Login", "Account");
}
}
EDIT:
Regarding your edited question as I understand besides the fact that you need to integrate WebSecurity with EF as above (I also forgot to mention that after creating InitializeSimpleMmebershipAttribute class as above you need to decorate your controller with that attribute) you also have problems with implementing generic repository. If that line is a problem:
var allDomains = _db.PrimaryDomains;
Then i suggest to read this article about implementing generic repository:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
If you want thing realy simple all you need is just add to your interface method
GetDomainByUserId(int userId)
and just implement that interface like that:
public class UserDb : DbContext, IUserDataSource
{
public UserDb()
: base("DefaultConnection")
{
}
public DbSet<User> Users { get; set; }
public DbSet<PrimaryDomain> PrimaryDomains { get; set; }
IQueryable<User> IUserDataSource.Users
{
get { return Users; }
}
IQueryable<PrimaryDomain> IUserDataSource.PrimaryDomains
{
get { return PrimaryDomains; }
}
IQueryable<PrimaryDomain> GetDomainByUserId(int userId)
{
return PrimaryDomains.Where(x => x.Id == userId).ToQueryable();
}
void IUserDataSource.Save()
{
SaveChanges();
}
}
But this is very bad approach and I strongly recommend reading that article.

Resources