Long-running Seed method with Entity Framework code-first - entity-framework-6

My Seed() method is getting rather long-running. Is there a way to write to the console whilst running the update-database command?
So far, Console.WriteLine() and Debug.WriteLine() have not written output to the Package Manager Console.

You can avoid run-on seed methods by extending the base DbContext class with a method that accepts an interface and returns the context instance to keep things fluent.
Though it doesn't solve the issue of not being able to write console/debug output in the package manager console, it will tell you which entity that you failed on while keeping things tidy.
Configuration.cs
using System.Configuration;
using MySolution.Data.Migrations.Seedings;
namespace MySolution.Data.Migrations
{
using System.Data.Entity.Migrations;
internal sealed class Configuration : DbMigrationsConfiguration<MySolution.Data.MySolutionContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(MySolution.Data.MySolutionContext context)
{
context
.AppendSeeding<AddressSeeding>(true)
.AppendSeeding<CustomerSeeding>(true)
.AppendSeeding<OrderSeeding>(true)
.AppendSeeding<ShipmentSeeding>(true)
.AppendSeeding<ApplicationSettingsSeeding>();
}
}
}
DbContextExtensions.cs
using System;
using System.Data.Entity;
namespace MySolution.Data.Migrations {
public static class DbContextExtensions {
public static DbContext AppendSeeding<T>(
this DbContext context, bool saveChanges = false)
where T : ISeeding, new() {
var seeding = new T();
try {
seeding.Seed(context);
if (saveChanges) {
context.SaveChanges();
}
}
catch (Exception ex) {
throw new Exception(
$"Unhandled exception while appending seed data via {seeding.GetType().Name}",
ex);
}
return context;
}
}
}
ISeeding.cs
using System.Data.Entity;
namespace MySolution.Data.Migrations {
public interface ISeeding {
void Seed(DbContext context);
}
}
AddressSeeding.cs (Example implementation of ISeeding)
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using MySolution.Data.Models;
namespace MySolution.Data.Migrations.Seedings {
public class AddressSeeding : ISeeding {
public void Seed(DbContext context) {
((MySolutionContext)context).Address.AddOrUpdate(
address => new { address.Address1},
new Address {
Address1 = "111 Fake St.",
City = "Nowhereland",
County = "Countyville",
State = "ST",
Zip = "12345",
AddressType = new AddressType {
Name = "Home"
},
}
);
}
}
}

Related

Resolve named Dependencies with Dependency Resolver

I've noticed that when I register my dependencies via named overrides Dependency Resolver struggles to resolve components properly. Seems like the first instance is provided. Everything is fine with ctor injection.
Example:
Registration
RegisterProvider<IAccountProvider, AccountProvider>();
RegisterProvider<IAccountProvider, CustomAccountProvider>("customAccountProvider");
Resolution
var instance = DependecyResolver.Current.GetService<IAccountProvider>();
Cannot retrieve customAccountProvider instance.
It always refers to the first registered component ignoring named constraints.
When you have multiple implementations of the same component you have to name them or mark them with marking interface. Here is a code example with naming the instances :
using System;
using System.Linq;
using System.Reflection;
using Castle.Facilities.TypedFactory;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
namespace ResolvingNamedInctances
{
class Program
{
static void Main(string[] args)
{
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<ITypedFactoryComponentSelector>().ImplementedBy<AccountProviderTypeSelector>());
container.Register(Component.For<IAccountProviderSelector>().AsFactory(typeof(AccountProviderTypeSelector)));
container.Register(Component.For<IAccountProvider>().ImplementedBy<DefaultAccountProvider>().Named("default"));
container.Register(Component.For<IAccountProvider>().ImplementedBy<CustomAccountProvider>().Named("custom"));
// uncomment this line in MVC app and use DependencyResolver instead of container
//DependencyResolver.SetResolver(new WindsorDependencyResolver(container.Kernel));
var accountProviderSelector = container.Resolve<IAccountProviderSelector>();
var defaultAccountProvider = accountProviderSelector.GetAccountProvider(); // default
defaultAccountProvider.Provide();
var customAccountProvider = accountProviderSelector.GetAccountProvider("custom");
customAccountProvider.Provide();
Console.ReadLine();
}
}
public class AccountProviderTypeSelector : ITypedFactoryComponentSelector
{
public Func<IKernelInternal, IReleasePolicy, object> SelectComponent(MethodInfo method, Type type, object[] arguments)
{
string providerName = arguments.Length > 0 ? (string)arguments[0] : "default";
return (k, r) => k.GetHandlers(typeof(IAccountProvider))
.Where(
h =>
{
return h.ComponentModel.Name == providerName;
})
.Select(h => (IAccountProvider)k.Resolve(
h.ComponentModel.Name,
typeof(IAccountProvider),
new Arguments { },
r))
.FirstOrDefault();
}
}
public interface IAccountProviderSelector
{
IAccountProvider GetAccountProvider();
IAccountProvider GetAccountProvider(string nameIdentifier);
}
public interface IAccountProvider
{
void Provide();
}
public class DefaultAccountProvider : IAccountProvider
{
public void Provide()
{
Console.WriteLine("Working in default AccountProvider");
}
}
public class CustomAccountProvider : IAccountProvider
{
public void Provide()
{
Console.WriteLine("Working in standart CustomAccountProvider");
}
}
}

Ninject Conditional Self bind to change scope (For Task-scheduler) not working properly?

Within MVC Web Application DbContext binding work properly with InRequestScope()
kernel.Bind<DbContext>().ToSelf().InRequestScope();
kernel.Bind<IUnitOfWork<DbContext>>().To<UnitOfWork<DbContext>>();
But from a Task Scheduler call DbContext in InRequestScope() unable to update Db Table (without any error), until I change Binding to InSingletonScope() OR InThreadScope()
Question: So is their any way change scope to InSingletonScope() / InThreadScope() for a Task Scheduler Call. ?
// For Task Scheduler Call, I tried bellow bind, but not working properly
kernel.Bind<DbContext>().ToSelf()
.When(request => request.Target.Type.Namespace.StartsWith("NameSpace.ClassName"))
.InSingletonScope();
** And probably I miss some thing. Need help.
Code Snippet Updated
#region Commented Code
public EmailTask() : this
( DependencyResolver.Current.GetService<IMessageManager>(),
, DependencyResolver.Current.GetService<IUnitOfWork<DbContext>>()) { }
#endregion
public EmailTask(IMessageManager messageManager, IUnitOfWork<DbContext> unitOfWork)
{
this._messageManager = messageManager;
this._unitOfWork = unitOfWork;
ProcessEmail();
}
public class NonRequestScopedParameter : IParameter { ... }
public void ProcessEmail()
{
var temp = SomeRepository.GetAll();
SendEmail(temp);
temp.Date = DateTime.Now;
SomeRepository.Update(temp);
unitOfWork.Commit();
}
public class ExecuteEmailTask : ITask
{
private readonly IResolutionRoot _resolutionRoot;
private int _maxTries = 5;
public ExecuteEmailTask(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
public void Execute(XmlNode node)
{
XmlAttribute attribute1 = node.Attributes["maxTries"];
if (attribute1 != null && !String.IsNullOrEmpty(attribute1.Value))
{
this._maxTries = int.Parse(attribute1.Value);
}
/// send email messages
var task = _resolutionRoot.Get<EmailTask>(new NonRequestScopedParameter());
}
}
In Web.Config
<ScheduleTasks>
<Thread seconds="60">
<task name="ExecuteEmailTask" type="namespace.ExecuteEmailTask, AssemblyName" enabled="true" stopOnError="false" maxTries="5"/>
</Thread>
</ScheduleTasks>
In Global.asax
protected void Application_Start()
{
/* intialize Task */
TaskConfig.Init();
TaskManager.Instance.Initialize(TaskConfig.ScheduleTasks);
TaskManager.Instance.Start();
}
Ninject Bind Syntax
kernel.Bind<DbContext>().ToSelf().InRequestScope(); // Default bind
kernel.Bind<DbContext>().ToSelf()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope(); // For Scheduler
Note: EmailTask class also have SomeReposity as a Constructor Argument.
Queries:-
But what is the bind syntax to resolve TaskScheduler(IResolutionRoot resolutionRoot) ?
What is the configuration code to run TaskScheduler ?
As say to put IFakeDbContext directly into constructor, can this work with IUnitOfWork<FakeDbContext> ?
Problem
Task unable to call with Overloaded Constructor , it is only able to call TaskScheduler default Constructor.
Question 4: Can any way to invoke TaskScheduler(IResolutionRoot resolutionRoot) from TaskScheduler default constructor ?
Sample Code Snippet to create Task & run using System.Threading.Timer
private ITask createTask()
{
if (this.Enabled && (this._task == null))
{
if (this._taskType != null)
{
this._task = Activator.CreateInstance(this._taskType) as ITask;
}
this._enabled = this._task != null;
}
return this._task;
}
Question 5: Can I resolve TaskScheduler(IResolutionRoot resolutionRoot) here ?
Solved
public ExecuteEmailTask() :
this(DependencyResolver.Current.GetService<IResolutionRoot>())
OR
public ExecuteEmailTask() : this(new Bootstrapper().Kernel) { }
public ExecuteEmailTask(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
First of, you should note that InSingletonScope() is usually a bad idea for DbContext's/Sessions. What happens if some other service changes data in the meantime? I would recommend investigating what effects this has.
For the scenario you first described, a correctly formulated .When(...) should work.
As an alternative to the .When(...) binding you could also use a .Named("FooBar") binding.
The constructor of the scheduled task would then need to look like:
ctor(Named["FooBar"] DbContext dbContext);
However, note, that this only (easily) works in case you need to inject the DbContext into a single constructor. If the task features dependencies and these need the same DbContext instance, too, it gets a bit tricker.
Since you updated your answer and say that this is the case, i would recommend an entirely different approach: Using a request parameter as basis for the When(...) condition combined with InCallScope binding. See below for an example.
Brace yourself, this is ab it of code :) The implementation requires the ninject.extensions.NamedScope extension (nuget).
I've also used xUnit and FluentAssertions nuget packages to execute the tests.
public class Test
{
// the two implementations are just for demonstration and easy verification purposes. You will only use one DbContext type.
public interface IFakeDbContext { }
public class RequestScopeDbContext : IFakeDbContext { }
public class CallScopeDbContext : IFakeDbContext { }
public class SomeTask
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency1 Dependency1 { get; set; }
public Dependency2 Dependency2 { get; set; }
public SomeTask(IFakeDbContext fakeDbContext, Dependency1 dependency1, Dependency2 dependency2)
{
FakeDbContext = fakeDbContext;
Dependency1 = dependency1;
Dependency2 = dependency2;
}
}
public class Dependency1
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency1(IFakeDbContext fakeDbContext)
{
FakeDbContext = fakeDbContext;
}
}
public class Dependency2
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency2(IFakeDbContext fakeDbContext)
{
FakeDbContext = fakeDbContext;
}
}
public class TaskScheduler
{
private readonly IResolutionRoot _resolutionRoot;
public TaskScheduler(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
public SomeTask CreateScheduledTaskNow()
{
return _resolutionRoot.Get<SomeTask>(new NonRequestScopedParameter());
}
}
public class NonRequestScopedParameter : Ninject.Parameters.IParameter
{
public bool Equals(IParameter other)
{
if (other == null)
{
return false;
}
return other is NonRequestScopedParameter;
}
public object GetValue(IContext context, ITarget target)
{
throw new NotSupportedException("this parameter does not provide a value");
}
public string Name
{
get { return typeof(NonRequestScopedParameter).Name; }
}
// this is very important
public bool ShouldInherit
{
get { return true; }
}
}
[Fact]
public void FactMethodName()
{
var kernel = new StandardKernel();
// this is the default binding
kernel.Bind<IFakeDbContext>().To<RequestScopeDbContext>();
// this binding is _only_ used when the request contains a NonRequestScopedParameter
// in call scope means, that all objects built in the a single request get the same instance
kernel.Bind<IFakeDbContext>().To<CallScopeDbContext>()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope();
// let's try it out!
var task = kernel.Get<SomeTask>(new NonRequestScopedParameter());
// verify that the correct binding was used
task.FakeDbContext.Should().BeOfType<CallScopeDbContext>();
// verify that all children of the task get injected the same task instance
task.FakeDbContext.Should()
.Be(task.Dependency1.FakeDbContext)
.And.Be(task.Dependency2.FakeDbContext);
}
}
Since, as you say, the task scheduler does not make use of the IoC to create the task, it only supports a parameterless constructor. In that case you can make use DependencyResolver.Current (however, note that i'm in no way an expert on asp.net /MVC so i'm not making any claims that this is thread safe or working 100% reliably):
public class TaskExecutor : ITask
{
public TaskExecutor()
: this(DependencyResolver.Current.GetService<IResolutionRoot>())
{}
internal TaskExecutor(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public void Execute()
{
IFooTask actualTask = this.resolution.Get<IFooTask>(new NonRequestScopedParameter());
actualTask.Execute();
}
}

How can I incorporate this Castle Windsor DI code into my Controller and Repository code?

Note: I can't bountify this question yet (it's too new), but I will reward a good answer with 50 points, and a great answer with 100 (when possible).
I need to incorporate DI into my Web API project. I currently have the expected Model and Controller folders/classes, along with corresponding Repository classes.
That seemed to work well for awhile, but now I need to use DI with the Controllers so that I can pass an Interface type to the Controllers' constructor.
I'm struggling with just how to implement this; that is, how to incorporate the DI "extravaganza" into my existing Model/Controller/Repository structure. I have example DI code, but I don't know just how it should be applied to my project.
Perhaps some code is in order to try to make this clear. I will show a simple sample of what I've got, followed by the DI code I'd like to somehow incorporate into it / with it.
Here is the existing Model/Controller/Repository code:
MODEL
public class Department
{
public int Id { get; set; }
public int AccountId { get; set; }
public string Name { get; set; }
}
CONTROLLER
public class DepartmentsController : ApiController
{
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
public int GetCountOfDepartmentRecords()
{
return _deptsRepository.Get();
}
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch)
{
return _deptsRepository.Get(ID, CountToFetch);
}
public void PostDepartment(int accountid, string name)
{
_deptsRepository.PostDepartment(accountid, name);
}
public HttpResponseMessage Post(Department department)
{
// Based on code 2/3 down http://www.codeproject.com/Articles/344078/ASP-NET-WebAPI-Getting-Started-with-MVC4-and-WebAP?msg=4727042#xx4727042xx
department = _deptsRepository.Add(department);
var response = Request.CreateResponse<Department>(HttpStatusCode.Created, department);
string uri = Url.Route(null, new { id = department.Id });
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
}
REPOSITORY
public class DepartmentRepository : IDepartmentRepository
{
private readonly List<Department> departments = new List<Department>();
public DepartmentRepository()
{
using (var conn = new OleDbConnection(
#"Provider=Microsoft.ACE.OLEDB.12.0;User ID=BlaBlaBla...
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT td_department_accounts.dept_no,
IIF(ISNULL(t_accounts.name),'No Name provided',t_accounts.name) AS name
FROM t_accounts INNER JOIN td_department_accounts ON
t_accounts.account_no = td_department_accounts.account_no ORDER BY
td_department_accounts.dept_no";
cmd.CommandType = CommandType.Text;
conn.Open();
int i = 1;
using (OleDbDataReader oleDbD8aReader = cmd.ExecuteReader())
{
while (oleDbD8aReader != null && oleDbD8aReader.Read())
{
int deptNum = oleDbD8aReader.GetInt16(0);
string deptName = oleDbD8aReader.GetString(1);
Add(new Department { Id = i, AccountId = deptNum, Name,
deptName });
i++;
}
}
}
}
}
public int Get()
{
return departments.Count;
}
private Department Get(int ID) // called by Delete()
{
return departments.First(d => d.Id == ID);
}
public IEnumerable<Department> Get(int ID, int CountToFetch)
{
return departments.Where(i => i.Id > ID).Take(CountToFetch);
}
public Department Add(Department dept)
{
if (dept == null)
{
throw new ArgumentNullException("Department arg was null");
}
// This is called internally, so need to disregard Id vals that already exist
if (dept.Id <= 0)
{
int maxId = departments.Max(d => d.Id);
dept.Id = maxId + 1;
}
if (departments != null) departments.Add(dept);
return dept;
}
public void PostDepartment(int accountid, string name)
{
int maxId = departments.Max(d => d.Id);
Department dept = new Department();
dept.Id = maxId + 1;
dept.AccountId = accountid;
dept.Name = name;
departments.Add(dept);
}
public void Post(Department department)
{
int maxId = departments.Max(d => d.Id);
department.Id = maxId + 1;
departments.Add(department);
}
public void Put(Department department)
{
int index = departments.ToList().FindIndex(p => p.Id == department.Id);
departments[index] = department;
}
public void Put(int id, Department department)
{
int index = departments.ToList().FindIndex(p => p.Id == id);
departments[index] = department;
}
public void Delete(int id)
{
Department dept = Get(id);
departments.Remove(dept);
}
And now here is the DI code that I want to incorporate
Classes in the DIInstallers folder:
IDepartmentProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HandheldServer.DIInstallers
{
public interface IDepartmentProvider
{
// These are the methods that are in the sample example IAuthProvider interface; I don't know what I need yet, though...
//bool Authenticate(string username, string password, bool createPersistentCookie);
//void SignOut();
}
}
DepartmentProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HandheldServer.DIInstallers
{
public class DepartmentProvider : IDepartmentProvider
{
// TODO: Implement methods in IDepartmentProvider, once they have been added
}
}
DepartmentProviderInstaller.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
namespace HandheldServer.DIInstallers
{
public class DepartmentProviderInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn(typeof(IDepartmentProvider))
.WithServiceAllInterfaces());
// If I declare/implement more interface types (other than IDepartmentProvider), I assume there would be another container.Register() call for each of them?
}
}
}
Classes in the DIPlumbing folder:
WindsorCompositionRoot.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.Windsor;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
namespace HandheldServer.DIPlumbing
{
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer container;
public WindsorCompositionRoot(IWindsorContainer container)
{
this.container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)this.container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
}
}
WindsorControllerFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;
namespace HandheldServer.DIPlumbing
{
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)kernel.Resolve(controllerType);
}
}
}
The Global.asax.cs file
using System;
using System.Reflection;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Castle.Windsor;
using Castle.Windsor.Installer;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.Http.Dispatcher;
using HandheldServer.DIPlumbing;
namespace HandheldServer
{
public class WebApiApplication : System.Web.HttpApplication
{
private static IWindsorContainer container;
protected void Application_Start()
{
BootstrapContainer();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
// Code that runs when an unhandled error occurs
void Application_Error(object sender, EventArgs e)
{
// Get the exception object.
Exception exc = Server.GetLastError();
log.Error(exc.Message);
// Clear the error from the server
Server.ClearError();
}
private static void BootstrapContainer()
{
container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot(container));
}
protected void Application_End()
{
container.Dispose();
}
}
}
So, I think I've basically got the code I need, but how to fold the DI code into my previous (Model/Controller/Repository) code is the part that has me stumped.
You can simply use WebApiContrib.IoC.CastleWindsor (Nuget).
This test should give you an idea of how to use it.

Join Multiple Transactions Across Instances of EF Connections

I have the following setup in my Data Layer
namespace DAL
{
public abstract class BaseDalObj:IDisposable
{
protected Auto.Entities entities;
public BaseDalObj()
{
entities= new Auto.Entities();
}
public void Dispose()
{
}
}
public class Class1: BaseDalObj
{
public void Save(object a)
{
entities.SaveItem(a);
}
}
public class Class2: BaseDalObj
{
public void Save(object b)
{
entities.SaveItem(b);
}
}
}
namespace Business
{
public class BusinessLL
{
public Object a,b;
public BusinessDAL()
{
a = new Object();
b = new Object();
}
public void Save()
{
using(var dbObj1 = new DAL.Class1())
{
dbObj1.Save(a);
using(var dbObj2 = new DAL.Class2())
{
dbObj2.Save(b);
}
}
}
}
}
what I would like is some way to call both Save functions but within the same TransactionScope.
I have no idea how i can do this using EF. I have looked into using the TransactionScope Class but I cannot get to grips with how it works.
I have been looking at using the entities.Connection.BeginTransaction() but that returns a DbTransaction and TransactionScope only accepts a type Transaction Class
Any help or pointers that can point me in the right way would really help.
If there is an ambient transaction present when opening a connection the connection will automatically enlist into this transaction. This applies to the ObjectContext too - when you call ObjectContext.SaveChanges it will automatically enlist the connection into the ambient transaction.
Therefore, I think this should work:
using(var transaction = new TransactionScope)
{
using(var class1 = new Class1())
{
class1.Save(x);
}
using(var class2 = new Class2())
{
class2.Save(y);
}
transaction.Complete();
}

Help Linq to Sql

Why am I getting a exception when ApplyPropertyChanges???
The code is almost the same when I'm editing a user table but is not working with my news table.
The create, delete and details are all working fine but when I try to edit a news I'm getting the exception below:
The ObjectStateManager does not contain a ObjectStateEntry 'MagixCMS.Models.noticia'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MagixCMS.Models
{
public class NoticiaRepository : INoticiaRepository
{
#region INoticiaRepository Members
magixcmsEntities _entities = new magixcmsEntities();
public noticia CreateNoticia(noticia noticiaToCreate)
{
_entities.AddTonoticiaSet(noticiaToCreate);
_entities.SaveChanges();
return noticiaToCreate;
}
public void DeletaNoticia(noticia noticiaToDelete)
{
var noticiaOriginal = GetNoticia(noticiaToDelete.Id);
_entities.DeleteObject(noticiaOriginal);
_entities.SaveChanges();
}
public noticia EditNoticia(noticia noticiaToEdit)
{
var noticiaOriginal = GetNoticia(noticiaToEdit.Id);
_entities.ApplyPropertyChanges(noticiaToEdit.EntityKey.EntitySetName, noticiaToEdit); //EXCEPTION HERE
_entities.SaveChanges();
return noticiaToEdit;
}
public noticia GetNoticia(int id)
{
return (from c in _entities.noticiaSet where c.Id == id select c).FirstOrDefault();
}
public IEnumerable<noticia> ListNoticias()
{
return _entities.noticiaSet.ToList();
}
#endregion
}
}
I google the exception and didn't found much help.
I solve it.
The problem is on the EF model.
To solve it you'll need a extension method to persist your data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace MagixCMS.Models
{
public static class Extensions
{
public static void Update(ObjectContext context, string entitySetName, IEntityWithKey entity)
{
entity.EntityKey = context.CreateEntityKey(entitySetName, entity);
context.Attach(entity);
var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
var propertyNameList = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name);
foreach (var propName in propertyNameList)
{
stateEntry.SetModifiedProperty(propName);
}
}
}
}
And in the Edit method you do:
public noticia EditNoticia(noticia noticiaToEdit)
{
//GET THE CONTEXT FOR THE ENTITY
ObjectContext _context = this._entities.noticiaSet.Context;
var noticiaOriginal = GetNoticia(noticiaToEdit.Id);
//UPDATE THE ORIGINAL ENTITY WITH THE NEW VALUES
Extensions.Update(_context, noticiaOriginal.EntityKey.EntitySetName, noticiaToEdit);
//PERSIST THE DATA
_entities.SaveChanges();
return noticiaToEdit;
}

Resources