Can I reuse a method between two different data contexts in MVC? - c#

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.

Related

Database Class Instance, Fields, Entity Framework

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.

Entity Framework Code First returning same data for different tables mapped to same class

I have a class Timer which I want to use with different tables that have the same structure so I am passing in the table name.
public class TimerContext : DbContext
{
public DbSet<Timer> Timers { get; set; }
private readonly string _tableName;
public TimerContext(string tableName) : base("name=fooDb")
{
_tableName = tableName;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Timer>().ToTable(_tableName);
base.OnModelCreating(modelBuilder);
}
}
However when I pass in two different table names they return the same data. prevTimers contains the exact same data as currTimers. How do I get the unique data from each table? Why do I get the same data for two different tables?
var currTimers = new TimerContext(currentTimerTableName).Timers.ToList();
var prevTimers = new TimerContext(previousTimerTableName).Timers.ToList();
EF will call the OnModelCreating method to create an in-memory copy of the model as soon as it is needed. Once it has done this, this copy will be used. In your case, the code to generate prevTimers uses this in-memory model which is mapped to the current timers table. If you place a breakpoint on the OnModelCreating method, you should see that it is only called one time.
All that said, it is possible to dig deep and interrogate the in-memory model (you have to use the old school context type, ObjectContext vs. DbContext). Using some code from Rowan Miller here, you can find which table is mapped to each entity set. Using this code, each item in the tables variable has a read / write Table property that contains the database table name. I haven't tried setting this but it certainly seems plausible. Of course, you would need to alter the model somewhere else outside of the OnModelCreating method, say in the constructor, so that the code will fire each time an instance of the context is created.
* UPDATE *
Because I am always interested in learning new things, I couldn't leave this alone and threw together a test application. Unfortunately, it looks like you cannot set the property (despite it being a read / write property) as it throws an InvalidOperationException stating that the item is read-only. Perhaps there is another way but I have not found it...yet.
* UPDATE *
The solution is actually much simpler that what I had first mentioned. A couple of the constructors of the DbContext class accepts an instance of the DbCompiledModel class as one of its parameters. Using the DbModelBuilder class, you can build the same code that you would normally put in the OnModelCreating method. You can call the Build method of this class to create an instance of the DbModel class. You can call the Compile method of this class to create an instance of the DbCompiledModel class. The only real trick is that the Build method requires some additional information (I used an instance of the DbProviderInfo class but I think you could also use an actual connection but that would probably incur a hit to the database). I have tested this and this one does indeed work as desired.
Something like...
DbModelBuilder builder = null;
builder = new DbModelBuilder();
builder.Entity<TestEntity>().ToTable(tableName);
DbModel model1 = null;
model1 = builder.Build(new DbProviderInfo("System.Data.SqlClient", "2012"));
builder.Entity<TestEntity>().ToTable(anotherTableName);
DbModel model2 = null;
model2 = builder.Build(new DbProviderInfo("System.Data.SqlClient", "2012"));
DbCompiledModel compiledModel1 = null;
DbCompiledModel compiledModel2 = null;
compiledMdoel1 = model1.Compile();
compiledMdoel2 = model2.Compile();
TestContext context1 = null;
TestContext context2 = null;
context1 = new TestContext(compiledModel1);
context2 = new TestContext(compiledModel2);
Of course, the constructor of the TestContext class would have to pass the compiled model instance to the base constructor.
cannot add comment, but I just want to add one thing that happened to me
my code was like this:
DbModelBuilder builder = new DbModelBuilder();
this.OnModelCreating(builder);
var model = builder.Build(this.Database.Connection);
I was thought that when i pass the current DbConnection object to this method, it will somehow "inherit" of all connection settings, but seems I was wrong.
After debugging a while, i just realize it generate some weird connection string for me, which always results in cannot find database issue.
So my solution is, when instantiate the "TestContext" (as in Jason Richmeier's answer), pass nameOrConnectionString as first parameter and the compiled model as second, and it solved my issue.
I wonder, since EF keep a in-memory copy of certain model, is manually create another one will just create a new copy in memory? And if my code need to do this a lot of times, it is going to keep creating new models in memory and finally ends with a memory overflow?

Create a C# class that allows for a variable datacontext

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
}

LINQ to SQL: Reusing DataContext

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.

Problem using LINQ to SQL with one DataContext per atomic action

I have started using Linq to SQL in a (bit DDD like) system which looks (overly simplified) like this:
public class SomeEntity // Imagine this is a fully mapped linq2sql class.
{
public Guid SomeEntityId { get; set; }
public AnotherEntity Relation { get; set; }
}
public class AnotherEntity // Imagine this is a fully mapped linq2sql class.
{
public Guid AnotherEntityId { get; set; }
}
public interface IRepository<TId, TEntity>
{
Entity Get(TId id);
}
public class SomeEntityRepository : IRepository<Guid, SomeEntity>
{
public SomeEntity Get(Guid id)
{
SomeEntity someEntity = null;
using (DataContext context = new DataContext())
{
someEntity = (
from e in context.SomeEntity
where e.SomeEntityId == id
select e).SingleOrDefault<SomeEntity>();
}
return someEntity;
}
}
Now, I got a problem. When I try to use SomeEntityRepository like this
public static class Program
{
public static void Main(string[] args)
{
IRepository<Guid, SomeEntity> someEntityRepository = new SomeEntityRepository();
SomeEntity someEntity = someEntityRepository.Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"));
Console.WriteLine(someEntity.SomeEntityId);
Console.WriteLine(someEntity.Relation.AnotherEntityId);
}
}
everything works nicely until the program gets to the last WriteLine, because it throws an ObjectDisposedException, because the DataContext does not exist any more.
I do see the actual problem, but how do I solve this? I guess there are several solutions, but none of those I have thought of to date would be good in my situation.
Get away from the repository pattern and using a new DataContext for each atomic part of work.
I really would not want to do this. A reason is that I do not want to be the applications to be aware of the repository. Another one is that I do not think making linq2sql stuff COM visible would be good.
Also, I think that doing context.SubmitChanges() would probably commit much more than I intended to.
Specifying DataLoadOptions to fetch related elements.
As I want my Business Logic Layer to just reply with some entities in some cases, I do not know which sub-properties they need to use.
Disabling lazy loading/delayed loading for all properties.
Not an option, because there are quite a few tables and they are heavily linked. This could cause a lot of unnecessary traffic and database load.
Some post on the internet said that using .Single() should help.
Apparently it does not ...
Is there any way to solve this misery?
BTW: We decided to use Linq t0 SQL because it is a relatively lightweight ORM solution and included with the .NET framework and Visual Studio. If the .NET Entity Framework would fit better in this pattern, it may be an option to switch to it. (We are not that far in the implementation, yet.)
Rick Strahl has a nice article about DataContext lifecycle management here: http://www.west-wind.com/weblog/posts/246222.aspx.
Basically, the atomic action approach is nice in theory but you're going to need to keep your DataContext around to be able to track changes (and fetch children) in your data objects.
See also: Multiple/single instance of Linq to SQL DataContext and LINQ to SQL - where does your DataContext live?.
You have to either:
1) Leave the context open because you haven't fully decided what data will be used yet (aka, Lazy Loading).
or 2) Pull more data on the initial load if you know you will need that other property.
Explaination of the latter: here
I'm not sure you have to abandon Repository if you go with atomic units of work. I use both, though I admit to throwing out the optimistic concurrency checks since they don't work out in layers anyway (without using a timestamp or some other required convention). What I end up with is a repository that uses a DataContext and throws it away when it's done.
This is part of an unrelated Silverlight example, but the first three parts show how I'm using a Repository pattern with a throwaway LINQ to SQL context, FWIW: http://www.dimebrain.com/2008/09/linq-wcf-silver.html
Specifying DataLoadOptions to fetch related elements. As I want my Business Logic Layer to just reply with some entities in some cases, I do not know which sub-properties they need to use.
If the caller is granted the coupling necessary to use the .Relation property, then the caller might as well specify the DataLoadOptions.
DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Entity>(e => e.Relation);
SomeEntity someEntity = someEntityRepository
.Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"),
loadOptions);
//
using (DataContext context = new DataContext())
{
context.LoadOptions = loadOptions;
This is what I do, and so far it's worked really well.
1) Make the DataContext a member variable in your repository. Yes, this means you're repository should now implement IDisposable and not be left open... maybe something you want to avoid having to do, but I haven't found it to be inconvenient.
2) Add some methods to your repository like this:
public SomeEntityRepository WithSomethingElseTheCallerMightNeed()
{
dlo.LoadWith<SomeEntity>(se => se.RelatedEntities);
return this; //so you can do method chaining
}
Then, your caller looks like this:
SomeEntity someEntity = someEntityRepository.WithSomethingElseTheCallerMightNeed().Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"));
You just need to make sure that when your repository hits the db, it uses the data load options specified in those helper methods... in my case "dlo" is kept as a member variable, and then set right before hitting the db.

Categories