I have a very simple table and I can happily query it using LINQ To SQL, but when I do any save/updates the GetChangeSet method on my datacontext is always empty.
My code is very simple (concatenated code from various classes):
public static EntitiesDataContext EntitiesContext
{
get { return new EntitiesDataContext("Data Source=ANTSLAPTOP\\sqlexpress;Initial Catalog=nmncouk_d000;Integrated Security=True"); }
}
public static void Save(Price price)
{
EntitiesContext.Prices.InsertOnSubmit(price);
EntitiesContext.SubmitChanges();
}
[Test]
public void SavePrice()
{
Price price = new Price();
price.Lower = 1;
price.Upper = 2;
price.PricePerDay = 10;
Price.Save(price);
Assert.AreNotEqual(price.PriceId, 0);
}
The problem is with your EntitiesDataContext method. Although it's static, it's returning a new DataContext on each call. So you are calling insertonsubmit and submitchanges against different contexts.
Adjust your save method to use the same context and all should be well.
Related
When I save changes to DbContext, it doesn't update my database. No errors either.
Yes, the incoming form data is filled. Yes, the connection string is correct, I know this because I'm able to retrieve data from the database perfectly fine. If it's of relevance, it's a many-to-many relationship.
A roadmap is like an article as far as you're concerned which can be associated with many tags.
public static class RoadmapService
{
static ConkerDbEntities dbContext;
public static void createDbContext(ConkerDbEntities _dbContext)
{
dbContext = _dbContext;
}
public static void addToDatabase(Form form)
{
Roadmaps roadmap = new Roadmaps { RoadmapTitle = form.title,
RoadmapSummary = form.summary,
RoadmapBody = form.body };
var tags = new Tags[form.tags.Length];
for(int i = 0; i < tags.Length; i++)
{
tags[i] = new Tags();
tags[i].TagId = form.tags[i];
}
var roadmapTags = new RoadmapTags[form.tags.Length];
for(int i = 0; i < tags.Length; i++)
{
roadmapTags[i] = new RoadmapTags{Roadmap = roadmap, Tag = tags[i]};
}
dbContext.AddRange(roadmapTags);
dbContext.SaveChangesAsync();
}
}
}
Where dbcontext is created, this is the constructor of Startup.cs
public static SearchEngine engine;
public static ConkerDbEntities dbContext;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
dbContext = new ConkerDbEntities();
RoadmapService.createDbContext(dbContext);
engine = new SearchEngine(dbContext);
}
I get no errors, and there's nothing added to the database. I'm probably doing something fundamentally wrong here. Thanks in advance.
You are using async programming, but your method is not async.
use dbContext.SaveChangesAsync(); when you have async methods, use dbContext.SaveChanges(); when is not async.
Also, you are using static classes, this should not be the case if you are using dependency injection. DI will handle the life cycle of your objects.
Your class is not defined as should be, you have some format errors, and should look something like this:
public class RoadmapService
{
private readonly ConkerDbEntities _dbContext;
public RoadmapService(ConkerDbEntities dbContext)
{
_dbContext = dbContext;
}
public async Task AddToDatabase(Form form)
{
Roadmaps roadmap = new Roadmaps {
RoadmapTitle = form.title,
RoadmapSummary = form.summary,
RoadmapBody = form.body
};
var tags = new Tags[form.tags.Length];
for(int i = 0; i < tags.Length; i++)
{
tags[i] = new Tags();
tags[i].TagId = form.tags[i];
}
var roadmapTags = new RoadmapTags[form.tags.Length];
for(int i = 0; i < tags.Length; i++)
{
roadmapTags[i] = new RoadmapTags{Roadmap = roadmap, Tag = tags[i]};
}
_dbContext.AddRange(roadmapTags);
_dbContext.SaveChangesAsync();
}
}
Then in your controller you can use your service as is
public class OrdersController : Controller
{
private readonly RoadmapService _roadmapService;
public OrdersController(RoadmapService roadmapService)
{
_roadmapService = roadmapService;
}
[HttpGet]
[Route("api/[controller]/{folio}")]
public async Task<IActionResult> Status(string folio)
{
await _roadmapService.AddToDatabase(something);
return Ok();
}
}
I also would recommend to study about linq and how you can avoid those foreach cycles using select, check the default net core coding standards.
Edit
I'm curious, would LINQ provide any performance benefits in this case
vs just a regular for loop?
It is more about the readability and maintainability of your code, in the answer I already put a link to a post where is better explained, you are creating technical debt in your code, you are initializing arrays and filling them, handling indices and more... , but it reduces to this:
What is more easy to read? this:
public async Task AddToDatabase(Form form)
{
Roadmaps roadmap = new Roadmaps {
RoadmapTitle = form.title,
RoadmapSummary = form.summary,
RoadmapBody = form.body
};
var tags = new Tags[form.tags.Length];
for(int i = 0; i < tags.Length; i++)
{
tags[i] = new Tags();
tags[i].TagId = form.tags[i];
}
var roadmapTags = new RoadmapTags[form.tags.Length];
for(int i = 0; i < tags.Length; i++)
{
roadmapTags[i] = new RoadmapTags{Roadmap = roadmap, Tag = tags[i]};
}
_dbContext.AddRange(roadmapTags);
_dbContext.SaveChangesAsync();
}
or this
public async Task AddToDatabase(Form form)
{
var roadmap = new Roadmap {
RoadmapTitle = form.title,
RoadmapSummary = form.summary,
RoadmapBody = form.body
};
var roadmapTags = form.Tags
.Select(tagId => new Tag // First we take our form.tags and convert it to Tag objects
{
TagId = tagId
})
.Select(tag => new RoadmapTags // Then we take the result of the previous conversion and we
{ // transform again to RoadmapTags, we even could do this in one pass
Roadmap = roadmap, // but this way is more clear what the transformations are
Tag = tag
})
.ToList();
_dbContext.AddRange(roadmapTags);
await _dbContext.SaveChangesAsync();
}
If you just started learning programming, you could ignore this until you are more confortable with the for, foreach, while and other control structures. This is structure programming, and it is a topic by itself.
Also how would I pass the roadmapService object to the Controller
constructor, I've just never seen that.
That is the magic of dependency injection, just let the system create the object for you, you just have to ask for the type.
This is also a big topic, you should check my previous link to the microsoft documentation, but basically, all you have to do is to define classes as dependencies, so, when you ask for one, the system itself check the dependencies, and the dependencies of that objects, until resolves all the tree of dependencies.
With this, if you need one more dependency latter in your class, you can add directly but you do not need to modify all the classes that uses that class.
To use this in the controller, please check the official docs, you just have to add you dependencies to the constructor, and win!, basically two parts:
Add in your Startup.class
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<MySpecialClassWithDependencies>();
...
}
Then in your controller:
public class HomeController : Controller
{
private readonly MySpecialClassWithDependencies _mySpecialClassWithDependencies;
public HomeController(MySpecialClassWithDependencies mySpecialClassWithDependencies)
{
_mySpecialClassWithDependencies = mySpecialClassWithDependencies;
}
public IActionResult Index()
{
// Now i can use my object here, the framework already initialized for me!
return View();
}
I was wondering for some time what is the proper way to make a new instance of an dbcontext? I got some problem with it because when I make up change in my database through SQL Server my context doesn't update the data. Let me explain how my website work.
We are doing an appointment website for our customer to take appointment obviously. we will hosting all database on our server. How it work is the application made up 2 connection:
first connection
this connection connect all the time to the same database let's call it master.
It'll redirect the user to the good database with the url code in it example:
www.example.com/foo
the server will check for the code where here is foo
So it'll lookup in the table to matchup the code and then take the good database name where it should redirect and it's here that my second connection come's up
Second connection
This one will make the connection to the correct database according to the data the master has return. From here all seems to work well except for the DBContext that actually never update because I don't instantiate it correctly and I don't have an large experience with it. Here's the code i did with my coworker:
using System;
using System.Data.EntityClient;
using System.Data.SqlClient;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Routing;
using WebRV.Models.Entities;
namespace WebRV.RouteDb
{
public class DbConnection
{
private static DbConnection instance;
private Cliniciel_WebRV_Entities db;
private String connectionString;
private readonly Cliniciel_WebRV_MasterEntities masterDb = new Cliniciel_WebRV_MasterEntities();
private Int32 idCie;
private static readonly object myLock = new object();
private DbConnection() {
var context = new HttpContextWrapper(System.Web.HttpContext.Current);
var routeData = RouteTable.Routes.GetRouteData(context);
// Use RouteData directly:
String code = routeData.Values["code"].ToString();
//String code = Thread.CurrentContext. .RequestContext.RouteData.Values["code"].ToString();
var response = masterDb.SYS_tbCustDBLocation.Where(p => p.CustDBLocationCode == code).ToList();
if (response.Count == 1)
{
try
{
db = CreateConnection(response.FirstOrDefault());
idCie = (db.SYS_vwCie.Where(p => p.ClinicielWebName == code).FirstOrDefault()).IdCie;
}
catch (Exception e)
{
throw e;
}
}
else {
throw new FormatException();
}
}
private Cliniciel_WebRV_Entities CreateConnection(SYS_tbCustDBLocation data)
{
connectionString = *****
db = new Cliniciel_WebRV_Entities();
db.Database.Connection.ConnectionString = connectionString;
db.Database.Connection.Open();
return db;
}
private static void CreateInstance() {
instance = new DbConnection();
}
public static DbConnection GetInstance() {
lock (myLock)
{
if (instance == null)
{
CreateInstance();
}
}
return instance;
}
public String GetConnectionString()
{
return connectionString;
}
public Cliniciel_WebRV_Entities GetConnection()
{
return db;
}
public Int32 GetIdCie()
{
//hard code 1 but test
//return idCie;
return 1;
}
}
}
and here's an example of how I use it:
//[CompanyCodeFilter]
public class HomeController : AppointementController
{
//public static Cliniciel_WebRV_Entities entityDB = DbConnection.GetConnection();
public HomeController()
{
base.Refresh();
}
public JsonResult GetConsultationDescription(Int32 idService)
{
//base.Refresh();
entityDB.Set<SYS_vwService>().AsNoTracking();
var motifDescription = entityDB.SYS_vwService.Where(s => s.IDLang == cultureName && s.IdService == idService && s.IdCie == idCie).FirstOrDefault();
var base64 = Convert.ToBase64String(motifDescription.ServiceImage);
var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
var imageDecode = imgSrc;
if (base64 == "AA==")
{
imageDecode = "";
}
var result = new { motifDescription, imageDecode };
return Json(result, JsonRequestBehavior.AllowGet);
}
}
Here base.refresh() call this:
using System;
using System.Linq;
using WebRV.Filters;
using WebRV.Localization;
using WebRV.Models.Entities;
using WebRV.RouteDb;
namespace WebRV.Controllers
{
//[CompanyCodeFilter]
public class AppointementController : BaseController
{
protected Cliniciel_WebRV_Entities entityDB;
protected Int32 idCie;
protected String cultureName;
//public AppointementController() {
// Refresh();
//}
//Permet de bien vérifier quel DB utilisé, quel idCie et quel cultureName.
protected void Refresh() {
entityDB = DbConnection.GetInstance().GetConnection();
idCie= DbConnection.GetInstance().GetIdCie();
cultureName = CultureLocalization.GetCulture();
//cultureName = "en-ca";
}
}
}
If someone can help me to instantiate the connection properly it'll be appreciate thank you
I think you need to better understand how the db context works.
It's a combination of many design patterns but at a basic level, it's essentially a unit of work that tracks changes to the objects that represent data. Because of this, it's not meant to be used as a connection that stays open all the time and you can just go back and forward, although you could use it like that to a certain extent.
From your code, it looks like you are using it in an ASP.NET MVC application. Because of the nature of the application, you can do one of two things:
You can create the db context whenever you need to use it, or
You can create the db context as a property of the controller
In the end, they somewhat amount to the same thing. I personally would recommend you create an instance of the context whenever you need to use it, making sure to dispose of it properly. You could then have a base controller class that exposes a CreateConnetion() method that creates an instance for your contexts, sort of like the Cliniciel_WebRV_Entities CreateConnection() method you already have, except that you don't need to open the connection explicitly and the connection string can be passed on as a constructor parameter if you add a partial class and implement one for the context. Something like this:
public partial class Cliniciel_WebRV_Entities
{
public Cliniciel_WebRV_Entities(string nameOrConnectionString):base(nameOrConnectionString)
{
}
}
You can then use it like this:
private Cliniciel_WebRV_Entities CreateConnection()
{
//Code to figure out which db to connect to (based on the other connection. You should consider caching that too if it doesn't change very often)
var nameOrConnectionString = FigureOutConnection();
var db = new Cliniciel_WebRV_Entities(nameOrConnectionString);
return db;
}
Keep in mind that the context depends on the metadata so make sure your connection string reflects that.
In your code, you would then consume it like this:
public JsonResult GetConsultationDescription(Int32 idService)
{
using(var entityDB = CreateConnection())
{
var motifDescription = entityDB.SYS_vwService.Where(s => s.IDLang == cultureName && s.IdService == idService && s.IdCie == idCie).FirstOrDefault();
var base64 = Convert.ToBase64String(motifDescription.ServiceImage);
var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
var imageDecode = imgSrc;
if (base64 == "AA==")
{
imageDecode = "";
}
var result = new { motifDescription, imageDecode };
return Json(result, JsonRequestBehavior.AllowGet);
}
}
}
Another thing to remember and something that bites every newcomer is that you should not pass entities (objects from the context) to the views in models. This is because once the context is disposed, objects with navigational properties will break. There are ways around it but not recommended. Map the objects to models instead.
By the way, don't forget to call the SaveChanges() method after modifying entities or the database will not be updated with the changes.
You should really read very carefuly from some very good sources about DbContext: for example:
http://www.entityframeworktutorial.net/EntityFramework4.3/dbcontext-vs-objectcontext.aspx
https://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext(v=vs.113).aspx
When you pass a DbContext to service or business class, you pass it as a Value not as a reference so you will loss all changes made in client code, the option is to pass it as a reference but it is not a safe and not a good practice but, if you instead pass the context as a delegate you are passing a reference to the object directly so its the best approch in order to keep your injected context with changes from business/service layers.
Here is the idea:
Option 1, pass it as reference, not recommended:
public void Call()
{
//Attach changes to context, Passing as reference
Process(ref _context);
//All changes attached to context
_context.SaveChanges();
}
public void Process(ref Cliniciel_WebRV_MasterEntities context)
{
var c = context();
//Get entites dbcontext
//Update entites
}
Option 2, pass it as a delegate, best approch:
public void Call()
{
//Attach changes to context, Passing as reference
Process(() => _context);
//All changes attached to context
_context.SaveChanges();
}
public void Process(Func<Cliniciel_WebRV_MasterEntities> context)
{
var c = context();
//Get entites dbcontext
//Update entites
}
class abc {
string value1 = "", value2 = "", ........... valueN = "";
#region Methods
public void method1(DataSet dataSet) {
populate valueS using populateValues() and use it for doing something
}
....
public void methodN(DataSet dataSet) {
populate valueS using populateValues() and use it for doing something else
}
#endregion
public void populateValues(DataSet dataSet) {
#region populate valueS so they can be used by methodS
if(dataSet.Tables.Contains("Table1") {
var dataRow1 = dataSet.Tables["Table1"].Select();
if(dataRow1.Length > 0) {
value1 = dataRow1[0]["value1"].ToString()
}
}
.....
if(dataSet.Tables.Contains("TableN") {
var dataRowN = dataSet.Tables["TableN"].Select();
if(dataRowN.Length > 0) {
valueN = dataRowN[0]["valueN"].ToString()
}
}
#endregion
}
}
The above snippet is an abstract skeleton of a large legacy project in which there are many methodS() like:
method1()
method2()
.....
methodN()
Almost all of these require almost all valueS:
value1
value2
...
valueN
which are populated using populateValues().
This is how abc is used in other places:
class xyz {
abc a = new abc();
public void doingSomething() {
//Get data from DB and populate to dataSet
a.method1();
a.method5();
....
}
public void doingSomethingElse() {
//Get data from DB and populate to dataSet
a.method1();
return;
}
}
doingSomething() is part of a web service project which is mostly called at the same time from few overlapping-lu running windows service projects.
I'm seeing a danger of possible overwriting of valueS by populateValues() when any of the methodS() are called in parallel.
The structure of dataSet is mostly unpredictable and hence I always need to check the Table1 existence.
The methods will mostly be called in parallel as they're part of a web services project.
How do I save myself from overwriting these large number of values without messing the code in C#?
In other words, how do we make sure that the valueS set after methodN() is not passed to methodN+1() when they're called simultaneously?
The most safe approach would be to copy pasted entire populateValues() body in each methodS() however, this will mess the project a lot.
Declare a class having all Values
Class resultVals
{
string value1 = "", value2 = "", ........... valueN = "";
}
Modify populateValues to return new class resultVals.
public resultVals populateValues(DataSet dataSet) {
resultVals valueS =new resultVals() ;
#region populate valueS so they can be used by methodS
if(dataSet.Tables.Contains("Table1") {
var dataRow1 = dataSet.Tables["Table1"].Select();
if(dataRow1.Length > 0) {
valueS.value1 = dataRow1[0]["value1"].ToString()
}
}
.....
if(dataSet.Tables.Contains("TableN") {
var dataRowN = dataSet.Tables["TableN"].Select();
if(dataRowN.Length > 0) {
valueS.valueN = dataRowN[0]["valueN"].ToString()
}
}
#endregion
}
return resultVals ;
}
Use the returned value (resultVals) in all methods.
public void method1(DataSet dataSet) {
resultVals valueS = populateValues()
and use it for doing something
}
You need to synchronize populateValues
lock(new object())
{
var result = populateValues();
}
However, lock is the simplest one. There are others. ex, you can use Monitor class as well.
Remove all the fields from the abc class.
Remove populate values method.
Create extension method to get value by a number.
Use the extension in place of the valueN field (easily regexp replaced in the code file).
public static string GetValue(this DataSet dataSet, int n)
{
if (dataSet.Tables.Contains("Table" + n)
{
var dataRow = dataSet.Tables["Table" + n].Select();
if (dataRow.Length > 0)
{
return = dataRow[0]["value" + n].ToString()
}
}
return string.Empty;
}
public void method1(DataSet dataSet)
{
var value1 = dataSet.GetValue(1);
}
Better yet, combine this with the Anil's answer and use a struct
public struct Values
{
public string value1;
...
public string valueN;
public static Values Init(DataSet dataSet)
{
// populate values logic here (use the extension from above)
value1 = dataSet.GetValue(1);
...
valueN = dataSet.GetValue(N);
}
}
And then call the methods.
var dataSet = new DataSet();
var values = Values.Init(dataSet);
a.method1(values);
a.method5(values);
public void method1(Values values)
{
// use values.value1 instead of value1 here
}
I want to use an own function to work with different database tables. As I'm using SQLite, the tables are filled using custom classes.
I'm trying to fill them recursively so I've created this struct and array:
public class cCodesPair
{
public string cCodeKey { get; set; }
public Type CommonCodeClass { get; set; }
public cCodesPair(string key, Type o)
{
this.cCodeKey = key;
this.CommonCodeClass = o;
}
}
private cCodesPair[] codesPairs = new cCodesPair[]
{
new cCodesPair("DESC_UNITS", typeof(SQLEventUnits)),
new cCodesPair("DESC_DISCIPLINE", typeof(SQLDisciplines)),
new cCodesPair("DESC_COUNTRY", typeof(SQLCountries)),
new cCodesPair("DESC_VIDEOS", typeof(SQLVideos)),
new cCodesPair("DESC_ATHLETES", typeof(SQLAthletes))
};
The idea of creating this, was to loop through the array to create query the table and to fill a Dictionary for each.
The function that tries to do that is:
public void Load<T>(T t, SQLiteConnection conn) where T : Type
{
try
{
var query = conn.Table<T>;
//MORE CODE...
} catch (Exception ex)
{
Debug.WriteLine("Exception")
}
}
The function that loops through the array and calls the Load function is the following one:
private async Task fillDictionaries()
{
for (int i = 0; i < codesPairs.Length; i++)
{
MasterDescriptions m = new MasterDescriptions();
Type t = codesPairs[i].CommonCodeClass;
m.Load(t, conn);
}
}
But I get that the table queried has been one called Type.
I would like to get Tables SQLEventUnits, SQLDisciplines, etc dinamically.
Anyone knows how to?
Thanks in advance!
Do you know Microsoft Enterprise Library?
https://msdn.microsoft.com/en-us/library/ff648951.aspx
Does exactly what you want to implement.
Another reference:
http://www.codeproject.com/Tips/657233/Enterprise-Library-6-Sqlite-Logging-and-Exceptions
1.
I have this method in my repository class
public class VariablesRepository : IVariablesRepository
{
readonly DBContextClass _context = DBContextClass.Current;
public Variables Find(string name)
{
return _context.Variables.FirstOrDefault(c => c.Name.ToLower().Equals(name.ToLower())) ?? new Variables();
}
}
2.
I also have this static class
public class Defaults {
private static VariablesRepository _variablesRepository;
static Defaults() {
_variablesRepository = new VariablesRepository();
}
public class MOSScheduleTypes
{
private static int _tryValue;
public static readonly int OneTime = int.TryParse(_variablesRepository.Find("MOSScheduleTypes.OneTime").Value, out _tryValue)
? _tryValue
: 1;
}
}
3.
Now if i do this somewhere in code: for example
if(someValue == Defaults.MOSScheduleTypes.OneTime)
{
//some code here....
}
I get the error : The operation cannot be completed because the DbContext has been disposed
The error is caused by the data context being disposed. As you haven't shown any code for disposing of...anything, it means that there is some code somewhere else that is disposing of that same data context.
Note that data contexts are designed to be short lived; it's code smell to see you holding onto the data context as you are for later use, particularly in a static variable that appears to be long lived.
Instead of grabbing the current data context once you should be grabbing it right when you need it. Since you won't be storing the data context as an instance field of VariablesRepository, the method can also be made static (it will have no instance data to use).
public class VariablesRepository : IVariablesRepository
{
public static Variables Find(string name)
{
return DBContextClass.Current.Variables.FirstOrDefault(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? new Variables();
}
}
public class Defaults
{
public class MOSScheduleTypes
{
private static int _tryValue;
public static readonly int OneTime = int.TryParse(VariablesRepository.Find("MOSScheduleTypes.OneTime").Value, out _tryValue)
? _tryValue
: 1;
}
}
Ok, solved this issue by using raw ado.net code, so i just changed the find method from entity framework linq code to raw ado.net data access code.
The error didn't occur any more, if anyone has a better solution using entity framework, please share. The find method rewritten, code below:
public Variables Find(string name)
{
var objVariable = new Variables();
using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionStringValue"].ToString()))
{
conn.Open();
var sql = #" select top 1 *
from SomeTable
where Column1 = #Name";
try
{
var cmd = new SqlCommand(sql, conn);
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#Name", SqlDbType.NVarChar).Value = name;
SqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows)
{
if (dr.Read())
{
objVariable.Column1 = dr["Column1"].ToString();
objVariable.Column2 = dr["Column2"].ToString();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
return objVariable;
}
The reason for this is that your LINQ query is deferred, in other words it is not run in your "VariablesRepository" but only in the calling code. So the DBContext has already been disposed of when it is executed. You can force it to execute immediately by adding .ToList() so changing
return _context.Variables.FirstOrDefault(c => c.Name.ToLower().Equals(name.ToLower())) ?? new Variables();
to
return _context.Variables.ToList().FirstOrDefault(c => c.Name.ToLower().Equals(name.ToLower())) ?? new Variables();