I have a library (based on code found in an old blog post) that allows me to very easily wrap a façade around my data access using Entity Framework. It uses ObjectContext and has performed well enough for my purposes.
But now, we are excitedly investigating code first using DbContext and of course would like to reuse / adapt as much of our existing effort as possible.
Everything went ok naively converting our Facade enabling library with IObjectContextAdapter until we tried to utilise our facade, when the following error was received:
The type 'Employee' cannot be used as type parameter 'TEntity' in the generic type or method 'DbContextManagement.FacadeBase'. There is no implicit reference conversion from 'Employee' to 'System.Data.Objects.DataClasses.EntityObject'
MSDN says:
EntityObject derived types are not supported by the DbContext API, to use these entity types you must use the ObjectContext API.
That's fine, but how would I then go ahead completing my refactor to bypass this inability?
Here's some code (line breaks introduced):
FacadeBase.cs
namespace DbContextManagement
{
using System;
using System.Collections;
using System.Configuration;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Reflection;
public abstract class FacadeBase<TDbContext, TEntity>
where TDbContext : DbContext, new()
where TEntity : EntityObject
{
protected TDbContext DbContext
{
get
{
if (DbContextManager == null)
{
this.InstantiateDbContextManager();
}
return DbContextManager.GetDbContext<TDbContext>();
}
}
private DbContextManager DbContextManager { get; set; }
public virtual void Add(TEntity newObject)
{
var context = ((IObjectContextAdapter)this.DbContext).ObjectContext;
string entitySetName;
if (newObject.EntityKey != null)
{
entitySetName = newObject.EntityKey.EntitySetName;
}
else
{
string entityTypeName = newObject.GetType().Name;
var container = context.MetadataWorkspace.GetEntityContainer(
context.DefaultContainerName,
DataSpace.CSpace);
entitySetName = (from meta in container.BaseEntitySets
where meta.ElementType.Name ==
entityTypeName
select meta.Name).First();
}
context.AddObject(entitySetName, newObject);
}
public virtual void Delete(TEntity obsoleteObject)
{
var context = ((IObjectContextAdapter)this.DbContext).ObjectContext;
context.DeleteObject(obsoleteObject);
}
private void InstantiateDbContextManager()
{
var objectContextManagerConfiguration =
ConfigurationManager.GetSection("DbContext") as Hashtable;
if (objectContextManagerConfiguration != null &&
objectContextManagerConfiguration.ContainsKey("managerType"))
{
var managerTypeName =
objectContextManagerConfiguration["managerType"] as string;
if (string.IsNullOrEmpty(managerTypeName))
{
throw new ConfigurationErrorsException(
"The managerType attribute is empty.");
}
managerTypeName = managerTypeName.Trim().ToLower();
try
{
var frameworkAssembly =
Assembly.GetAssembly(typeof(DbContextManager));
var managerType =
frameworkAssembly.GetType(managerTypeName, true, true);
this.DbContextManager =
Activator.CreateInstance(managerType) as DbContextManager;
}
catch (Exception e)
{
throw new ConfigurationErrorsException(
"The managerType specified in the
configuration is not valid.", e);
}
}
else
{
throw new ConfigurationErrorsException(
"A Facade.DbContext tag or its managerType attribute
is missing in the configuration.");
}
}
}
}
EmployeeFacade.cs
namespace Facade
{
using System.Collections.Generic;
using System.Linq;
using DataModel;
using DataModel.Entities;
using DbContextManagement;
public sealed class EmployeeFacade : FacadeBase<FleetContext, Employee>
{
public Employee GetById(int? employeeId)
{
return employeeId == null
? null
: this.DbContext.Employees.FirstOrDefault(m => m.Id == employeeId);
}
}
}
Employee.cs
namespace DataModel.Entities
{
public class Employee
{
public int Id { get; set; }
public string Surname { get; set; }
public string Forename { get; set; }
public string EmployeeNumber { get; set; }
}
}
If your entities are derived from EntityObject and the code you want to reuse is dependent on EntityObject based entities then it is a show stopper. You cannot use DbContext API until your entities are POCOs (no EntityObject parent).
Btw. you can use code only mapping with ObjectContext (and POCOs) without ever using DbContext. You just need to:
Create EntityTypeConfiguration or ComplexTypeConfiguration based class for each your POCO entity / complex type describing the mapping
Use DbModelBuilder to collect your configurations and call Build to get DbModel instance
Call Compile on your DbModel instance to get DbCompiledModel
Cache compiled model for lifetime of your application
When you need a new ObjectContext instance call CreateObjectContext on compiled model
Just be aware that code mapping has much more limited feature set so not everything you currently have in EDMX will be possible to achieve through code mapping.
With a very large nod to the author of the original code in the link given above, here is what I ended up with. It passes basic scenarios but I haven't yet tried out deeper navigational and related queries. Even if there is an issue, I reckon it will be bug fix rather than show stop.
Here goes. The following is reusable, works with multiple contexts and means you can go to the database and get something back in 2 lines of actual code in the client.
DataModel (Class Library Project)
Your EF Code First project with DbContext POCOs, etc.
DbContextManagement (Class Library Project)
DbContextManager.cs
namespace DbContextManagement
{
using System.Data.Entity;
/// <summary>
/// Abstract base class for all other DbContextManager classes.
/// </summary>
public abstract class DbContextManager
{
/// <summary>
/// Returns a reference to an DbContext instance.
/// </summary>
/// <typeparam name="TDbContext">The type of the db context.</typeparam>
/// <returns>The current DbContext</returns>
public abstract TDbContext GetDbContext<TDbContext>()
where TDbContext : DbContext, new();
}
}
DbContextScope.cs
namespace DbContextManagement
{
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Threading;
/// <summary>
/// Defines a scope wherein only one DbContext instance is created, and shared by all of those who use it.
/// </summary>
/// <remarks>Instances of this class are supposed to be used in a using() statement.</remarks>
public class DbContextScope : IDisposable
{
/// <summary>
/// List of current DbContexts (supports multiple contexts).
/// </summary>
private readonly List<DbContext> contextList;
/// <summary>
/// DbContext scope definitiion.
/// </summary>
[ThreadStatic]
private static DbContextScope currentScope;
/// <summary>
/// Holds a value indicating whether the context is disposed or not.
/// </summary>
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="DbContextScope"/> class.
/// </summary>
/// <param name="saveAllChangesAtEndOfScope">if set to <c>true</c> [save all changes at end of scope].</param>
protected DbContextScope(bool saveAllChangesAtEndOfScope)
{
if (currentScope != null && !currentScope.isDisposed)
{
throw new InvalidOperationException("DbContextScope instances cannot be nested.");
}
this.SaveAllChangesAtEndOfScope = saveAllChangesAtEndOfScope;
this.contextList = new List<DbContext>();
this.isDisposed = false;
Thread.BeginThreadAffinity();
currentScope = this;
}
/// <summary>
/// Gets or sets a value indicating whether to automatically save all object changes at end of the scope.
/// </summary>
/// <value><c>true</c> if [save all changes at end of scope]; otherwise, <c>false</c>.</value>
private bool SaveAllChangesAtEndOfScope { get; set; }
/// <summary>
/// Save all object changes to the underlying datastore.
/// </summary>
public void SaveAllChanges()
{
var transactions = new List<DbTransaction>();
foreach (var context in this.contextList
.Select(dbcontext => ((IObjectContextAdapter)dbcontext)
.ObjectContext))
{
context.Connection.Open();
var databaseTransaction = context.Connection.BeginTransaction();
transactions.Add(databaseTransaction);
try
{
context.SaveChanges();
}
catch
{
/* Rollback & dispose all transactions: */
foreach (var transaction in transactions)
{
try
{
transaction.Rollback();
}
catch
{
// "Empty general catch clause suppresses any errors."
// Haven't quite figured out what to do here yet.
}
finally
{
databaseTransaction.Dispose();
}
}
transactions.Clear();
throw;
}
}
try
{
/* Commit all complete transactions: */
foreach (var completeTransaction in transactions)
{
completeTransaction.Commit();
}
}
finally
{
/* Dispose all transactions: */
foreach (var transaction in transactions)
{
transaction.Dispose();
}
transactions.Clear();
/* Close all open connections: */
foreach (var context in this.contextList
.Select(dbcontext => ((IObjectContextAdapter)dbcontext).ObjectContext)
.Where(context => context.Connection.State != System.Data.ConnectionState.Closed))
{
context.Connection.Close();
}
}
}
/// <summary>
/// Disposes the DbContext.
/// </summary>
public void Dispose()
{
// Monitor for possible future bugfix.
// CA1063 : Microsoft.Design : Provide an overridable implementation of Dispose(bool)
// on 'DbContextScope' or mark the type as sealed. A call to Dispose(false) should
// only clean up native resources. A call to Dispose(true) should clean up both managed
// and native resources.
if (this.isDisposed)
{
return;
}
// Monitor for possible future bugfix.
// CA1063 : Microsoft.Design : Modify 'DbContextScope.Dispose()' so that it calls
// Dispose(true), then calls GC.SuppressFinalize on the current object instance
// ('this' or 'Me' in Visual Basic), and then returns.
currentScope = null;
Thread.EndThreadAffinity();
try
{
if (this.SaveAllChangesAtEndOfScope && this.contextList.Count > 0)
{
this.SaveAllChanges();
}
}
finally
{
foreach (var context in this.contextList)
{
try
{
context.Dispose();
}
catch (ObjectDisposedException)
{
// Monitor for possible future bugfix.
// CA2202 : Microsoft.Usage : Object 'databaseTransaction' can be disposed
// more than once in method 'DbContextScope.SaveAllChanges()'.
// To avoid generating a System.ObjectDisposedException you should not call
// Dispose more than one time on an object.
}
}
this.isDisposed = true;
}
}
/// <summary>
/// Returns a reference to a DbContext of a specific type that is - or will be -
/// created for the current scope. If no scope currently exists, null is returned.
/// </summary>
/// <typeparam name="TDbContext">The type of the db context.</typeparam>
/// <returns>The current DbContext</returns>
protected internal static TDbContext GetCurrentDbContext<TDbContext>()
where TDbContext : DbContext, new()
{
if (currentScope == null)
{
return null;
}
var contextOfType = currentScope.contextList
.OfType<TDbContext>()
.FirstOrDefault();
if (contextOfType == null)
{
contextOfType = new TDbContext();
currentScope.contextList.Add(contextOfType);
}
return contextOfType;
}
}
}
FacadeBase.cs
namespace DbContextManagement
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Entity;
using System.Reflection;
/// <summary>
/// Generic base class for all other Facade classes.
/// </summary>
/// <typeparam name="TDbContext">The type of the db context.</typeparam>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TEntityKey">The type of the entity key.</typeparam>
/// <remarks>Not sure the handling of TEntityKey is something I've worked with properly.</remarks>
public abstract class FacadeBase<TDbContext, TEntity, TEntityKey>
where TDbContext : DbContext, new()
where TEntity : class
{
/// <summary>
/// Gets the db context.
/// </summary>
public TDbContext DbContext
{
get
{
if (DbContextManager == null)
{
this.InstantiateDbContextManager();
}
return DbContextManager != null
? DbContextManager.GetDbContext<TDbContext>()
: null;
}
}
/// <summary>
/// Gets or sets the DbContextManager.
/// </summary>
/// <value>The DbContextManager.</value>
private DbContextManager DbContextManager { get; set; }
/// <summary>
/// Adds a new entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Add(TEntity newObject)
{
this.DbContext.Set<TEntity>().Add(newObject);
}
/// <summary>
/// Deletes an entity object.
/// </summary>
/// <param name="obsoleteObject">An obsolete object.</param>
public virtual void Delete(TEntity obsoleteObject)
{
this.DbContext.Set<TEntity>().Remove(obsoleteObject);
}
/// <summary>
/// Gets all entities for the given type.
/// </summary>
/// <returns>DbContext Set of TEntity.</returns>
public virtual IEnumerable<TEntity> GetAll()
{
return this.DbContext.Set<TEntity>();
}
/// <summary>
/// Gets the entity by the specified entity key.
/// </summary>
/// <param name="entityKey">The entity key.</param>
/// <returns>Entity matching specified entity key or null if not found.</returns>
public virtual TEntity GetByKey(TEntityKey entityKey)
{
return this.DbContext.Set<TEntity>().Find(entityKey);
}
/// <summary>
/// Deletes the entity for the specified entity key.
/// </summary>
/// <param name="entityKey">The entity key.</param>
public virtual void DeleteByKey(TEntityKey entityKey)
{
var entity = this.DbContext.Set<TEntity>().Find(entityKey);
if (entity != null)
{
this.DbContext.Set<TEntity>().Remove(entity);
}
}
/// <summary>
/// Instantiates a new DbContextManager based on application configuration settings.
/// </summary>
private void InstantiateDbContextManager()
{
/* Retrieve DbContextManager configuration settings: */
var contextManagerConfiguration = ConfigurationManager.GetSection("DbContext") as Hashtable;
if (contextManagerConfiguration == null)
{
throw new ConfigurationErrorsException("A Facade.DbContext tag or its managerType attribute is missing in the configuration.");
}
if (!contextManagerConfiguration.ContainsKey("managerType"))
{
throw new ConfigurationErrorsException("dbManagerConfiguration does not contain key 'managerType'.");
}
var managerTypeName = contextManagerConfiguration["managerType"] as string;
if (string.IsNullOrEmpty(managerTypeName))
{
throw new ConfigurationErrorsException("The managerType attribute is empty.");
}
managerTypeName = managerTypeName.Trim().ToUpperInvariant();
try
{
/* Try to create a type based on it's name: */
var frameworkAssembly = Assembly.GetAssembly(typeof(DbContextManager));
var managerType = frameworkAssembly.GetType(managerTypeName, true, true);
/* Try to create a new instance of the specified DbContextManager type: */
this.DbContextManager = Activator.CreateInstance(managerType) as DbContextManager;
}
catch (Exception e)
{
throw new ConfigurationErrorsException("The managerType specified in the configuration is not valid.", e);
}
}
}
}
ScopedDbContextManager.cs
namespace DbContextManagement
{
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
/// <summary>
/// Manages multiple db contexts.
/// </summary>
public sealed class ScopedDbContextManager : DbContextManager
{
/// <summary>
/// List of Object Contexts.
/// </summary>
private List<DbContext> contextList;
/// <summary>
/// Returns the DbContext instance that belongs to the current DbContextScope.
/// If currently no DbContextScope exists, a local instance of an DbContext
/// class is returned.
/// </summary>
/// <typeparam name="TDbContext">The type of the db context.</typeparam>
/// <returns>Current scoped DbContext.</returns>
public override TDbContext GetDbContext<TDbContext>()
{
var currentDbContext = DbContextScope.GetCurrentDbContext<TDbContext>();
if (currentDbContext != null)
{
return currentDbContext;
}
if (this.contextList == null)
{
this.contextList = new List<DbContext>();
}
currentDbContext = this.contextList.OfType<TDbContext>().FirstOrDefault();
if (currentDbContext == null)
{
currentDbContext = new TDbContext();
this.contextList.Add(currentDbContext);
}
return currentDbContext;
}
}
}
UnitOfWorkScope.cs
namespace DbContextManagement
{
/// <summary>
/// Defines a scope for a business transaction. At the end of the scope all object changes can be persisted to the underlying datastore.
/// </summary>
/// <remarks>Instances of this class are supposed to be used in a using() statement.</remarks>
public sealed class UnitOfWorkScope : DbContextScope
{
/// <summary>
/// Initializes a new instance of the <see cref="UnitOfWorkScope"/> class.
/// </summary>
/// <param name="saveAllChangesAtEndOfScope">if set to <c>true</c> [save all changes at end of scope].</param>
public UnitOfWorkScope(bool saveAllChangesAtEndOfScope)
: base(saveAllChangesAtEndOfScope)
{
}
}
}
Facade (Class Library Project)
YourEntityFacade.cs
namespace Facade
{
using System.Collections.Generic;
using System.Linq;
using DataModel;
using DataModel.Entities;
using DbContextManagement;
public class YourEntityFacade : FacadeBase<YourDbContext, YourEntity, int>
{
public override IEnumerable<YourEntity> GetAll()
{
return base.GetAll()
.Distinct()
.ToList();
}
}
}
TestConsole (Console Application Project)
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="DbContext" type="System.Configuration.SingleTagSectionHandler" />
</configSections>
<DbContext managerType="DbContextManagement.ScopedDbContextManager" />
<connectionStrings>
<add
name="YourDbContext"
providerName="System.Data.SqlClient"
connectionString="Your connection string" />
</connectionStrings>
</configuration>
Program.cs
namespace TestConsole
{
using System;
using System.Collections.Generic;
using DataModel.Entities;
using DbContextManagement;
using Facade;
public static class Program
{
public static void Main()
{
TestGetAll();
Console.ReadLine();
}
private static void TestGetAll()
{
Console.WriteLine();
Console.WriteLine("Test GetAll()");
Console.WriteLine();
IEnumerable<YourEntity> yourEntities;
using (new UnitOfWorkScope(false))
{
yourEntities= new YourEntityFacade().GetAll();
}
if (yourEntities != null)
{
foreach (var yourEntity in yourEntities)
{
Console.WriteLine(
string.Format("{0}, {1}",
yourEntity.Id,
yourEntity.Name));
}
}
else
{
Console.WriteLine("GetAll() NULL");
}
}
}
}
So, usage is very simple and you can work with multiple contexts and facades within a scope. With this approach, the only code you are writing is custom queries. No more endless updating UnitOfWorks with repository references and authoring copy-cat repositories. I think it's great but be aware this is beta code and I'm sure it has a large hole somewhere that will need plugging :)
Thank you to all and in particular Ladislav for patience and help on this and many other related questions I ask. I hope this code is of interest. I contacted the author at the blog above but no reply yet and these days I think he's into NHibernate.
Richard
Related
Im working on a C# Project that uses Entity Framework, Repository-Pattern, UnitOfWork-Pattern and Moq.
I´m new to EF and Unit Tests with Moq and got the following problem:
I get null pointers, when i try to test the methods from the service class...and it seems as if the context can´t get instantiated. Can anyone point my mistake out or provide me with a link?
Example:
portionService.cs
/// <summary>
/// The PortionService class represents a service for the Portion model.
/// </summary>
public class PortionService : Service, IPortionService
{
/// <summary>
/// In this constructor the base constructor of the Service class is called.
/// </summary>
/// <param name="context">Represents a context of the data access layer.</param>
public PortionService(IDALContext context) : base(context) { }
public void Add(Portion portion)
{
context.Portion.Create(portion);
context.SaveChanges();
}
public Portion GetPortionByName(string name)
{
return context.Portion.GetAll().Where(p => p.Name.ToUpper() == name.ToUpper()).LastOrDefault();
}
portionServiceTests.cs
// TestClass for PortionService-Tests
[TestClass]
public class PortionServiceTests
{
private PortionService _portionService;
// define the mock object
private Mock<IPortionService> _portionServiceMock;
[TestInitialize]
public void Init()
{
_portionService = new PortionService(new DALContext());
// create the mock object
_portionServiceMock = new Mock<IPortionService>();
}[TestMethod]
public void EnteringPortionNameReturnsThePortion()
{
//arrange
// arrange data
Portion portion = new Portion { PortionID = 12, Name = "testPortion" };
//arrange expectations
_portionServiceMock.Setup(service => service.GetPortionByName("testPortion")).Returns(portion).Verifiable();
//act
var result = _portionService.GetPortionByName("testPortion");
//verify
Assert.AreEqual(portion, result.Name);
}
DALContext.cs
public class DALContext : IDALContext, IDisposable
{
/// <summary>
/// The _context property represents the context to the current Database.
/// </summary>
private DatabaseContext _context;
private Repository<Portion> _portionRepository;
...
/// <summary>
/// In this constructor the single instance of the DataBaseContext gets instantiated.
/// </summary>
public DALContext()
{
_context = new DatabaseContext();
}
You are trying to verify the mock result to an actual data in the database, that's why it fails. Your unit test should test the service, and that the service calls the context, not the mock of the service itself.
The following example uses FakeDbSet approach from Rowan Miller's article.
using System.Data.Entity;
using System.Linq;
using Moq;
using NUnit.Framework;
using SharpTestsEx;
namespace StackOverflowExample.EntityFramework
{
public class DataEntity
{
public int Id { get; set; }
public string Data { get; set; }
}
public interface IContext
{
IDbSet<DataEntity> DataEntities { get; }
}
public class DataService
{
private IContext _db;
public DataService(IContext context)
{
_db = context;
}
public DataEntity GetDataById(int id)
{
return _db.DataEntities.First(d => d.Id == id);
}
}
[TestFixture]
public class DataServiceTests
{
[Test]
public void GetDataByIdTest()
{
//arrange
var datas = new FakeDbSet<DataEntity>
{
new DataEntity {Id = 1, Data = "one"},
new DataEntity {Id = 2, Data = "two"}
};
var context = new Mock<IContext>();
context.SetupGet(c => c.DataEntities).Returns(datas);
var service = new DataService(context.Object);
//act
var result = service.GetDataById(2);
//assert
result.Satisfy(r =>
r.Id == 2
&& r.Data == "two");
}
}
}
it's actually not easy to unit test EF based application. I would recommend using a library like effort to mock the entity framework.
I implemented my repository pattern and unit of work in the following way:
How to create a simple data access layer
How to create unit of work
and I was asked if create multiple instance of my database (which is the unit of work) will cause memory leak? and what are the downside of such implementation? or is the implementation done right?
Thanks.
What you should worry about is the creation of the DbContext as this is expensive.
I typically use one DbContext per request instead of creating multiple DbContext instances everywhere.
I store it in the HttpContext object and dispose of it at the end of the request.
You can find more info on it here
Find below my modified implementation. (original was from the link above and used ObjectContext).
My context class
using System.Data.Entity;
using System.Data.Objects;
using System.Web;
using Fot.Admin.Models;
namespace Context
{
public static class ContextManager
{
internal const string DB = "MY_DB_CONTEXT";
/// <summary>
/// Get an instance that lives for the life time of the request per user and automatically disposes.
/// </summary>
/// <returns>Model</returns>
public static T AsSingleton<T>() where T : DbContext, new()
{
HttpContext.Current.Items[DB] = (T)HttpContext.Current.Items[DB] ?? new T();
return (T)HttpContext.Current.Items[DB];
}
}
}
My Context Module
using System;
using System.Data.Entity;
using System.Data.Objects;
using System.Web;
namespace Context
{
/// <summary>
/// Entity Module used to control an Entities DB Context over the lifetime of a request per user.
/// </summary>
public class ContextModule : IHttpModule
{
private const string DB = ContextManager.DB;
void context_EndRequest(object sender, EventArgs e)
{
Dispose();
}
#region IHttpModule Members
public void Dispose()
{
if(HttpContext.Current != null)
{
if (HttpContext.Current.Items[DB] != null)
{
var entitiesContext = (DbContext) HttpContext.Current.Items[DB];
entitiesContext.Dispose();
HttpContext.Current.Items.Remove(DB);
}
}
}
public void Init(HttpApplication context)
{
context.EndRequest += new EventHandler(context_EndRequest);
}
#endregion
}
}
In my web.config under <httpModules> i add this below.
<add name="ContextModule" type="Context.ContextModule" />
This ensures that the end_Request is called after every request so you can dispose the context properly.
When you need the DbContext usage is as below.
var Context = ContextManager.AsSingleton<MyDBContext>();
I have a xaml code as follows:
On MouseLeftButtonDown i redirect it to viewModel where the color GenerateGlowEffect must change. This is not reflected. It always returns null for this.PropertyChanged
XAML:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
xmlns:telerikDocking="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Docking"
xmlns:Telerik_Windows_Controls="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
xmlns:System="clr-namespace:System;assembly=mscorlib" x:Class="TabItemContents.MapDetail"
mc:Ignorable="d" Width="Auto" Height="430.333">
<Grid x:Name="LayoutRoot" Background="{StaticResource TabControlActiveAreaColor}" Height="430.333" VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid.RowDefinitions>
<RowDefinition Height="192.833"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle IsHitTestVisible="True" x:Name="Rectangle_Generate" Margin="25.334,70.167,0,65.333" Cursor="{DynamicResource HandCursor}" RadiusX="10" RadiusY="10" HorizontalAlignment="Left" Width="88.5" StrokeThickness="1.5" d:LayoutOverrides="HorizontalAlignment" MouseLeftButtonDown="Rectangle_Generate_MouseLeftButtonDown" >
<Rectangle.Fill>
<SolidColorBrush Color="{DynamicResource RectangleColor}"/>
</Rectangle.Fill>
<Rectangle.Effect >
<DropShadowEffect RenderingBias="Quality" BlurRadius="{DynamicResource BlurRadius}" ShadowDepth="0" Color="{Binding GenerateGlowEffect}" />
</Rectangle.Effect>
</Rectangle>
</Grid>
CODEBEHIND:
public partial class MapDetail : UserControl
{
ViewModel ViewModelObject;
public MapDetail()
{
InitializeComponent();
ViewModelObject= new ViewModelObject((IUnityContainer)Application.Current.Properties["UnityContainer"]);
}
private void Rectangle_Generate_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ViewModelObject.SetColor();
}
}
VIEWMODEL:
public class ViewModel : ViewModelBase
{
private IUnityContainer _unityContainer;
private ILogger _logger;
private Model model;
private string _generateGlowEffect;
public ViewModel(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
try
{
model = new Model(unityContainer);
}
catch (Exception ex)
{
throw ex;
}
}
public void SetColor()
{
GenerateGlowEffect = "White";
}
public string GenerateGlowEffect
{
get { return _generateGlowEffect; }
set {
_generateGlowEffect = value;
OnPropertyChanged("GenerateGlowEffect");
}
}
}
}
public string GenerateGlowEffect
{
get { return _generateGlowEffect; }
set {
_generateGlowEffect = value;
OnPropertyChanged("GenerateGlowEffect");
}
}
ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged, IDisposable
{
#region Constructor
protected ViewModelBase()
{
}
#endregion // Constructor
#region DisplayName
/// <summary>
/// Returns the user-friendly name of this object.
/// Child classes can set this property to a new value,
/// or override it to determine the value on-demand.
/// </summary>
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
#region IDisposable Members
/// <summary>
/// Invoked when this object is being removed from the application
/// and will be subject to garbage collection.
/// </summary>
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// Child classes can override this method to perform
/// clean-up logic, such as removing event handlers.
/// </summary>
protected virtual void OnDispose()
{
}
#endregion // IDisposable Members
}
}
If this.PropertyChanged is null it means that your XAML is not bound against the property.
You need to set the DataContext otherwise the WPF engine doesn't know what to bind against.
public MapDetail()
{
InitializeComponent();
this.ViewModelObject= new VViewModelObject((IUnityContainer)Application.Current.Properties["UnityContainer"]);
this.DataContext = this.ViewModelObject;
}
I'm using entity framework and trying to unit test my data services which are using EF.
I'm not using repository and unit of work patterns.
I tried the following approach to mock the context and DbSet:
private static Mock<IEFModel> context;
private static Mock<IDbSet<CountryCode>> idbSet;
[ClassInitialize]
public static void Initialize(TestContext testContext)
{
context = new Mock<IEFModel>();
idbSet = new Mock<IDbSet<CountryCode>>();
context.Setup(c => c.CountryCodes).Returns(idbSet.Object);
}
I get null "Object reference not set to an instance of an object" error for idbSet "Local".
Is there any way to mock idbSet like this?
Thanks
I worked it out like this:
Created two classes named DbSetMock:
public class DbSetMock<T> : IDbSet<T>
where T : class
{
#region Fields
/// <summary>The _container.</summary>
private readonly IList<T> _container = new List<T>();
#endregion
#region Public Properties
/// <summary>Gets the element type.</summary>
public Type ElementType
{
get
{
return typeof(T);
}
}
/// <summary>Gets the expression.</summary>
public Expression Expression
{
get
{
return this._container.AsQueryable().Expression;
}
}
/// <summary>Gets the local.</summary>
public ObservableCollection<T> Local
{
get
{
return new ObservableCollection<T>(this._container);
}
}
/// <summary>Gets the provider.</summary>
public IQueryProvider Provider
{
get
{
return this._container.AsQueryable().Provider;
}
}
#endregion
#region Public Methods and Operators
/// <summary>The add.</summary>
/// <param name="entity">The entity.</param>
/// <returns>The <see cref="T"/>.</returns>
public T Add(T entity)
{
this._container.Add(entity);
return entity;
}
/// <summary>The attach.</summary>
/// <param name="entity">The entity.</param>
/// <returns>The <see cref="T"/>.</returns>
public T Attach(T entity)
{
this._container.Add(entity);
return entity;
}
/// <summary>The create.</summary>
/// <typeparam name="TDerivedEntity"></typeparam>
/// <returns>The <see cref="TDerivedEntity"/>.</returns>
/// <exception cref="NotImplementedException"></exception>
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
throw new NotImplementedException();
}
/// <summary>The create.</summary>
/// <returns>The <see cref="T"/>.</returns>
/// <exception cref="NotImplementedException"></exception>
public T Create()
{
throw new NotImplementedException();
}
/// <summary>The find.</summary>
/// <param name="keyValues">The key values.</param>
/// <returns>The <see cref="T"/>.</returns>
/// <exception cref="NotImplementedException"></exception>
public T Find(params object[] keyValues)
{
throw new NotImplementedException();
}
/// <summary>The get enumerator.</summary>
/// <returns>The <see cref="IEnumerator"/>.</returns>
public IEnumerator<T> GetEnumerator()
{
return this._container.GetEnumerator();
}
/// <summary>The remove.</summary>
/// <param name="entity">The entity.</param>
/// <returns>The <see cref="T"/>.</returns>
public T Remove(T entity)
{
this._container.Remove(entity);
return entity;
}
#endregion
#region Explicit Interface Methods
/// <summary>The get enumerator.</summary>
/// <returns>The <see cref="IEnumerator"/>.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return this._container.GetEnumerator();
}
#endregion
}
and EFModelMock:
public class EFModelMock : IEFModel
{
#region Fields
/// <summary>The country codes.</summary>
private IDbSet<CountryCode> countryCodes;
#endregion
#region Public Properties
/// <summary>Gets the country codes.</summary>
public IDbSet<CountryCode> CountryCodes
{
get
{
this.CreateCountryCodes();
return this.countryCodes;
}
}
#endregion
#region Public Methods and Operators
/// <summary>The commit.</summary>
/// <exception cref="NotImplementedException"></exception>
public void Commit()
{
throw new NotImplementedException();
}
/// <summary>The set.</summary>
/// <typeparam name="T"></typeparam>
/// <returns>The <see cref="IDbSet"/>.</returns>
/// <exception cref="NotImplementedException"></exception>
public IDbSet<T> Set<T>() where T : class
{
throw new NotImplementedException();
}
#endregion
#region Methods
/// <summary>The create country codes.</summary>
private void CreateCountryCodes()
{
if (this.countryCodes == null)
{
this.countryCodes = new DbSetMock<CountryCode>();
this.countryCodes.Add(
new CountryCode { CountryName = "Australia", DisplayLevel = 2, TelephoneCode = "61" });
}
}
#endregion
}
Then tested like this:
[TestClass]
public class CountryCodeServiceTest
{
#region Static Fields
/// <summary>The context.</summary>
private static IEFModel context;
#endregion
#region Public Methods and Operators
/// <summary>The initialize.</summary>
/// <param name="testContext">The test context.</param>
[ClassInitialize]
public static void Initialize(TestContext testContext)
{
context = new EFModelMock();
}
/// <summary>The country code service get country codes returns correct data.</summary>
[TestMethod]
public void CountryCodeServiceGetCountryCodesReturnsCorrectData()
{
// Arrange
var target = new CountryCodeService(context);
var countryName = "Australia";
var expected = context.CountryCodes.ToList();
// Act
var actual = target.GetCountryCodes();
// Assert
Assert.IsNotNull(actual);
Assert.AreEqual(actual.FirstOrDefault(a => a.CountryName == countryName).PhoneCode, expected.FirstOrDefault(a => a.CountryName == countryName).TelephoneCode);
}
You have to set up the Local property of your idbSet mock.
For example:
idbSet = new Mock<IDbSet<CountryCode>>();
var col = new ObservableCollection<CountryCode>();
idbSet.SetupGet(x => x.Local).Returns(col);
I had been using a method to create my mock sets like this:
public static Mock<IDbSet<T>> CreateMockSet<T>(IQueryable<T> data) where T : class
{
var mockSet = new Mock<IDbSet<T>>();
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
return mockSet;
}
I merely added this line:
mockSet.Setup(x => x.Local).Returns(new ObservableCollection<T>());
before the return statement and it solved my problems.
Many of my queries look like this:
var myset = context.EntitySetName.Local.SingleOrDefault(x=>x.something==something)
??
context.SingleOrDefault(x=>x.something==something);
So I just need Local to not be null so that it doesn't throw a null reference exception.
I am trying to use my generic repository with a "unit of work" pattern.
Here is my work details
public class GenericRepository:IRepository
{
private readonly string _connectionStringName;
private ObjectContext _objectContext;
private readonly PluralizationService _pluralizer = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en"));
public GenericRepository()
{
this._objectContext = ContextManager.CurrentFor();
}
public void Add<TEntity>(TEntity entity) where TEntity : class
{
((DataEntities.MyTestDBEntities)_objectContext).Countries.AddObject(new Country() { CountryName="UGANDA"});
this._objectContext.AddObject(GetEntityName<TEntity>(), entity);
}
public void Update<TEntity>(TEntity entity) where TEntity : class
{
var fqen = GetEntityName<TEntity>();
object originalItem;
EntityKey key = ObjectContext.CreateEntityKey(fqen, entity);
if (ObjectContext.TryGetObjectByKey(key, out originalItem))
{
ObjectContext.ApplyCurrentValues(key.EntitySetName, entity);
}
}
private string GetEntityName<TEntity>() where TEntity : class
{
return string.Format("{0}.{1}", ObjectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));
}
public object Get<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return ObjectContext.CreateQuery<TEntity>(entityName);
}
public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
{
return GetQuery<TEntity>().Where(criteria);
}
private IUnitOfWork unitOfWork;
public ObjectContext ObjectContext
{
get { return ContextManager.CurrentFor(); }
}
public IUnitOfWork UnitOfWork
{
get
{
if (unitOfWork == null)
{
unitOfWork = new UnitOfWork(this.ObjectContext);
}
return unitOfWork;
}
}
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return ObjectContext.CreateQuery<TEntity>(entityName);
}
}
then I will redirect save changes and other committing the transaction with UnitOfWork.cs
public class UnitOfWork:IUnitOfWork
{
private DbTransaction _transaction;
private ObjectContext _objectContext;
public UnitOfWork(ObjectContext context)
{
_objectContext = context;
}
public bool IsInTransaction
{
get { return _transaction != null; }
}
public void BeginTransaction()
{
BeginTransaction(IsolationLevel.ReadCommitted);
}
public void BeginTransaction(IsolationLevel isolationLevel)
{
if (_transaction != null)
{
throw new ApplicationException("Cannot begin a new transaction while an existing transaction is still running. " +
"Please commit or rollback the existing transaction before starting a new one.");
}
OpenConnection();
_transaction = _objectContext.Connection.BeginTransaction(isolationLevel);
}
public void RollBackTransaction()
{
if (_transaction == null)
{
throw new ApplicationException("Cannot roll back a transaction while there is no transaction running.");
}
try
{
_transaction.Rollback();
}
catch
{
throw;
}
finally
{
ReleaseCurrentTransaction();
}
}
public void CommitTransaction()
{
if (_transaction == null)
{
throw new ApplicationException("Cannot roll back a transaction while there is no transaction running.");
}
try
{
_objectContext.SaveChanges();
_transaction.Commit();
}
catch
{
_transaction.Rollback();
throw;
}
finally
{
ReleaseCurrentTransaction();
}
}
public void SaveChanges()
{
if (IsInTransaction)
{
throw new ApplicationException("A transaction is running. Call BeginTransaction instead.");
}
_objectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
}
public void SaveChanges(SaveOptions saveOptions)
{
if (IsInTransaction)
{
throw new ApplicationException("A transaction is running. Call BeginTransaction instead.");
}
_objectContext.SaveChanges(saveOptions);
}
/// <summary>
/// Releases the current transaction
/// </summary>
private void ReleaseCurrentTransaction()
{
if (_transaction != null)
{
_transaction.Dispose();
_transaction = null;
}
}
private void OpenConnection()
{
if (_objectContext.Connection.State != ConnectionState.Open)
{
_objectContext.Connection.Open();
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the managed and unmanaged resources.
/// </summary>
/// <param name="disposing"></param>
private void Dispose(bool disposing)
{
if (!disposing)
return;
if (_disposed)
return;
ReleaseCurrentTransaction();
_disposed = true;
}
private bool _disposed;
}
and I am getting my context through my ContextManager class:
public class ContextManager
{
/// <summary>
/// The default connection string name used if only one database is being communicated with.
/// </summary>
public static readonly string DefaultConnectionStringName = "DefaultDb";
/// <summary>
/// An application-specific implementation of IObjectContextStorage must be setup either thru
/// <see cref="InitStorage" /> or one of the <see cref="Init" /> overloads.
/// </summary>
private static IObjectContextStorage Storage { get; set; }
/// <summary>
/// Maintains a dictionary of object context builders, one per database. The key is a
/// connection string name used to look up the associated database, and used to decorate respective
/// repositories. If only one database is being used, this dictionary contains a single
/// factory with a key of <see cref="DefaultConnectionStringName" />.
/// </summary>
// private static Dictionary<string, IObjectContextBuilder<ObjectContext>> objectContextBuilders = new Dictionary<string, IObjectContextBuilder<ObjectContext>>();
private static object _syncLock = new object();
/// <summary>
/// Used to get the current object context session if you're communicating with a single database.
/// When communicating with multiple databases, invoke <see cref="CurrentFor()" /> instead.
/// </summary>
public static ObjectContext Current
{
get { return CurrentFor(); }
}
/// <summary>
/// Used to get the current ObjectContext associated with a key; i.e., the key
/// associated with an object context for a specific database.
///
/// If you're only communicating with one database, you should call <see cref="Current" /> instead,
/// although you're certainly welcome to call this if you have the key available.
/// </summary>
public static ObjectContext CurrentFor()
{
ObjectContext context = null;
lock (_syncLock)
{
if (context == null)
{
context =new TestDAL.DataEntities.MyTestDBEntities();
//Storage.SetObjectContextForKey(key, context);
}
}
return context;
}
/// <summary>
/// This method is used by application-specific object context storage implementations
/// and unit tests. Its job is to walk thru existing cached object context(s) and Close() each one.
/// </summary>
public static void CloseAllObjectContexts()
{
if (CurrentFor().Connection.State == System.Data.ConnectionState.Open)
{
CurrentFor().Connection.Close();
}
}
}
it gives me retrieval of entities, but when I want to create an entity it doesn't shows nay error nor any update in the database.
Any clue will be helpful.
Your public static ObjectContext CurrentFor() method will always create a new context. And your queries are using the ObjectContext property
public ObjectContext ObjectContext
{
get { return ContextManager.CurrentFor(); }
}
Hence you are using multiple instances of ObjectContext. You are calling SaveChanges() of a different instance of ObjectContext. So no changes will be persisted.
Do not handle the transactions explicitly as you did in UnitOfWork. The ObjectContext will do that part.
Your design is a complicated abstraction. Try to use the framework as it is or find a simple Repository pattern which has already being tested an used.