Can't create database EF Core C# - c#

Im trying to create my database after finnishing up my classes, getting error message
"Unable to create an object of type 'HamsterDbContext'. For the different patterns supported at design time"
public class HamsterDbContext : DbContext
{
public HamsterDbContext(DbContextOptions<HamsterDbContext> options) : base(options) { }
public virtual DbSet<Hamster> Hamsters { get; set; }
public virtual DbSet<Cage> Cages { get; set; }
public virtual DbSet<ExerciseArea> ExerciseArea { get; set; }
}
internal class DatabaseHelper
{
public static HamsterDbContext NewContext()
{
const string host = "(LocalDB)\\MSSQLLocalDB";
const string database = "HamsterDaycare";
var connectionString = $"Data Source={host};Initial Catalog={database};Integrated Security=True;";
var dbContextBuilder = new DbContextOptionsBuilder<HamsterDbContext>();
dbContextBuilder.UseSqlServer(connectionString);
return new HamsterDbContext(dbContextBuilder.Options);
}
}
Looks like this, typing add-migration CreateDatabase, what am i doing wrong here?

Fix your string connection. Instead DataSource, put server. Your ConnectionString must be this way:
{
"ConnectionStrings": {
"DefaultConnection": "server=yourserver;database=yourdatabase;user=youruser;password=yourpassword"},
...
},

Related

.NET Core pass configuration section via command line

In appsettingsjson file i have:
"DataSource": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "Root",
"CollectionName": "ApiLog"
},
in Program.cs, i get this data like this
builder.Services.Configure<DatabaseSettings>(
builder.Configuration.GetSection("DataSource"));
where DatabaseSettings class is;
public class DatabaseSettings
{
public string ConnectionString { get; set; } = null!;
public string DatabaseName { get; set; } = null!;
public string CollectionName { get; set; } = null!;
}
Then i can access instance of DatabaseSettings via dependency injection like:
public class LogService
{
private readonly IMongoCollection<Log> _collection;
public LogService(
IOptions<DatabaseSettings> databaseSettings)
{
var mongoClient = new MongoClient(
databaseSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(
databaseSettings.Value.DatabaseName);
_collection = mongoDatabase.GetCollection<ElekseLog>(
databaseSettings.Value.CollectionName);
}
}
the question is i dont want to store db info in appsettings json file. I want to pass tis info from command line without changing the code. How can I achieve this?
You need to "flatten" the parameters by joining "path elements" with : and pass as key-value pairs. For example:
yourapp "DataSource:ConnectionString"="mongodb://localhost:27017"
Or
yourapp --DataSource:ConnectionString=mongodb://localhost:27017
Some info can be found in the docs - Command-line configuration provider.

Why am i getting argumentNullException when adding migration

I'm trying to create a migration in my web api for one-to-many db. Services are configured this way and the connection string is successfully received from launchsettings.json
var connectionStr = Environment.GetEnvironmentVariable("ApplicationDbContext");
services.AddDbContext<InsolationResultContext>(options =>
{
options.UseNpgsql(connectionStr, builder =>
{
builder.CommandTimeout(300);
builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
});
});
This are models
public class DocumentInsolationResult
{
public string Id { get; set; }
public string UserId { get; set; }
public IEnumerable<InsolationResult> Elements { get; set; }
}
public class InsolationResult
{
public string UniqueId { get; set; }
public string Insolation { get; set; }
}
And DbContext
public class InsolationResultContext : DbContext
{
public DbSet<DocumentInsolationResult> DocumentInsolationResults { get; set; }
public DbSet<InsolationResult> InsolationResults { get; set; }
public InsolationResultContext(DbContextOptions<InsolationResultContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DocumentInsolationResult>()
.HasMany(p => p.Elements)
.WithOne();
}
}
the connection string looks like this:
"environmentVariables": {
"ApplicationDbContext": "Host=192.168.1.***;Port=****;Database=***.******;Username=*****;Password=****",
"ASPNETCORE_ENVIRONMENT": "Development"
},
When trying to "Add-Migartion Init i always get "Value cannot be null. (Paramtere 'connectionString'). What am i doing wrong? Coulnd't actually find the answer on the internet
upd: i'me receiving the connection string from launchsetting, it's ok as i'm using the same way of getting connectionString on some other projects
upd2 hardcoding the connection string worked for me
If you use IConfiguration instead of Environment.GetEnvironmentVariable then it becomes a lot easier:
var connString = config.GetValue<string>("ApplicationDbContext");
It should be
"ConnectionStrings": {
"ApplicationDbContext": "Host=192.168.1.***;Port=****;Database=***.******;Username=*****;Password=****""
}
in appsettings.Development.json file.
Thus it is giving error "Value cannot be null. (Paramtere 'connectionString')."
And in your startup.cs file
services.AddDbContext<InsolationResultContext >(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("ApplicationDbContext")
});
Well the problem was really with the connection string. I couldn't create a migration when using connection string from appsettings or launchsettings. After hardcoding the connection string in stratup.cs i was able to create migration. And now after the db is initialized i'm using connection string from launchsettings. This might be some EF core tricky things.

Storing user login data in Business logic layer during session

I've got project with dll dependecies like below:
DataAccessLayer(DAL) <- BusinessLogicLayer(BLL) <- MVC Web App(WEB)
The project needs to connect with database by separated SQL logins (Each SQL login means different App user or in other words already existing database users and passwords will be used as app users/logins). So thre's no way to youse default connection.
I'm passing class "Connection" (Class is defined on BLL so DAL can see it but WEB can't) to my Context class to make connection with database like below:
public class WMSContext : DbContext
{
public WMSContext(Connection con)
: base(con.ContextOption)
{
}
public virtual DbSet<Users> Users { get; set; }
public virtual DbSet<Groups> Groups { get; set; }
public virtual DbSet<UsersConfig> UsersConfig { get; set; }
public virtual DbSet<UsersGroup> UsersGroup { get; set; }
public virtual DbSet<UsersGroupConfig> UsersGroupConfig { get; set; }
public virtual DbSet<ShipmentVehicles> ShipmentVehicles { get; set; }
public virtual DbSet<VSHIPMENTS> VSHIPMENTS { get; set; }
public virtual DbSet<VShipmentsDetails> VShipmentsDetails { get; set; }
}
public class Connection
{
public string Login { get; private set; }
public string Password { get; private set; }
public string Server { get; private set; }
public string Database { get; private set; }
public DbContextOptions<WMSContext> ContextOption { get; private set; }
public Connection(string servAddr, string dbName, string login, string pass)
{
Login = login;
Password = pass;
Server = servAddr;
Database = dbName;
string connStr = "Data Source = " + servAddr + "; Initial Catalog = "+ dbName + "; Persist Security Info = True; User ID = "+ login + "; Password = "+ pass+ "";
var optBuild = new DbContextOptionsBuilder<WMSContext>();
optBuild.UseSqlServer(connStr);
ContextOption = optBuild.Options;
}
}
The problem is that the instance of Connection class should be stored somwhere during user session to perform other requests for certain SQL user. My first thought was to assign instance of Connection to Session. Something like below:
Connection = new Connection(){ some login data }
HttpContext.Session.SetString("Login Data", connection);
but in that case i would have to set DAL dependent to WEB and it not seems to be elegant slolution. Sure i can just extract that Connection class to class liblary that will be shared by all projects but i'm curious if there is some way to store data only in BLL and only temporary and delete them when WEB user session ends?
Or maybe there is some other way than using Sessions?
I suggest you create a context object with an interface that you can inject into the DAL. That way the DAL will depend on your interface but not on any web-specific APIs.
For example
interface IDatabaseAuthenticationContext
{
string DatabaseUserName { get; set; }
string DatabasePassword { get; set ; }
}
class AuthenticationContext: IDatabaseAuthenticationContext
{
private readonly HttpContextBase _httpContext;
public AuthenticationContext(HttpContextbase httpContext)
{
_httpContext = httpContext;
}
public string DatabaseUserName
{
get
{
return _httpContext.Session["DatabaseUserName"];
}
set
{
_httpContext.Session["DatabaseUserName"] = value;
}
}
public string DatabasePassword
{
get
{
return _httpContext.Session["DatabasePassword"];
}
set
{
_httpContext.Session["DatabasePassword"] = value;
}
}
}
Then in your DAL:
class DataAccessLayer : IDataAccessLayer
{
private readonly IDatabaseAuthenticationContext _dbAuthenticationContext;
public DataAccessLayer(IDatabaseAuthenticationContext context)
{
_dbAuthenticationContext = context; //Injected
}
public void ExecuteSomeCommand()
{
using (var conn = new SqlConnection(this.CreateConnectionString()))
{
var cmd = new SqlCommand("SomeCommand");
cmd.CommandType = StoredProcedure;
cmd.Connection = conn;
cmd.ExecuteNonQuery();
}
}
private string CreateConnectionString()
{
return String.Format("Server={0};UID={1};PWD={2}", this.GetServerName(),
_dbAuthenticationContext.DatabaseUserName,
_dbAuthenticationContext.Databasepassword);
}
Then in your composition root:
container.RegisterType<IDatabaseAuthenticationContext, AuthenticationContext>();
container.RegisterType<HttpContextBase, () => new HttpContextWrapper(HttpContext.Current));
container.RegisterType<IDataAccessLayer, DataAccessLayer>();
This way you can pass what you need to the DAL without requiring the DAL to know anything about HttpContext or other web-specific APIs. If you ever write a console application (or other program without a reference to System.Web), you just need to write a different implementation of AuthenticationContext that implements IDatabaseAuthenticationContext.

MVC 6 Create View issues : There was an error creating the DBContext to get the model

Attempting to create a view for "Create" using the NavBar Model and the NavBarEntity shown below (in MVC6) receives this message...
There was an error running the selected code generator: There was an error creating the DBVContext instance to get the model... Value cannot be null... Parameter Name: connectionString
I picked this mode in View Wizard...
public class NavBarModel
{
public string ID { get; set; }
public List<LinkModel> Links { get; set; }
}
This DBContext class is shown here...
public class NavBarEntity : DbContext
{
public NavBarEntity()
{
ID = Guid.NewGuid().ToString();
}
[Key]
public string ID { get; set; }
public DbSet<List<LinkModel>> Links { get; set; }
}
And the LinkModel shown here..
public class LinkModel
{
public LinkModel()
{
ID = Guid.NewGuid().ToString();
}
[Key]
private string ID { get; set; }
public string HREF { get; set; }
public string Text { get; set; }
}
Configure Services looks like this...
var cfg2 = Configuration["Data Source=MyPC\\SQLEXPRESS;Initial Catalog=Dashboard;Integrated Security=True;Pooling=False"];
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(cfg))
.AddDbContext<NavBarEntity>(options =>
{
options.UseSqlServer(cfg2);
});
Question: What am I doing wrong?
Thanks for the help listed above..
For newbies to MVC6 and EF7, the method named ConfigureServices, must contain a json pointer to the appsetting.json. That method is found in the Startup.cs file.
This is the services configuration to match the code shown above. The string value in the brackets points to the json location...
var cfg2 = Configuration["Data:DashboardContext:ConnectionString"];
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DashboardContext>(options =>
{
options.UseSqlServer(cfg2);
})
But, you must also put a value into appsettings.json like this:
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-TestWebApplication1-d91c23e4-3565-476d-a7c0-45665bc0c367;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"DashboardContext": {
"ConnectionString": "Data Source= MYPC\\SQLEXPRESS;Initial Catalog=Dashboard;Integrated Security=True;Pooling=False"
}
},
The root cause of the Parameter Name: connectionString being null was that the appsettings.json has to be exactly as shown above. The json parsing routines must be able to locate the string name/value pair... Notice that these configurations fall under the "Data" name that contains other names. in this case "DefaultConnection" was there by default, and I added "DashboardContext" portion.
Also in MVC 6 you must change the connectionString type to IServiceProvider and NOT string as was done before...
public class DashboardContext : DbContext
{
public DashboardContext(IServiceProvider connectionString) : base (connectionString)
{}
public DbSet<NavBarEntity> NavBars { get; set; }
}
Alas: The Views created no problem...Yes!
The way you've tried to combine the DbContext and your entity isn't right. The DbContext should reference any entities you have as DbSets - entities should not inherit from it.
Your DbContext should look similar to this (EF6)
public class MyDbContext : DbContext
{
public MyDbContext(string connectionString)
: base(connectionString)
{ }
public DbSet<NavBarEntity> NavBars { get; set; }
// Other entities
}
The constructor takes the name of the connecting string entry that's defined in your web.config you want to use. There are other ways to do this though - see here
Then create your entities as a simple class (POCO):
public class NavBarEntity
{
public NavBarEntity()
{
ID = Guid.NewGuid().ToString();
}
[Key]
public string ID { get; set; }
// Other properties/columns here
}
EDIT
My original answer was based on EF6 rather than EF7. Here's how I would implement the context in EF7 for completeness:
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{ }
public DbSet<NavBarEntity> NavBars { get; set; }
// Other entities
}

Accessing the db connection from within my code first "table" class

Is it possible to get to the current connection from inside your code first "table" class?
I'm trying to write a MVC Multi Tenancy app (1 app, many db's) the easiest way i can think of is to pass the connection string (or tenant name) when creating the dbcontext (i've looked at other ways to do this, but don't really understand them). However once i go into the table class I cannot then access the current db connection to perform other actions i need.
Example code
public class ConnectionContext : DbContext
{
public ConnectionContext(String connectionString)
: base(connectionString)
{
if (!this.Database.Exists())
throw new Exception("Database does not exist");
}
public DbSet<Table1> Table1 { get; set; }
}
[Table("Table1")]
public class Table1
{
[Key]
public String Column1 { get; set; }
public String Column2 { get; set; }
public Int32 GetNoOfColumns()
{
using (var conn = new ConnectionContext("???")) // <-- **issue here**
{
//Code to get info
return 0;
}
}
public void Authorize()
{
using (var conn = new ConnectionContext("???")) // <-- **issue here**
{
this.Column2 = "Authorized";
conn.Entry(this).State = EntityState.Modified;
conn.SaveChanges();
}
}
}
Not sure it's possible in the way that I was looking at.
You don't have to make a constructor with a connection string parameter, you can create your dbcontext class like this:
public class ConnectionContext : DbContext
{
public ConnectionContext()
: base("nameOrConnectionString")
{
if (!this.Database.Exists())
throw new Exception("Database does not exist");
}
public DbSet<Table1> Table1 { get; set; }
}
And then call your connection context this way:
using (var conn = new ConnectionContext())
{
}

Categories

Resources