Generic method for database update - c#

I use following method to update orgnrs in a database:
UpdateService service = new UpdateService()
service.UpdateDocsA(orgnrs);
public void UpdateDocsA(List<string> orgnrs)
{
using (var context = new Data.InexchangeEntitiesA())
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
Problem is that I have multiple databases with similar update.
service.UpdateDocsA(orgnrs);
service.UpdateDocsB(orgnrs);
service.UpdateDocsC(orgnrs);
service.UpdateDocsC(orgnrs);
The only difference is in following line:
using (var context = new Data.InexchangeEntitiesA())
using (var context = new Data.InexchangeEntitiesB())
using (var context = new Data.InexchangeEntitiesC())
using (var context = new Data.InexchangeEntitiesD())
I want to create a generic update method. Any ideas how can I achieve it? or how to pass Data.InexchangeEntities to a method?

Assuming that the method signature for InexchangeEntitiesA() and InexchangeEntitiesB() etc. is common why not pass that in to your UpdateDocs method?
If we assume those methods all return an IDataContext object which implements a Customers() method;
UpdateService service = new UpdateService()
service.UpdateDocs(Data.InexchangeEntitiesA, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesB, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesC, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesD, orgnrs);
public void UpdateDocs<T>(Func<T> getContext, List<string> orgnrs) where T : IDataContext
{
using (var context = getContext())
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = context.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}

Or something like this
public class Customer { }
public interface ISomeFunkyInterface
{
DbSet<Customer> Customers { get; set; }
}
public class DbContextA : DbContext, ISomeFunkyInterface
{
public DbSet<Customer> Customers { get; set; }
}
public void UpdateDocs<T>(List<string> orgnrs)
where T : DbContext, ISomeFunkyInterface ,new()
{
using var context = new T();
foreach (string orgnr in orgnrs)
{
context.Customers.Add(...);
}
context.SaveChanges();
}

service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesA());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesB());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesC());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesD());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesE());
...
public void UpdateDocs(List<string> orgnrs, YOURCONTEXTTYPE context)
{
using (context)
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}

There are multiple ways to achieve that. The most obvious to me is extract what is common and create multiple methods for that:
public void UpdateDocsA(List<string> orgnrs)
{
using (var context = new Data.InexchangeEntitiesA())
{
Perform(context, orgnrs);
}
}
void Perform(DbContext context, List<string> orgnrs)
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{ }
}
context.SaveChanges();
}
This will reduce the duplicated code massivly. However it still feels ugly, because you still have four Update-methods.
You could also extract what is uncommon and inject a paremeter to switch on:
public void UpdateDocs(string type, List<string> orgnrs)
{
using (var context = GetContext(type))
{
Perform(context, orgnrs);
}
}
DbContext GetContext(string type)
{
return type == "A" ? new Data.InexchangeEntitiesA() :
type == "B" ? new Data.InexchangeEntitiesB() :
type == "C" ? new Data.InexchangeEntitiesC() :
new Data.InexchangeEntitiesD();
}
This does not seem much better, does it? We only have a single method, however it still uses some weird type-switching. Let´s try something more generic - assuming all your entities have some common base-interface or -class:
public void UpdateDocs<T>(List<string> orgnrs) where T: IMyInterface, new()
{
using (var context = new T)
{
Perform(context, orgnrs);
}
}

Related

Is filling different DataTables of the same DataSet a thread-safe operation?

I have many methods filling different DataTables in the same DataSet:
public override void FillMethod1(MyDataSet ds)
{
try
{
using (SqlDataAdapter adapter = new SqlDataAdapter(query, connectionObj))
{
using (SqlCommandBuilder adapterSCB = new SqlCommandBuilder(adapter))
{
adapter.Fill(ds.MyTable);
}
}
}
catch (Exception e)
{
//Exception handling
}
}
public override void FillMethod2(MyDataSet ds)
{
try
{
using (SqlDataAdapter adapter = new SqlDataAdapter(query, connectionObj))
{
using (SqlCommandBuilder adapterSCB = new SqlCommandBuilder(adapter))
{
adapter.Fill(ds.MyTable2);
}
}
}
catch (Exception e)
{
//Exception handling
}
}
[....]
using(MyDataSet ds = new MyDataSet())
{
FillMethod1(ds);
FillMethod2(ds);
[...]
}
And I'd like to parallelize these operation using Task:
using(MyDataSet ds = new MyDataSet())
{
Task fill1 = Task.Run(() => FillMethod1(ds));
Task fill2 = Task.Run(() => FillMethod2(ds));
[...]
Task.WaitAll(fill1, fill2, [...]);
}
After some research I found that DataSet is not thread-safe, but is it safe when working on different DataTable?
I created a class handling parallel fills on the same DataSet. Each fill has its own DataSet and it will be merged into the original one at the end of the execution.
To handle data table relations, it is possible to create a sequential list of fills that will be run in parallel with other fills. The merge method will wait for all operations to finish and merge results into the original DataSet (passed in the constructor)
public class FillParallelizer<TDataSet> : IDisposable
where TDataSet : DataSet, new()
{
private bool _seqFillStarted = false;
private IList<QueryObject> _queryObjList = new List<QueryObject>();
private Queue<FillOperation> _seqOperationBufferQueue = new Queue<FillOperation>();
public DataSet MasterDS { get; }
public FillParallelizer(DataSet masterDS)
{
MasterDS = masterDS ?? throw new ArgumentNullException(nameof(masterDS));
}
public FillParallelizer<TDataSet> ParallelFill(Action<TDataSet> methodAction)
{
if (methodAction == null) throw new ArgumentNullException(nameof(methodAction));
QueryObject queryObj = new QueryObject(methodAction);
_queryObjList.Add(queryObj);
return this;
}
public void Merge()
{
var taskList = _queryObjList.Select(x => x.Task).ToArray();
Task.WaitAll(taskList);
foreach (var item in _queryObjList)
{
MasterDS.Merge(item.TempDS);
}
}
public FillParallelizer<TDataSet> SequentialFill(Action<TDataSet> action)
{
if(!_seqFillStarted)
{
_seqFillStarted = true;
}
if (!_seqFillStarted) throw new ArgumentException("Sequential fill not started");
_seqOperationBufferQueue.Enqueue(new FillOperation(action));
return this;
}
public FillParallelizer<TDataSet> RunSequentialFill()
{
_seqFillStarted = false;
QueryObject queryObj = new QueryObject(new Queue<FillOperation>(_seqOperationBufferQueue));
_seqOperationBufferQueue.Clear();
_queryObjList.Add(queryObj);
return this;
}
public void Dispose()
{
if (_queryObjList == null || !_queryObjList.Any())
{
return;
}
foreach (var item in _queryObjList) item.Dispose();
}
class FillOperation
{
public Action<TDataSet> Action { get; }
public FillOperation(Action<TDataSet> action)
{
Action = action ?? throw new ArgumentNullException(nameof(action));
}
public void Run(TDataSet tempDS)
{
if (tempDS == null) throw new ArgumentNullException(nameof(tempDS));
Action(tempDS);
}
}
class QueryObject : IDisposable
{
public TDataSet TempDS { get; }
public Queue<FillOperation> FillOperationList { get; }
public Task Task { get; }
public QueryObject(Queue<FillOperation> fillOperationList)
{
TempDS = new TDataSet();
FillOperationList = fillOperationList ?? throw new ArgumentNullException(nameof(fillOperationList));
Task =
Task
.Run
(() =>
{
foreach (FillOperation op in FillOperationList) op.Run(TempDS);
}
);
}
public QueryObject(Action<TDataSet> action)
: this(new Queue<FillOperation>(new FillOperation[] { new FillOperation(action) }))
{
}
public void Dispose()
{
Task.Dispose();
}
}
}
An example of usage:
using(MyDataSet myDS = new MyDataSet())
using (FillParallelizer<MyDataSet> parallelizer = new FillParallelizer<MyDataSet>(myDS))
{
parallelizer
.ParallelFill(ds => MyDAO.FillTable1(ds))
.ParallelFill(ds => MyDAO.FillTable2(ds))
.SequentialFill(ds => MyDAO.FillTable3(ds))
.SequentialFill(ds => MyDAO.FillTable4(ds))
.SequentialFill(ds => MyDAO.FillTable5(ds))
.RunSequentialFill()
.ParallelFill(ds => MyDAO.FillTable6(ds))
.ParallelFill(ds => MyDAO.FillTable7(ds))
.Merge();
//All fills completed
}
Fill methods 3, 4, 5 are done sequentially but in parallel with other fills, to handle relations between tables

Use Rhino Mock to report the function that was called

I have a failing testcase that depends on an external module.
I want to use Rhino Mock to generate a report on called functions.
I created a minimal example that illustrates my problem:
using NUnit.Framework;
using Rhino.Mocks;
using System;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
object liveFastDieYoung = service.HiddenAmongManyCalls();
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch(Exception e)
{
string[] calls = MagicallyGetTheCallsThatWereMadeToTheMock();
foreach(var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private string[] MagicallyGetTheCallsThatWereMadeToTheMock()
{
return new[]
{
"This is where I am lost, I do not know how to get the calls from the repository."
};
}
}
}
I tried to find something online without success.
Do Rhino Mocks record all calls and can I access that list?
Edit:
An attempt to verify Expectations did not work since I am looking for calls I did not expect.
I could build a list of calls using GetArgumentsForCallsMadeOn. I can reflect on the Interface. I started on a method for that but I currently fail to see how I can convert a MethodInfo to an Action<T>.
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
interfaceMethodInfos.Add(property.GetGetMethod());
interfaceMethodInfos.Add(property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
interfaceMethodInfos.Add(method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
Action<Interface> magic = null; //convert methodinfo into action - still missing
var calls = rhinomock.GetArgumentsForCallsMadeOn(magic); //magic is currently null, here be crash
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more){ callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else { callbuilder.Append(parameter.ToString()); }
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
I was able to use reflection to get the output I wanted.
Here is the minimal example where the test fails and the output contains all method calls.
using NUnit.Framework;
using Rhino.Mocks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
string TestCall2(string arg1, int arg2);
string FULLACCESS { get; set; }
string READONLY { get; }
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
service.TestCall2("callA", 1);
service.TestCall2("callB", 1);
object liveFastDieYoung = service.HiddenAmongManyCalls();
service.TestCall2("callA", 2);
service.TestCall2("callB", 2);
var a = service.FULLACCESS;
var b = service.READONLY;
service.FULLACCESS = "some";
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch (Exception e)
{
var calls = GetCallsList(service);
foreach (var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
AddMethodInfoIfValid(interfaceMethodInfos, property.GetGetMethod());
AddMethodInfoIfValid(interfaceMethodInfos, property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
AddMethodInfoIfValid(interfaceMethodInfos, method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
int paramcount = methodinfo.GetParameters().Length;
object[] args = new object[paramcount];
Action<Interface> lambdacall = (i) => methodinfo.Invoke(i, args);
var calls = rhinomock.GetArgumentsForCallsMadeOn(lambdacall);
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more) { callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else {
callbuilder
.Append('(').Append(parameter.GetType().Name).Append(")'")
.Append(parameter.ToString()).Append("'");
}
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
private static void AddMethodInfoIfValid(List<MethodInfo> interfaceMethodInfos, MethodInfo methodinfo)
{
if (null != methodinfo)
{
interfaceMethodInfos.Add(methodinfo);
}
}
}
}

Adding to List inside a Using block

I have a class which implements IDisposable like such
public class SomeClass : IDisposable
{
private IList<string> _someList = new List<string>();
public IList<string> SomeList
{
get { return _someList; }
}
public void Dispose()
{
_someList = null;
}
}
and a method that uses 'using' blocks to create two instances of this class and add it to a collection like such
private static void Main(string[] args)
{
IList<SomeClass> classes = new List<SomeClass>();
using (var sc = new SomeClass())
{
sc.SomeList.Add("a");
classes.Add(sc);
}
using (var sc = new SomeClass())
{
sc.SomeList.Add("b");
classes.Add(sc);
}
foreach (var a in classes)
{
Console.WriteLine(a.SomeList[0]);
}
Console.ReadLine();
}
Whats happening here is by the time i come to the foreach to iterate through my elements in "classes" the "SomeList" property of both my objects are null.
I do understand that since each using executes Dispose() and in the dispose, i AM null-ing the two lists, they will be null.
My question is how do i achieve this without having to stop null-ing "SomeList" inside my dispose().
thanks
--UPDATE 1
Something closer to real code
base.OnPreRender(e);
Page page = HttpContext.Current.CurrentHandler as Page;
HtmlHead head = (HtmlHead)page.Header;
if (Settings.AreSet)
{
using (var noIndex = new HtmlMeta())
{
noIndex.Name = "somename";
noIndex.Content = "somecontent";
head.Controls.AddAt(0, noIndex);
}
}
using (var machineName = new HtmlMeta())
{
machineName.Name = "somename2";
machineName.Content = "somecontent2";
head.Controls.AddAt(1, machineName);
}
UpdateHeader(head);
--UPDATE #2
A variant of the above class with a string property now, as opposed to a a list.
public class SomeClass2 : IDisposable
{
public string SomeString { get; set; }
public void Dispose()
{
SomeString = string.Empty;
}
}
It still does the same thing for the following method. My 'classes' contains lists of 'SomeClass2' which has 'SomeString' as empty (what i set in my dispose())
IList<SomeClass2> classes = new List<SomeClass2>();
using (var sc = new SomeClass2())
{
sc.SomeString = "a";
classes.Add(sc);
}
using (var sc = new SomeClass2())
{
sc.SomeString = "a";
classes.Add(sc);
}
foreach (var a in classes)
{
Console.WriteLine(a.SomeString);
}
Console.ReadLine();
You don't want Dispose to be called? Just don't use using statement.
Or move the entire logic inside using:
private static void Main(string[] args)
{
IList<SomeClass> classes = new List<SomeClass>();
using (var sc = new SomeClass())
{
sc.SomeList.Add("a");
classes.Add(sc);
using (var sc = new SomeClass())
{
sc.SomeList.Add("b");
classes.Add(sc);
foreach (var a in classes)
{
Console.WriteLine(a.SomeList[0]);
}
}
}
Console.ReadLine();
}
Btw: looks like using IDisposable in that case is pointless. GC would gather unused List<T> objects anyway.

Unity IOC and Factory pattern, and repository pattern

I am trying to use the Unity Block of the enterprise library in a program i am writing.
But i think i am using dependency injection wrong. I was wondering if some one could point me in the right direction.
static void Main(string[] args)
{
using (IUnityContainer container = new UnityContainer())
{
InitialiseContainer(container);
DataCopierFactory dcFactory = new DataCopierFactory();
ERunOptions dataCopierType = ExtractParams(args);
IDataCopier dataCopier = dcFactory.CreateDataCopier((int)dataCopierType, container);
dataCopier.DetectChanges();
dataCopier.ParseData();
dataCopier.CopyData();
}
}
}
//use the ioc container to register the EF context type to the repository interfaces..
private static void InitialiseContainer(IUnityContainer container)
{
//add Extensions:
container.AddNewExtension<Interception>();
//Licence Schedule
container.RegisterType<IEFContext, LTE_DownFromWeb_EFContext>("DataCopier.ScheduleDataCopier.Source");
container.RegisterType<IEFContext, LTE_Licensing_EFContext>("DataCopier.ScheduleDataCopier.Destination");
container.RegisterType<IRepositorySession>("Schedule_Source",new InjectionConstructor(container.Resolve<IEFContext>("DataCopier.ScheduleDataCopier.Source")));
container.RegisterType<IRepositorySession>("Schedule_Destination",new InjectionConstructor(container.Resolve<IEFContext>("DataCopier.ScheduleDataCopier.Destination")));
}
So basically the DataCopier Factory creates an instance of a DataCopier like so:
DataCopierFactory:
//return a data copier that will transfer data from any DB to any other DB
public IDataCopier CreateDataCopier(int i, IUnityContainer container)
{
switch(i)
{
case 1:
return new ScheduleDataCopier(container);
default:
throw new InvalidOperationException("Parameter " + i + " does not exist");
}
}
a data copier looks like this:
class ScheduleDataCopier : IDataCopier
{
private List<Site> _sites;
private List<SitesAndApparatuses> _scheduleList;
private IUnityContainer _container;
public ScheduleDataCopier(IUnityContainer container)
{
_container = container;
_scheduleList = new List<SitesAndApparatuses>();
}
//check if new sites registration has arrived in tblSites on down from web db.
public bool DetectChanges()
{
using (var db = _container.Resolve<IRepositorySession>("Schedule_Source"))
{
SiteAudit lastSite = new SitesAuditRepository().GetLatest();
var sitesRepo = new SitesRepository();
var sites = sitesRepo.Where(x => x.SID > lastSite.SALatestSID);
if (sites.Count() < 1)
{
return false;
}
_sites = sites.ToList();
db.Dispose();
}
return true;
}
//parse the data into a list of object SitesAndApparatuses
public bool ParseData()
{
try
{
foreach (Site s in _sites)
{
var schedule = (SitesAndApparatuses)XmlObjectBuilder.Deserialize(typeof(SitesAndApparatuses), s.XMLFile);
schedule.acCode = s.Registration.RAcCode;
_scheduleList.Add(schedule);
}
}
catch (Exception ex)
{
throw new NotImplementedException("HANDLE THIS SHIT!", ex);
}
return true;
}
public bool CopyData()
{
try
{
using (var db = _container.Resolve<IRepositorySession>("Schedule_Destination"))
{
var licensingScheduleRepo = new LicensingScheduleRepository();
//some logic
db.Commit();
}
}
catch (Exception ex)
{
}
return true;
}
}
Second question, i resolve my unit of work object called RepositorySession in the Datacopier classes using the unity container i passed... is this the wrong approach and why, im struggling to find any info on it online?
This is probably too much code for someone to read.. but im hoping for an answer!
Thanks in advance
Neil
I'd do something like:
container.RegisterType<IEFContext, LTE_DownFromWeb_EFContext>("Source");
container.RegisterType<IEFContext, LTE_Licensing_EFContext>("Destination");
container.RegisterType<IRepositorySession>("Source",new InjectionConstructor(new ResolvedParameter<IEFContext>("Source"));
container.RegisterType<IRepositorySession>("Destination",new InjectionConstructor(new ResolvedParameter<IEFContext>("Destination")));
container.RegisterType<IDataCopier,ScheduleDataCopier>("0",new InjectionConstructor(new[] {new ResolvedParameter<IRepositorySession("Source"),new ResolvedParameter<IRepositorySesison>("Destination")}));
//Now resolve
ERunOptions dataCopierType = ExtractParams(args);
IDataCopier dataCopier = container.Resolve<IDataCopier(dataCopierType.ToString());
dataCopier.DetectChanges();
dataCopier.ParseData();
dataCopier.CopyData();
DataCopier Class
class ScheduleDataCopier : IDataCopier
{
private List<Site> _sites;
private List<SitesAndApparatuses> _scheduleList;
private IRepositorySession _source;
private (IRepositorySession _destination;
public ScheduleDataCopier(IRepositorySession source, (IRepositorySession destination)
{
_source=source;
_destination=destination;
_scheduleList = new List<SitesAndApparatuses>();
}
//check if new sites registration has arrived in tblSites on down from web db.
public bool DetectChanges()
{
SiteAudit lastSite = new SitesAuditRepository().GetLatest();
var sitesRepo = new SitesRepository();
var sites = sitesRepo.Where(x => x.SID > lastSite.SALatestSID);
if (sites.Count() < 1)
{
return false;
}
_sites = sites.ToList();
_source.DoSomething();
_source.CommitAndReleaseResources();//clean up but leave object reusable
return true;
}
//parse the data into a list of object SitesAndApparatuses
public bool ParseData()
{
try
{
foreach (Site s in _sites)
{
var schedule = (SitesAndApparatuses)XmlObjectBuilder.Deserialize(typeof(SitesAndApparatuses), s.XMLFile);
schedule.acCode = s.Registration.RAcCode;
_scheduleList.Add(schedule);
}
}
catch (Exception ex)
{
throw new NotImplementedException("HANDLE THIS SHIT!", ex);
}
return true;
}
public bool CopyData()
{
try
{
var licensingScheduleRepo = new LicensingScheduleRepository();
//some logic
_desitnation.Commit();
}
catch (Exception ex)
{
//handle exception
}
return true;
}
}
The two main differences between what you're doing and the above is that I'm using Injection Parameters (the ResolvedParameter class) to dynamically resolve instances of objects when they're needed.
This allows me to get Unity to do my entire DI process for me, including resolve my DataCopier. If I add another Datacopier I'd just need to add the new DataCopier type to Unity with a name that matches the appropriate ERunOptions type and I'd be able to resolve the new DataCopier with no change to my code:
container.RegisterType<IDataCopier,RandomDataCopier>("0",new InjectionConstructor(new[] {new ResolvedParameter<IRepositorySession("RandomSource"),new ResolvedParameter<IRepositorySesison>("RandomDestination")}));
and:
ERunOptions dataCopierType = ExtractParams(args);
IDataCopier dataCopier = container.Resolve<IDataCopier(dataCopierType.ToString());
dataCopier.DetectChanges();
dataCopier.ParseData();
dataCopier.CopyData();
Stays the same but can process a ScheduledDataCopier or a RandomDataCopier

Linq to SQL records not updating

I am trying to get a list of records from a table and then in a foreach loop I am trying to update the records one by one.
Here is my code
public IEnumerable<MessageOut> GetSMSInformation()
{
using (var db = new DataClasses1DataContext())
{
var sms = db.MessageOuts.Where(msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
}
public void StartSMSSending()
{
var information = GetSMSInformation();
foreach (var sms in information)
{
SendSMS(sms.reciever, sms.msg);
UpdateRecords(sms,"Delivered", DateTime.Now);
}
}
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime)
{
using (var db = new DataClasses1DataContext())
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
db.SubmitChanges();
}
}
The records just don't update.I am not getting any errors as well.
Can someone help me out with this?
In order to perform an update operation with Linq2Sql classes you need to keep your context alive. In the example you have above, you include your selection inside a using statement, so your records are returned and then the context is dumped. When you go to make the update, there is a fresh context, so there is no change registered to the objects. A way to do this could be to make the context a more global object or to pass it as a parameter.
public IEnumerable<MessageOut> GetSMSInformation(DataContext context)
{
var sms = context.MessageOuts.Where(
msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
public void StartSMSSending()
{
using (var db = new DataClasses1DataContext())
{
var information = GetSMSInformation(db);
foreach (var sms in information)
{
SendSMS(sms.reciever, sms.msg);
UpdateRecords(sms,"Delivered", DateTime.Now, db);
}
}
}
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime, DataContext context)
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
context.SubmitChanges();
}
Or, you could do this (my preferred method):
public IEnumerable<MessageOut> GetSMSInformation()
{
var sms = Context.MessageOuts.Where(
msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
public void StartSMSSending()
{
var information = GetSMSInformation(db);
foreach (var sms in information)
{
SendSMS(sms.reciever, sms.msg);
UpdateRecords(sms,"Delivered", DateTime.Now, db);
}
}
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime)
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
Context.SubmitChanges();
}
private DataClasses1DataContext _context = null;
public DataClasses1DataContextContext
{
get
{
return _context ?? (_context = new DataClasses1DataContext());
}
}
You are extracting the data from this DB context:
public IEnumerable<MessageOut> GetSMSInformation()
{
using (var db = new DataClasses1DataContext())
{
var sms = db.MessageOuts.Where(msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
}
Then you are updating data from the previous DB context are you are submitting changes to a different DB context:
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime)
{
using (var db = new DataClasses1DataContext())
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
db.SubmitChanges();
}
}
Pass the DB context as a reference or best keep is as a member of your class. This ensures a single db context instance for your class instance.
My suggestion would be the following:
class SmsCenter
{
private DataClasses1DataContext _dbContext;
public SmsCenter()
{
_dbContext = new DataClasses1DataContext();
}
public IEnumerable<MessageOut> GetSMSInformation()
{
var sms = _dbContext.MessageOuts.Where(msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
public void StartSMSSending()
{
var information = GetSMSInformation();
foreach (var sms in information)
{
SendSMS(sms.reciever, sms.msg);
UpdateRecords(sms,"Delivered", DateTime.Now);
}
}
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime)
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
_dbContext.SubmitChanges();
}
}
you din't relate "sms" object with db, like
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
db.MessageOut.Add(sms);
//or
db.MessageOut=sms;
db.SubmitChanges();

Categories