Modifying connection string of an ASP.NET GridView -> BLL -> DAL at runtime - c#

I am constructing a page to display a GridView of client data with pagination. My aspx page has a GridView whos DataSourceID is set to an ObjectDataSource. The ObjectDataSource is bound to a a BLL which in turn accesses the data through a DAL. I have the whole thing up and running while pointing to a static database. However, each client's data is stored in its own database. The next step is to modify the ConnectionString of the DAL depending on client login.
I have configured the DAL TableAdapter with the option ConnectionModifier set to 'Public'. My BLL can modify the connection string of the DAL, however I do not know how to pass to the BLL the client database name.
public class PDFDocumentsBLL {
private PDFTableAdapter _pdfdocumentsadapter = null;
protected PDFTableAdapter Adapter {
get {
if ( _pdfdocumentsadapter == null ) {
_pdfdocumentsadapter = new PDFTableAdapter();
_pdfdocumentsadapter.Connection = new System.Data.SqlClient.SqlConnection(
ConfigurationManager.ConnectionStrings["template"].ConnectionString.Replace( "TEMPLATE", "TESTCLIENT" )
);
}
return _pdfdocumentsadapter;
}
}
...
}
I would like to replace the string "TESTCLIENT" in the above code with a variable, but I am at a loss on how to pass this information to the BLL.

You may create some kind of database name provider that will return database name based on username, like
public class DataBaseNameProvider
{
public string GetDataBaseName()
{
var userName = Membership.GetUser().UserName;
return GetDatabaseNameByUserName(userName);
}
}
And call that class from your BLL.
If you don't like idea to use ASP.NET stuff in your BLL because you don't want to add additional dependency, you still can create a wrapper around your BLL that will be Membership aware and will create your BLL passing username there.

If you are using Windows Authentication, then you can simply use
ConfigurationManager.ConnectionStrings[WindowsIdentity.GetCurrent().Name]
And might be good practice to retrieve the whole connection string for each user, it makes it more flexible, so you could use a completely different type of database if needed.

What I ended up doing is adding a PDFDB property to my BLL:
public class PDFDocumentsBLL {
private PDFTableAdapter _pdfdocumentsadapter = null;
public string PDFDB = "PDF_TEMPLATE";
protected PDFTableAdapter Adapter {
get {
if ( _pdfdocumentsadapter == null ) {
_pdfdocumentsadapter = new PDFTableAdapter();
_pdfdocumentsadapter.Connection = new System.Data.SqlClient.SqlConnection(
ConfigurationManager.ConnectionStrings["pdf"].ConnectionString.Replace( "PDF_TEMPLATE", PDFDB )
);
}
return _pdfdocumentsadapter;
}
}
}
I then modified the GetBy/FillBy functions to take the DB as an additional parameter, and configured the ObjectDataSource to pass that value in from the Session variable.

Related

Using Core's DI Singleton as a memory data provider?

I need to get a list of stations(lazily) from a remote service and to store it in DB and in memory.
Then, if someone wants the list, he tries to read the in memory list.
If it's null , it goes to Db and if there's no data in DB , it should go to a remote service , fetch the list , and to store the list in DB and in memory.
(Basically, I don't want to go to the remote service every request)
So I've used the .net core's singleton service :
services.AddSingleton<IStations,Stations>()
Where the class will contain the list itself :
public class Stations:IStations
{
public List<StationModel> LstStationModel
{
get
{
lock (locker)
{
if (_lstStationModel == null)
{
var ls = GetStationsFromDb().Result;
if (ls!=null && ls.Count > 0)
_lstStationModel = ls;
else
_lstStationModel = GetStationsFromProvider().Result;
}
return _lstStationModel;
}
}
set
{
lock (locker)
{
_lstStationModel = value;
}
}
}
}
So now I have a single property in a singleton class .
When someone asks for the list , I check if it's null , then I go to db.
If the db doesn't have the data , I start fetching from remote and fill the list.
I've also added lock , so that 2 requests won't invoke fetching twice.
Question
Something here doesn't look right.I'm not sure it's the right way of dong it. And besides , I really don't like this solution.
Is there any way to do it in a more elegant/better way ?
You could use Lazy for that intent. If you pass true in the constructor it indicates it is thread-safe.
Example:
public class Stations : IStations
{
Lazy<List<StationModel>> _lazyStation = new Lazy<List<StationModel>>(() => Provider.GetStationsFromProvider().Result, isThreadSafe: true);
public List<StationModel> LstStationModel
{
get { return _lazyStation.Value; }
set { _lazyStation = new Lazy<List<StationModel>>(() => value, isThreadSafe: true); }
}
}
Note that this wouldn't be the best solution for caching or accessing data because you are coupling your Provider to the Stations object. I'd recommend creating a service/class and inject your "Provider" (as an abstraction) into it in order to invert the dependency (Dependency Inversion Principle). If you will create a Service/Manager class you could even inject a ICacheService and implement it using MemoryCache

3 layer architecture in a Winform App

I'm trying to implement this architecture for the first time in a Winform. So I have a simple but very important question for me.
Take a simple example. I want the form to retrieve a user list and to allow a modification of the phone number.
I have this for the first step (simplified and I normally use interfaces)
public Form1()
{
InitializeComponent();
UserService _userService = new UserService();
listBoxUsers.DataSource = _userService.GetAllUsers();
}
class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}
class UserService
{
UserRepository _userRepository=new UserRepository();
public Dictionary<int, string> GetAllUsers()
{
DataTable dtbl= _userRepository.AllUsers();
//Some code here
return dict;
}
}
class UserRepository
{
public DataTable AllUsers()
{
//Sql query
return dtbl;
}
}
Now by selecting a user in the lisbox, I'm able to display some information as the Phone number. When I'm changing the phone number, I need a method called UpdatePhoneNumber to update the SQL database.
But, where to place it? User or UserService (I don't talk about the SQL query, just the logic)
And after that, how to access (here or somewhere else in the app) to this user property to display it in the form? Directly with _user.Id (User must be instantiated in the form) or implement a _userService.The id which retrieves User.ID (in this case Form knows only UserService class).
Many thanks for your precious help
Put all Methods working on the User's data in the user class. Ask yourself the question what the user can do? Put all the logic which controlls the users in UserService like GetUserById, GetAllUsers, CreateUser and so..
Put all the method which the user can perform in the User class.
Or lately i was building such kind of thing and i merged User and UserServices into one and made the UserServices class method static so i can access them without instantion of the User.
Hope this help.
Here is what your basic 3-layered app looks like.
UI (your form and ui supporting objects)
BLL (GetAllUsers, SaveUser, DeleteUser, etc)
Data (ADO, EF, etc)
In your particular case, you really looking for Master-detail concept. A master usually the one where you display list of users
// Master
var _userList = Service.GetAllUsers(); // List<UserModel>
userGrid.Datasource = _userList;
I will not discuss it here but you can set bindings so that click on grid will result in detail control being populated. Or manually
// detail
UserModel model = master._userList[currIndex];
txtFirstName.Text = model.FirstName;
txtPhone.Text = model.Phone;
// . . . .
Now, of course, you're about to change the text box and save user...
// detail
UserModel model = master._userList[currIndex];
Service.SaveUser(model);
Master.Reload();
This is general idea, how this is done. If you follow, you have distinct layers. UI calls Service, which calls Data. For example, you have BLL
// Service
private IUserDataProvider _provider;
public List<UserModel> GetAllUsers()
{
var data = _provider.Get<User>();
// massage your 'data' and return List<UserModel>
. . . .
}
your provider might return some unwanted data, so you can use BLL to trim it and return only appropriate data. But you don't know what provider is doing inside. May be it is doing Ado.net or Entity Framework. Hence a true separation of layers.

ASP.NET - Can I use or select different DBs in an entity?

I'm fairly new in Entity Framework and web API. I'm trying to GET data from a table. But the table is going to be selected from different databases. So, there's multiple databases with the same tables and I need to select which database (I'm thinking [Fromuri]) I need to get the tables from. Right now I only have one database connected. Not sure if I need to add it all as a connection string or is there an easier way.
public IHttpActionResult Get()
{
using (var MGC = new GC_BranchNameEntities())
{
var serializer = new JsonSerializer();
var jsonIDSA = JsonConvert.SerializeObject(MGC.INV_LIVE_IDSA, Formatting.None);
try
{
return Ok(jsonIDSA);
}
catch (Exception e)
{
return BadRequest("Error occured when retreiving IDSA data " + e.Message);
}
}
}
Can I do something like using(var MGC = new SelectdatabaseEntitiesusingParam) something like that?
There is a DbContext constructor overload that takes a connection string or name of a connection string as a parameter, and others that accept an existing DbConnection object.
Update:
Add a new constructor to your DbContext derived class, passing the parameter to the base constructor:
public GC_BranchNameEntities( string nameOrConnString ) :
base( nameOrConnString )
{
}
Then, when instantiating your DbContext, pass in the name of the connection string to use (one defined in your config file) or an actual connection string. Alternatively, you can also create a constructor overload that accepts an existing DbConnection object (similarly to the nameOrConnString parameter) and pass that to the base constructor.

How to combine database access and cache in asp.net mvc (An object reference is required for the non-static field, method, or property 'Module.dbApp')

This is actually 2 questions in one.
I have an asp.net mvc application where I have to load a list of Modules, its just a simple list with ID, modulename and a class name to render it on the view with font awesome.
My model is like this:
public class Module
{
[Key]
public int Id { get; set; }
public string ModuleName { get; set; }
public string FontAwesomeClass { get; set; }
}
Because the module list is a Partial View that will render some icons on the top navigation bar, I dont want that for each refresh of the app, it goes to the DB, so it must be cached(I am using Azure REDIS Cache, not relevant for the question anyway), so instead of calling the DB context directly from the controller, I am calling a Cache Class that will check if the cache object exists, if not it will retrieve it from DB, if it does, it will return it from cache.
This my solution structure:
http://screencast.com/t/uayPYiHaPCav
Here is my controller Module.cs
public ActionResult GetModules()
{
return View(Cache.Module.GetModules());
}
As you can see the Controller does not have any logic where to get the data from.
Here is the Module.cs (on the Cache Namespace)
public class Module
{
private AppDataContext dbApp = new AppDataContext();
//Load modules from cache or from database
public static List<Models.Module> GetModules()
{
IDatabase cache = Helper.Connection.GetDatabase();
List<Models.Module> listOfModules = (List<Models.Module>)cache.Get("Modules");
if (listOfModules == null)
{
return dbApp.ModuleList.ToList();
}
else
{
return listOfModules;
}
}
}
Here I have a compiler error which I am not sure how to best fix it:
Error CS0120 An object reference is required for the non-static field,
method, or property 'Module.dbApp'
So that was my first question.
The 2nd question is more about the design pattern, do you consider this correct or not? the way I am trying to get the data from Cache, and its actually the Cache class which checks if data is on it or if it has to go to the DB.
First Question: make your private member static
private static AppDataContext dbApp = new AppDataContext();
2nd Question: your cache strategy seems pretty standard. The only thing is that you might want to expire cache data. For example, the cached data can get old and the longer it stays in the cache the older it gets. You might at some point want to expire it and get fresh data again.
Update:
#EstebanV for code sample (this off the top of my head, don't assume that it compiles):
/**
ICachedPersonDao abstracts away the caching mechanism
away from the core of your application
**/
public CachedPersonDao : ICachedPersonDao
{
private IPersonDao personDao = null;
public CachedPersonDao(IPersonDao personDao)
{
this.personDao = personDao;
}
public Person GetPersonById(int id){
bool isInCache = CACHE.SomeFunctionThatChecksInYourCache(id);
if (isInCache)
{
return CACHE.SomeFunctionThatReturnsTheCachedPerson(id);
}
else
{
//Well it's not in the cache so let's get it from the DB.
return this.personDao.GetPersonById(id);
}
}
}
/**
IPersonDao abstracts database communication
away from the core of your application
**/
public class PersonDao : IPersonDao
{
public Person GetPersonById(int id)
{
/** Get the person by id from the DB
through EntityFramework or whatever
**/
}
}
Usage:
In your controller, use ICachedPersonDao if you want to attempt to get from cache or use IPersonDao if you want to get it directly from the database without checking the cache.
Like I said, you should learn Dependency Injection it will help "inject" these dependencies into the classes that uses them.
I say again, this is off the top of my head. It won't compile. It's just to illustrate the concept.

PetaPoco (DotNetNuke) and SQL - Adding an object with a List property or other composite object

I'm using a Data Access Layer based on PetaPoco (DotNetNuke 7.0). I've used it successfully when working with relatively simple objects but I now have to insert an object which contains at least one property which is a List of other objects.
For example:
class Person
{
public Person(){}
public string name { get; set; }
public List<Address> addresses { get; set; }
}
class Address
{
...
}
The actual object I'm working with is much more complex than the example above - there are at least four composite List objects in the object to be inserted into the repository.
What I'd like to be able to do is define the table in SQL and to be able to make a simple call to PetaPoco like this:
public static void AddOrder(Person person)
{
using (IDataContext context = DataContext.Instance())
{
var repository = context.GetRepository<Person>();
repository.Insert(person);
}
}
The background to this is that the object is passed in to a web service from a Knockout/jQuery front-end so a JSON string is converted to a data object which must then be stored on the database.
I think there are three questions really:
How do I write the SQL table which represents Person and the contained Addresses List?
How do I write the necessary PetaPoco code to insert the Person object together with any objects it contains?
Should I forget about trying to store the object on the database and just store the JSON string on the database instead?
Thanks for looking :)
I haven't installed DotNetNuke 7 yet, however I examined the source code at codeplex and I think you can do it this way:
public static void AddOrder(Person person)
{
using (IDataContext context = DataContext.Instance())
{
var repositoryPerson = context.GetRepository<Person>();
var repositoryAddrress = context.GetRepository<Address>();
context.BeginTransaction();
try
{
repositoryPerson.Insert(person);
foreach(var address in person.addresses)
{
repositoryAddress.Insert(address);
}
context.Commit();
}
catch (Exception)
{
context.RollbackTransaction();
throw;
}
}
}
I haven't tested it so I can't guarantee it works, however this seems right to me.

Categories

Resources