I have an application that will generate reports from various databases, each with the same schema. The only difference between DB1, DB2, DB3, and DBx is the data. All tables, views, etc are the same in structure.
For each participating DB in my application I have created a distinct Linq to SQL DataContext.
I am creating a ReportHelper class that will generate reports from the underlying data. I want to be able to call a Method like "GetCustomerSales" and have it spit back the data for my report. The problem is that I want to pass or set the DataContext for the GetCustomerSales method before I call it (ideally when constructing the ReportHelper Class).
However, my GetCustomerSales method wants me to use a specific DataContext, and I do not want to create this method over and over for each potential DataContext in use in the app. What's the correct approach here?
Have a single data-context, that matches the common schema. The difference is that instead of using just new SomeDataContext(), you should supply an appropriate connection-string (or connection) to the constructor:
var db = new SomeDataContext(connectionString);
or
var db = new SomeDataContext(connection);
Now all you need is multiple connection-strings, which is easier than multiple data-contexts. Two choices there; you can store multiple strings, perhaps in config (this is especially useful if they each need different user accounts etc); or - you can use SqlConnectionStringBuilder to create the connection string at runtime, specifying the appropriate database.
To illustrate; every site in the StackExchange network is ultimately a different database, but they all use the same data-context type. We simply tell it about the connection at runtime (based on which hostname you accessed).
You can get around it by passing a connection string to your data context.
public class DataRepository
{
private MyDataContext ctx;
public DataRepository(string connection){
ctx = new MyDataContext(connection);
}
//now you can use your context
}
Related
Update: #StriplingWarrior's answer has set me on the right track, but I have new questions detailed in this post.
In my organization there are three different groups using three different databases. The schema of the databases are the same -- same table structure, relational logic, etc -- and the three groups need me to report on their data in my application in the same way.
I understand that in my application I need to create a separate data context for each database, and in my repositories I need to explicitly declare the data context(s) used by that repository. I'm fine with all of that.
But if all three repos need to run the same given method, just against different data contexts, do I really need to write the same method three times? Is there no way that I can put my method in a class library, say, or some other fourth file, and then run the method against the context I need to use as and when?
For example, my repo looks like this:
public interface IMyRepo
{
List<string> GetMyData();
}
public class MyRepo : IMyRepo
{
private readonly ContextA _context;
public MyRepo(ContextA context) {
_context = context;
}
public List<string> GetMyData()
{
//code to get my data and return it
}
}
Now remember that because of the way my org's database is setup, I need to write this repo three times, once for each group. All that changes is the context.
I'd like to take the actual code for method GetMyData() and move it to a fourth file, then just link my repo to that file and call the method through an object reference, passing the context as a parameter, say. So I could call GetMyData() from my repo above like this:
//method call in my context above
public List<string> GetMyData()
{
//call actual GetMyData() method in my linked file
return linkedFile.GetMyData(_context);
}
Is something even remotely like I describe possible?
EDIT: I was asked by #RStevoUK to provide a code example for the linked file I describe, so I'll try to explain here.
So in my context I have the method GetMyData(), which runs certain code and returns a list object. In the .NET Framework days, I could put the actual code for GetMyData() into a separate class file, and then pass in the ConnectionString object that the method would use to connect to the correct database and retrieve its data.
CLASS:
public class ExternalClassFile
{
public List<string> GetMyData(string connectionString)
{
using (SqlConnection connectionString)
{
//code to get my data and return it
}
}
}
Then, to run this method against a given database, all I would need to do is call an instance of the class, and invoke the method with the correct connection string:
ExternalClassFile ecf = new ExternalClassFile();
public List<string> CallSharedMethod()
{
string connection = \\connecting string code;
return ecf.GetMyData(connection);
}
I don't think I can do that with MVC; what I understand of it leads me to believe I can't. Also, I don't think Generics are what I want, since GetMyData() would contain an actual linq query
var data = (from d in _context.DatabaseTable_Model
select d).ToList();
for example. That "_context" variable references my datacontext, and that's all that changes.
I've tried to setup a "_context" variable using a "var" declaration instead of a specific context name, but MVC doesn't like that. Is there something else I can try that would make the above linq example reusable among multiple contexts?
I hope this makes more sense.
What I'm hearing is that the only difference between your three repositories would be the specific type of DbContext that gets injected into them. And the only reason you're stuck being tightly coupled to a given type is that you have code accessing a property on that type, like _context.DatabaseTable_Model.
You can typically rewrite your data access code to rely on DbSet<> instead of a specific DbContext type. And you can get a generic DbSet<> off of any DbContext by calling its DbContext.Set<>() method.
var baseQuery = var data = _context.Set<DatabaseTable_Model>();
var data = (from d in baseQuery
select d).ToList();
Then you can make your repo take any type of DbContext:
private readonly DbContext _context;
public MyRepo(DbContext context) {
_context = context;
}
However, it's worth challenging the assumption that you need three separate data context types in the first place. If the schemas of the databases are literally the same, then the only difference between these classes should be their name, right? And the only reason the name matters is because DbContext's default constructor uses naming conventions to decide how to find the connection string to use.
DbContext has other constructors, though, so if you make your context class use one of those other constructors it should be perfectly possible to construct separate instances of the same class pointing at different databases by passing in the name of the connection string (or the actual connection string, or the connection itself) that you want it to use.
Im new to C# and Entity Framework and I have a question about fields and initialization of a database class.
I have received some code in a program from my teacher that has a connection to a MySQL database through Entity Framework.
So far we have seen examples where inside methods for adding stuff to the database you first create an instance of it. An example of what we have seen so far:
using (var db = new p4_databaseEntities())
{
cp_car carEntity = new cp_car();
carEntity.make = car.make;
carEntity.model = car.model;
carEntity.year = car.year;
db.cp_car.Add(carEntity); //
db.SaveChanges();
MessageBox.Show("A Car has been added");
}
this.Close();
cp_car is a table in the database and a class in the program.
db is the current instance of the database.
Now, in the code I have received, this is not done this way. Its done in a different matter. Btw the program is a windows forms.
In the first form window, inside the constructor, he has created a new instance of the database and he calls upon a method called init
from the another class called LinqQueries. The code for that:
public Form1()
{
InitializeComponent();
p4_databaseEntities db = new p4_databaseEntities();
LinqQueries.Init(db);
this.CenterToScreen();
}
How the LinqQueries class looks:
private static p4_databaseEntities _db;
public static void Init(p4_databaseEntities db)
{
_db = db;
}
As I understand he created a new instance of the database in the constructor, where he also called on the init method. This method then defined the db object as _db. Now every single method he makes for adding or removing data from the database he is using _db and not db.
My question is does this mean that the init method assigns the static field of type p4_databaseEntities (name of database class) as an object? Is the value of the _db then an object? Is it a reference to an object? And also i noticed he uses the same field over and over again when making changes to the database which led me to believe it may be an active object that doesn’t die through out the programs lifespan?
If anyone could clarify this It would be greatly appreciated. Excuse any errors or wrong statements I have made please correct me if im wrong in any way. Im new to C# and Entity Framework.
Thanks beforehand
You are a bit inaccurate in your descriptions. This attributes to your confusion.
A DbContext is not a database, it represents the connection to the database. If you construct a DbContext object, you get access to the tables in the database via the Entities described in the DbSet.
Although it seems that a DbSet represents a table in the database, it does not. For instance an entity that you access via a DbSet can have an ICollection as member, which contains items that are not part of the table, but are items in a different table. Accessing the items in this ICollection causes an SQL Join to be performed, without you having to type the join
Hence a DbSet object in the DbConnection does not represent database table, it represents the access to the properties that can be accessed using the DbSet object, inclusive the properties of all objects in the database that are in tables that have a relation with the DbSet object.
Your first code with the using statement is the normal way entity framework should be used.
To hide the design of the database, quite often a separate class is introduced that is the only one that should use the DbContext. All users of the database should communicate using the seperate class.
This allows changing the internals of the database without having to change the code that uses the database.
This is what probably was meant as the purpose of the LinqQueries class. Instead of calling functions of the DbContext directly, users should call the (probably static) functions of LinqQueries. This way the internal structure of the database can change without having to change the callers of the LinqQueries functions.
What in fact is happening is that LinqQueries is meant to communicate with only one DbContext. LinqQueries does not decide which DbContext is used. Proper functioning depends heavily on exactly one user of LinqQueries who should create a DbContext and Init LinqQueries. This one user should also know when no one needs the LinqQueries anymore, because he has to Dispose() the DbContext.
This design if fairly error prone. Supposing the designer makes very grood products, his products will be used by a lot of users (meaning software, not operators). How do you assert that exactly one user will call the Init function?
If you really want that all users use the same Dbcontext, why not let the constructor of LinqQueries create this DbContext. In fact the design is similar to the singleton design pattern, so why not create LinqQueries as a singleton.
The limitation that all users of LinqQuery should use the same one and only DbContext limits the use of the LinqQuery class unnecessarily.
If users of the LinqQuery class could pass a DbContext to the constructor, then users could decide which database should be used by this particular LinqQueries object. This is quite handy when creating unit tests: instead of using the original database, the testable code could be used with a database with specific test values.
All in all the goal of the designer is unclear. IMHO it is a bad design and you are right that it is not clear what happens.
class LinqQueries
{
// default constructor: use the default DbContext
public LinqQueries()
{
this.DbContext = new p4_databaseEntities();
}
// special constructor: user provided dbContext
public LinqQueries(p4_databaseEntities dbContext)
{
this.dbContext = dbContext;
}
private readonly p4_databaseEntities dbContext;
public IEnumerable<...> GetMyElements(...)
{
return this.dbContext....;
}
}
This way, every creator of the LinqQueries would exactly know what to do either use the default p4_databaseEntities or create your own Dbcontext and dispose it when not needed anymore
using (var mydbContext = new p4_databaseEntities())
{
LinqQueries linqQueries = new LinqQueries(myDbContext);
// note that this constructor is very lightWeight!
var result = linqQueries.MyQeury(...);
}
This method is really safe. Any errors made by me, does not influence the code of any other user of the LinqQuery class.
I am trying to create driver for linqpad and have question:
When I am creating DynamicDataContextDriver, I must create class TypedDataContext.
What I should put in it?
How will it be populated?
Can I control how will it be populated?
If I use object database here, is there something that I must bear in mind?
I found some answer here, but I can not find there all the above answers.
A typed data context is simply a class with properties/fields suitable for querying. Those properties/fields will typically return IEnumerables or IQueryables. For example:
public class TypedDataContext
{
public IEnumerable<Customer> Customers { get { ... } }
public IEnumerable<Order> Orders { get { ... } }
...
}
When you use Visual Studio to create a new item of kind "LINQ to SQL classes" or "ADO.NET Entity Data Model", Visual Studio creates a typed data context for you which is an excellent example of what LINQPad expects. A typed data context can also expose methods (e.g., to map stored procedures or functions) - in fact it can expose anything that makes sense to the end user.
When you execute a query in LINQPad that has a connection, LINQPad subclasses the typed data context associated with the connection so that the query has access to all of its fields/properties. This is why Customers.Dump() is a valid query - we can just access Customers without having to instantiate the typed data context first.
A LINQPad driver can work in one of two ways. Either it can act like Visual Studio and build the typed data context automatically and on the fly (dynamic data context driver), or it can extract a typed data context from an existing assembly provided by the user (static data context driver). When you add a connection in LINQPad, you'll notice that the drivers are listed in two list boxes (Build data context automatically = dynamic driver, and Use a typed data context from your own assembly = static driver).
The typed data context is instantiated whenever a query executes. Because its properties typically return lazily evaluated IEnumerables/IQueryables, it's not helpful to think of "populating" it. However, it will need to know how to access an underlying data source, and this is done by passing arguments into the constructor.
LINQPad normally keeps the query's application domain alive between query runs, and this might be useful with caching and optimization should you be writing a driver for an object database. Other than that, there shouldn't be any special considerations for object databases.
I have a number of static methods that perform simple operations like insert or delete a record. All these methods follow this template of using:
public static UserDataModel FromEmail(string email)
{
using (var db = new MyWebAppDataContext())
{
db.ObjectTrackingEnabled = false;
return (from u in db.UserDataModels
where u.Email == email
select u).Single();
}
}
I also have a few methods that need to perform multiple operations that use a DataContext:
public static UserPreferencesDataModel Preferences(string email)
{
return UserDataModel.Preferences(UserDataModel.FromEmail(email));
}
private static UserPreferencesViewModel Preferences(UserDataModel user)
{
using(var db = new MyWebAppDataContext())
{
var preferences = (from u in db.UserDataModels
where u == user
select u.Preferences).Single();
return new UserPreferencesViewModel(preferences);
}
}
I like that I can divide simple operations into faux-stored procedures in my data models with static methods like FromEmail(), but I'm concerned about the cost of having Preferences() invoking two connections (right?) via the two using DataContext statements.
Do I need to be? Is what I'm doing less efficient than using a single using(var db = new MyWebAppDataContext()) statement?
If you examine those "two" operations, you might see that they could be performed in 1 database roundtrip. Minimizing database roundtrips is a major performance objective (second to minimizing database io).
If you have multiple datacontexts, they view the same record differently. Normally, ObjectTracking requires that the same instance is always used to represent a single record. If you have 2 DataContexts, they each do their own object tracking on their own instances.
Suppose the record changes between DC1 observing it and and DC2 observing it. In this case, the record will not only have 2 different instances, but those different instances will have different values. It can be very challenging to express business logic against such a moving target.
You should definately retire the DataContext after the UnitOfWork, to protect yourself from stale instances of records.
Normally you should use one context for one logical unit of work. So have a look at the unit of work pattern, ex. http://dotnet.dzone.com/news/using-unit-work-pattern-entity
Of cause there is some overhead in creating a new DataContext each time. But its a good practice to do as Ludwig stated: One context per unit of work.
Its using connection pooling so its not a too expensive operation.
I also think creating a new DataContext each time is the correct way but this link explains different approaches for handling the data context. Linq to SQL DataContext Lifetime Management
I developed a wrapper component that uses an interface like:
public interface IContextCacher {
DataContext GetFromCache();
void SaveToCache(DataContext ctx);
}
And use a wrapper to instantiate the context; if it exists in cache, it's pulled from there, otherwise, a new instance is created and pushed to the Save method, and all future implementations would get the value from the getter.
Depending on the type of application would be the actual caching mechanism. Say for instance, an ASP.NET web application. This could store the context in the items collection, so its alive for the request only. For a windows app, it could pull it from some singleton collection. It could be whatever you wanted under the scenes.
I've several databases that contains the exact same base tables with identical design. Now I need to be able to access a base table from any one of those database.
Is there any way to create a common interface that can still utilize the power of Linq2Sql? I was thinking that I would have a factory that created a data context for that chosen database which I could afterwards query:
string university = "SomeUniversity";
var database = UniversityDataContextFactory.Get(university);
var user = database.Users.Where(u => u.Id== id).First();
This, however, would require the data contexts returned from the individual databases to implement a shared interface and very possibly also to share data classes.
If the database schemas are identical then you only need one set of data classes - the only difference between one "data context" and another will be the connection string.
This is where things get a bit more fun. If you are explicitly creating the data context - as in your factory example - then there's not an issue so long as you have a means to provide/create the appropriate connection sting as there is a constructor for a data context that takes a connection string as a parameter. However if you're implicitly creating one (in back of something like dynamic data) then I'm not sure what you'd need to do (I've dealt with this on a per application instance basis but not for multiple connections in a single application).
The key to remember is that the data connection string in your .config is the default but not necessarily the only connection string that you can use with a data context.