Using Entity Famework without a DB connection - c#

I have an application that I tied to a DB for loading and storing data.
However I don't want to require the user to be on the network and have access to the DB. It just can't load or save without a connection.
I would like to instantiate it without a db connection (just use it in memory), but all constructors use the DB. I would prefer not to modify the entity framework generated .cs file in case I need to update it again from the DB I don't want to wipe out my changes.
How can I use the EF model without a connection?
public partial class SimRunnerEntities : ObjectContext
{
#region Constructors
/// <summary>
/// Initializes a new SimRunnerEntities object using the connection string found in the 'SimRunnerEntities' section of the application configuration file.
/// </summary>
public SimRunnerEntities() : base("name=SimRunnerEntities", "SimRunnerEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new SimRunnerEntities object.
/// </summary>
public SimRunnerEntities(string connectionString) : base(connectionString, "SimRunnerEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new SimRunnerEntities object.
/// </summary>
public SimRunnerEntities(EntityConnection connection) : base(connection, "SimRunnerEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
All SimRunnerEntities constructors use some sort of valid connection

Instantiating DbContext doesn't open connection to the database. DB connection will be opened after first query is made, where you select something or so. So if you are not modifying or reading any data, you are completely safe working without access to your database.
The thing is, that you can't really do anything with EF DbContext without triggering database, except using it's entity classes for some custom purpose.
The other question is: does you application can really work without using a database? And what are you trying to achieve?

Yes, you can do that.
In example, I have a solution which work with that architecture layout:
Project A - Server Side (WCF)
Project B - Class Library which Contain EF model + DB connection
Project C - Client side(WPF)
A reference to B,
A is WCF and hard copy the configuration from B so it can access the database using the DBContext.
C reference to B,
C is WPF project, the entities being filled from A using WCF and already have the goodies like INPC which already auto generated by EF.
The only thing I had to add in the client side is a custom class container which have list of properties of entities type (ie. List Users {get;set;} and etc..)
Further explanation according to comment request:
Q: Can you explain a bit more. If your Project B has a DB connection how does this solution work without a DB connection.
A: sure, project B configuration file (app.config) is simply not being used.
I just have it there for archive purpose or for copy paste..
I did copy and paste it to Project A app.config.
Q: In the UI I want to instantiate a data object but fails without the valid connection string because it include a EF item.
A: Regarding the instantiate an object.. simply as if you instantiate any other object: var entityX = new MyEntity{fill props code here};
It not requires me to use the Dbcontext to do that.
What I believe might be your next question is how to convoy data from WCF to client side - making the entities "trackable"? and for that I created a whole DTO Layer.. however that is beyond of your original question scope :)
Regarding the simple question can you use the Entities without using the dbcontext to manage them the answer is: Yes.

Related

Auto detach entity from dbcontext

services.AddDbContextPool<SecurityDBContext>(options =>
options.UseSqlServer(GlobalConfig.Configuration["ConnectionStrings:DefaultConnection"],
b => b.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))
);
This is how I added dbcontext,I used AddDbContextPool so the instance will be use over and over again for performance.
db.Entry(new AdminBlockClientConfig()
{
ActionId = input.aid.ToLongReturnZiro(),
MaxValue = input.value.ToIntReturnZiro(),
IsActive = input.isActive.ToBooleanReturnFalse(),
SiteSettingId = siteSettingId.ToIntReturnZiro()
}).State = EntityState.Added;
db.SaveChanges();
This is my code for adding new entity.
readonly SecurityDBContext db = null;
static List<AdminBlockClientConfig> AdminBlockClientConfigs = null;
public AdminBlockClientConfigService(SecurityDBContext db)
{
this.db = db;
}
This is my service constructor
services.AddScoped<IAdminBlockClientConfigService, AdminBlockClientConfigService>();
And this is my service injection config
The problem:
I don't have any validation for ActionId inside of my add new entity so if the user posts -1 for ActionId the entity will not be insert into SQL Server (relation problem) the system raises an exception and everything is as planed but the main problem is that one of the instance of SecurityDBContext become corrupted and I am no longer be able to call save change on that instance because the entity instance is still attached to dbcontext.
What is need:
It would be great if I can detach the entity after an error automatically so I can save the context.
I know how to detach entity from dbcontext, I need to its happen automatically (there is so many validation need to be added to project and I can not put time for those validation and if I put that time there will be high change of missing some place and its will be bug that can destroy my application and for performance I don't like to change the way I added dbcontext instance).
Thanks for your time.
edited: AddDbContextPool is not the problem, if one of my services add invalid data to dbcontext the other services can not use that dbcontext

How to use database sharding with EF Core and C#"

I'm currently in the process of converting my 6 years old C# application to .NET Core v3 and EF Core (and also using Blazor).
Most of it is working except for the Sharding part.
Our application creates a new database for each client. We use more or less this code for it: https://learn.microsoft.com/en-us/azure/sql-database/sql-database-elastic-scale-use-entity-framework-applications-visual-studio
I'm now trying to convert it to EF Core, but get stuck at this part:
// C'tor to deploy schema and migrations to a new shard
protected internal TenantContext(string connectionString)
: base(SetInitializerForConnection(connectionString))
{
}
// Only static methods are allowed in calls into base class c'tors
private static string SetInitializerForConnection(string connnectionString)
{
// We want existence checks so that the schema can get deployed
Database.SetInitializer<TenantContext<T>>(new CreateDatabaseIfNotExists<TenantContext<T>>());
return connnectionString;
}
// C'tor for data dependent routing. This call will open a validated connection routed to the proper
// shard by the shard map manager. Note that the base class c'tor call will fail for an open connection
// if migrations need to be done and SQL credentials are used. This is the reason for the
// separation of c'tors into the DDR case (this c'tor) and the internal c'tor for new shards.
public TenantContext(ShardMap shardMap, T shardingKey, string connectionStr)
: base(CreateDDRConnection(shardMap, shardingKey, connectionStr), true /* contextOwnsConnection */)
{
}
// Only static methods are allowed in calls into base class c'tors
private static DbConnection CreateDDRConnection(ShardMap shardMap, T shardingKey, string connectionStr)
{
// No initialization
Database.SetInitializer<TenantContext<T>>(null);
// Ask shard map to broker a validated connection for the given key
var conn = shardMap.OpenConnectionForKey<T>(shardingKey, connectionStr, ConnectionOptions.Validate);
return conn;
}
The above code doesn't compile because the Database object doesn't exist in this way in EF Core.
I assume I can simplify it using TenantContext.Database.EnsureCreated(); somewhere. But I can't figure out how to modify the methods, which to remove, which to change (and how).
Of course, I've been searching for an example using sharding and EF Core but couldn't find it.
Does anybody here has done this before in EF Core and is willing the share?
I'm specifically looking for what to put in startup.cs and how to create a new sharding/database when I create a new client.
In EF.Core just resolve the shard in OnConfiguring. EG
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var con = GetTenantConnection(this.tenantName);
optionsBuilder.UseSqlServer(con,o => o.UseRelationalNulls());
base.OnConfiguring(optionsBuilder);
}
Note that if you have a service or factory that returns open DbConnections, then you'll need to Close()/Dispose() them in the DbContext.Dispose(). If you get a connection string or a closed connection then DbContext will take care of closing the connection.
ASP.NET Core best-practices probably call for injecting an ITenantConfiguration service or somesuch in your DbContext. But the pattern is the same. Just save the injected service instance to a DbContext field and use it in OnConfiguring.
With the app that I'm working on, the desired shard is not discoverable until request time (for example, knowing what user is making the request, and then routing that user to their database). This meant that the OnConfiguring solution proposed above was not viable.
I worked around this by using IDbContextFactory<TContext>, and defining an extension on top of it, which sets the connection string based on whatever you want. I believe the database connection is created lazily in EF, and you are able to set the connection string up until the EF first needs to actually connect to the database.
In my case, it looked something like this:
var dbContext = _dbContextFactory.CreateDbContext();
var connectionString = $"DataSource={_sqlliteDirectory}/tenant_{tenant.TenantId}.db";
dbContext.Database.SetConnectionString(connectionString);
The downside is that it breaks the database abstraction (this code knows that my database is a local sqllite instance). An abstraction was not necessary in this layer of my app, but it is something very solvable if it's required.

2 MEF plugins using Entity Framework with different providers

I have a WPF application for which my users can create their own plugins by using MEF. Each plugin implements an interface that allows the main application to perform CRUD operations on some data source, e.g. a database.
I have created 2 plugins:
LocalDatabase - provides data from an SQLite database
RemoteDatabase - provides data from a MySQL database
Both are using Entity Framework to do their job. Each of those plugins needs to have its own implementation of the DbConfiguration class.
Now, the problem is that the WPF application loads those 2 plugins, but fails to assign each of them their own implementation of the DbConfiguration class, because it seems that you can have only one DbConfiguration per AppDomain.
So I always have only one of those plugins working.
I was thinking about having just one implementation of the DbConfiguration class and give each plugin an option to add its required configs to that, but the problem is that it creates some coupling between the WPF application and Entity Framework. I'd like to keep the Entity Framework stuff only inside the plugins without the need of modifying the WPF application. It shouldn't care about what plugins use to access their data source.
Is there any way of making it work this way? Could I maybe somehow create a separate AppDomain per each plugin, so maybe then each could use its own DbConfiguration class?
I've found a solution which is a bit hacky, but it does seem to work, so I thought I'd post it, in an unlikely case that someone would face the same issue somewhere in the future.
After some additional research, I've learnt that you can use the DbConfiguration.Loaded event to register some additional Dependency Resolvers for EF. So, in each plugin's constructor, I subscribe the event and add a new Dependency Resolver: SQLite for the LocalDatabase and MySql for the RemoteDatabase. I got rid of the custom DbConfiguration classes from each plugin.
This looked promising, but actually a new problem appeared - there were cases where LocalDatabase plugin called the MySql resolver and it actually returned the MySql implementation of the requested service type. Obviously the LocalDatabase plugin couldn't work with that, because it expected the SQLite implementation. And vice-versa.
So, each of the Resolvers, would actually need to check who called the GetService method - if it's some method from the same assembly that the custom resolver is in, it tries to resolve. Otherwise it's assumed that a resolver from different plugin should take care of that request and it returns null to actually let it do that.
The problem is that the GetService method doesn't supply any information about the requester. So that's where I came up with the hacky solution, which uses StackTrace to check whether any of the called methods belongs to the same Assembly that the current Resolver resides in.
public class CustomMySqlDbDependencyResolver : IDbDependencyResolver
{
private readonly Assembly _executingAssembly = Assembly.GetExecutingAssembly();
private readonly MySqlDependencyResolver _mySqlResolver = new MySqlDependencyResolver();
public object GetService(Type type, object key)
{
var stackTrace = new StackTrace();
StackFrame[] stackFrames = stackTrace.GetFrames().Skip(1).ToArray();
bool shouldResolve = stackFrames.Any(f => f.GetMethod().DeclaringType.Assembly.Equals(_executingAssembly));
if (!shouldResolve)
{
return null;
}
var resolvedService = _mySqlResolver.GetService(type, key);
return resolvedService;
}
public IEnumerable<object> GetServices(Type type, object key)
{
var service = GetService(type, key);
if (service != null)
{
yield return service;
}
}
}

How to let user dynamically specify database provider and connection details in C# EF4.1 Code First?

I am in the process of creating a C# app to replace a VB6 app that uses a MySQL database where multiple copies of the app use the same database. The new app must be able to use the current MySQL database but I would also like it to be database agnostic so future instances of the app can use whatever server the user wants. In a perfect world, I would like the app on first run to present the user with a dialog that lets them choose the database type (MySQL, SQL Server, etc...) and specify the server ip, user, password, and database name. The app would then connect to that server and either use the database if it is already there or create a new database if it isn't.
Using Code First I have gotten to the point where I understand how to use the existing database or create a new one but only by hard coding the connection string in the App.config file.
<add name="GumpIndexDatabase"
connectionString="server=localhost;userid=123;password=123;port=3306;database=gump_new_data;pooling=false;"
providerName="MySql.Data.MySqlClient"
/>
I can change the connection string and provider before launching the app and everything works as expected. I can also change the connection string after launch, but not the provider, and I have to know whether the provider is MySQL or MSSQL in order to get the connection string details correct (ex: user or userid)
class GumpIndexDatabase: DbContext
{
public GumpIndexDatabase(string connectionName)
: base(MakeConnectionString(connectionName))
{
}
private static string MakeConnectionString(string connectionName)
{
if (connectionName=MySQL) {
//return MySQL string
} else {
//return SQL Server string
}
}
Hours of searching have not turned up an example of how to do such a thing, so I'm suspecting it isn't allowed or recommended, even though it seems like such a simple thing. I have seen some articles on connection string builder but did not understand how to get a database specific string from the generic objects.
So the simple question: how to specify the database connection details at run time?
I wouldn't recommend enforcing this feature, unless you 100% positive you cannot live without it. It adds a lot of maintenance tasks, that may not be obvious just now (such as updating to newer versions, bug fixes, spending lots of time to figure out common interfaces, maintaining those interfaces, hell lot of testing, etc). So unless it is a requested business feature - forget about it.
However, given you know what you are doing, this problem is generally solved via interfaces. There may be these common interfaces (it's a task by itslef to figure out them):
IConnection
IDataProvider
IRepository<T>
At the moment you will implement interfaces using MySql database, such as class MySqlConnection : IConnection. If you need MS SQL, class MsSqlConnection : IConnection.
Effectively you must abstract all the functionality into common interfaces. You will have to provide implementations for each database/storage engine you want to support. At runtime, you will use IoC container and DI principle to set up the current implementation. All the child dependencies will use interfaces passed in as parameters to constructor (or properties or methods)
Did you try the Database property?
GumpIndexDatabase.Database.Connection.ConnectionString = "your conn string";
I just tested it very short, so no guarantee it works without problems. But I was successful using it in the contructor of one of my service layer classes:
public class MyService
{
protected DataContext DataContext { get; set; }
public MyService(DataContext dataContext)
{
DataContext = dataContext;
DataContext.Database.Connection.ConnectionString = "conn string";
}
}
Just saw that DbContext has an overload DbContext(string nameOrConnectionString). You should be able to use this too.
Using an existing connection
Or you use an existing connection. Your DbContext should have something like this:
public class DataContext : DbContext
{
public DataContext(DbConnection existingConnection)
: base(existingConnection, true) { }
}
And then initialize it whereever you need to:
public void SomeMethod()
{
var connString = "whatever"; // could also be something like Textbox1.Text
using (var connection = new SqlConnection(connString))
{
var context = new DataContext(connection);
}
}
Of course SqlConnection can be anything that inherits from DbConnection. See DbConnection Class.

Access session in class library.

I am developing an application architecture that uses 2 sub projects:
a) asp.net web application (it covers user interface and business logic) and
b) class library. (it covers data access layer)
After system user successfully logs in , the user information is stored in a session object.
The problem I am facing is when I try to access that session object in class library project(data access layer), it always returns null.
I need to access the session object in class library project because, in my case each user has their own username and password for database access(for security reasons);
So, How how do i read and write from/to session object in class library project
Use the System.Web.HttpContext.Current.Session object.
First of all, as Peri correctly noticed - you need to think again if having separate database logins for each user is a good idea - because you loose connection pooling (different users won't be able to reuse existing connections - and creating a new sql connection is quite expensive).
If you really wish to keep separate database users, I would create interface to abstract session from data access:
public interface ILoginDataService
{
LoginData Current { get; }
}
And implementation would pass login data from session. In such way you won't have session dependency to session in your data access logic - so it will be more testable, also you'll separate concerns.
Here is the code I used within a library to get session information.
public static string Entity()
{
string entity = "";
HttpContext httpContext = HttpContext.Current;
if (httpContext.ApplicationInstance.Session.Count > 0)
entity = httpContext.ApplicationInstance.Session["EntityCode"].ToString();
return entity;
}
I am having an ASP.Net application which uses session. I am able to access it in my app_code files using [WebMethod(EnableSession = true)] for the function. I am not sure whether this is your problem. I also faced session value as null when I removed (EnableSession = true) on the method.
using System.Web;
namespace ClassNameSpace
{
public class Class1 : IRequiresSessionState
{
private string sessionValue => HttpContext.Current.Session["sessionKey"].ToString();
}
}

Categories

Resources