I would like to take a model of a table and make a viewmodel with additions fields, then populate it as a view. How do I do this in the controller? When it was just a model it worked but I'm not sure how to do the same thing when it's a viewmodel. I'm new to asp.net mvc so any help would be appreciated.
Model:
public partial class tblTag
{
public int TagId { get; set; }
public string TagName { get; set; }
}
ViewModel:
public class tblTagViewModel
{
public string TagName { get; set; }
public string TagNameClr
{
get
{
if (TagName == "Test")
{
return "green";
}
else
{
return "red";
}
}
}
}
Controller:
Cannot implicitly convert type 'System.Data.Entity.DbSet<_1MvcSqlServer.Models.tblTag>' to '_1MvcSqlServer.ViewModel.tblTagViewModel'
Now that it's a viewmodel the structure from entities is different, I assume that is the problem. How do I resolve this?
private testEntities db = new testEntities();
public ActionResult Test()
{
ViewModel.tblTagViewModel model = new ViewModel.tblTagViewModel();
model = db.tblTags;
return(model);
}
Here is what I ended up with to get it to work. Is this the correct method?
public ActionResult Test()
{
List<ViewModel.tblTagViewModel> list = new List<ViewModel.tblTagViewModel>();
var model = new ViewModel.tblTagViewModel();
foreach(tblTag p in db.tblTags)
{
ViewModel.tblTagViewModel nw = new ViewModel.tblTagViewModel();
nw.TagName = p.TagName;
list.Add(nw);
}
return View(list);
}
Correct method - create Business layer, which would get data from DB layer and pass this data to View layer (and vice versa, get data from View layer and pass to DB layer).
Also, I recommend to look at IoC (DI) approach :
Business Layer:
First at all, you create new ClassLibrary project and create Business layer POCO classes and an abstraction of your repository:
/// <summary>
/// this is a Business layer class, which is very similar on EF class
/// </summary>
public class Tag
{
public int TagId { get; set; }
public string TagName { get; set; }
}
public interface IRepository
{
List<Tag> GetTagList();
}
Then you create Service:
public class Service
{
private readonly IRepository _repository;
public Service(IRepository repository)
{
if (repository == null)
throw new ArgumentNullException("repository");
_repository = repository;
}
public List<Tag> GetTagList()
{
return _repository.GetTagList();
}
}
on this stage you have compiled version of Business layer. You can add all complex logic of your application and test it. Attent, without DB amd View parts!
Next step - create DB layer
DB Layer:
Create one more Class Library in your solution, add link to BL project and add code like this:
public class EFRepository : IRepository
{
private ... _db;
public EFRepository()
{
.....
}
public List<Tag> GetTagList()
{
var tags = (from i in _db.tblTags select new Tag { TagId = i.TagId, TagName = i.TagName }).ToList();
}
}
so, you have DB and BL layers. And your BL layer is not depend on DB layer, vice versa, DB layer is depend on BL layer .And it's a correct approach, business logic of your application should not be depended on DB!
The last step - View Layer.
View Layer
You can separate View Layer to different project too. I do it inside my ASP.NET MVC application. Add references to DB and BL projects
public class tblTagViewModel
{
public string TagName { get; set; }
public string TagNameClr
{
get
{
if (TagName == "Test")
{
return "green";
}
else
{
return "red";
}
}
}
}
and your controller:
public class HomeController : Controller
{
private readonly Service _service;
public HomeController(IRepository repository)
{
if (repository == null)
throw new ArgumentNullException("repository");
_service = new Service(repository);
}
public ActionResult Test()
{
List<ViewModel.tblTagViewModel> list = new List<ViewModel.tblTagViewModel>();
vat taglist = _service.GetTagList();
foreach (tblTag p in taglist)
{
ViewModel.tblTagViewModel nw = new ViewModel.tblTagViewModel();
nw.TagName = p.TagName;
list.Add(nw);
}
return View(list);
}
}
and, the last step, set DB implementation to your web app:
public class MyControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
var efRepository = new EFRepository();
if (controllerType == typeof(HomeController))
{
return new HomeController(efRepository );
}
return base.GetControllerInstance(requestContext, controllerType);
}
pay attention, that your web-application is not depend on DB layer, it depends only on BL layer (this is logical too). Only one place to set EFRepository in your web application - in controller factory.
With this approach you can easily change EFRepository to another implementation of IRepository, with different DB or even fake DB. You can test any part of your application without unnecessary relationships. You have strong and correct architecture, where your application depend on business logic layer and not depend on DB layer etc. Any changes in DB layer or View layer are not affect on logic layer
For mapping similar POCO classes (i.e BL and DB, BL and View classes) with many properties you can user Automapper
Related
We have ASP MVC web project. After reading a lot of articles and discussions here in stackoverflow about the correct architechture we have decided to go with the following one, although there is not only one correct way of doing things this is the way we have decided, but we still have some doubts.
We are publishing this here not only to be helped but also to show what we have done in case it is helpful to somebody.
We are working in ASP .NET MVC project, EF6 Code first with MS SQL Server.
We have divided the project into 3 main layers that we have separate into 3 projects: model, service and web.
The model creates the entities and setup the DataContext for the database.
The service make the queries to the data base and transform those entities into DTOs to pass them to the web layer, so the web layer doesn't know anything about the database.
The web uses AutoFac for the DI (dependency Injection) to call the services we have in the service layer and obtain the DTOs to transform those DTOs into Model Views to use them in the Views.
After reading a lot of articles we decided not to implement a repository pattern and unit of work because, in summary, we have read the EF acts as a unit of work itself. So we are simplifying things a little here.
https://cockneycoder.wordpress.com/2013/04/07/why-entity-framework-renders-the-repository-pattern-obsolete/
This is the summary of our project. Now I'm going to go through every project to show the code. We are going to show only a couple of entities, but our project has more than 100 different entities.
MODEL
Data Context
public interface IMyContext
{
IDbSet<Language> Links { get; set; }
IDbSet<Resources> News { get; set; }
...
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
}
public class MyDataContext : DbContext, IMyContext
{
public MyDataContext() : base("connectionStringName")
{
}
public IDbSet<Language> Links { get; set; }
public IDbSet<Resources> News { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}
}
Here is how we declare the entities
public class Link
{
public int Id{ get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
SERVICES
These are the generic classes we use for all the services.
As you see we use the DTOs to get data from the web layer. Also we connect to the database using Dbset = Context.Set()
public interface IService
{
}
public interface IEntityService<TDto> : IService where TDto : class
{
IEnumerable<TDto> GetAll();
void Create(TDto entity);
void Update(TDto entity);
void Delete(TDto entity);
void Add(TDto entity);
void Entry(TDto existingEntity, object updatedEntity);
void Save();
}
public abstract class EntityService<T, TDto> : IEntityService<TDto> where T : class where TDto : class
{
protected IClientContext Context;
protected IDbSet<T> Dbset;
protected EntityService(IClientContext context) { Context = context; Dbset = Context.Set<T>(); }
public virtual IEnumerable<TDto> GetAll()
{
return Mapper.Map<IEnumerable<TDto>>(Dbset.AsEnumerable());
}
public virtual void Create(TDto entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
Dbset.Add(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Update(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Context.Entry(entity).State = EntityState.Modified;
Context.SaveChanges();
}
public virtual void Delete(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Dbset.Remove(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Add(TDto entity)
{
Dbset.Add(Mapper.Map<T>(entity));
}
public virtual void Entry(TDto existingEntity, object updatedEntity)
{
Context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
}
public virtual void Save()
{
Context.SaveChanges();
}
}
We declare the DTOs in this project (this is a very simple example so we don't have to put all the code here):
public class LinkDto
{
public int Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
Then one of our services:
public interface ILinkService : IEntityService<LinkDto>
{
IPagedList<LinkDto> GetAllLinks(string searchTitle = "", bool searchActive = false, int pageNumber = 1, int pageSize = 10);
LinkDto FindById(int id);
LinkDto Test();
}
public class LinkService : EntityService<Link, LinkDto>, ILinkService
{
public LinkService(IClientContext context) : base(context) { Dbset = context.Set<Link>(); }
public virtual IPagedList<LinkDto> GetAllLinks(bool searchActive = false, int pageNumber = 1, int pageSize = 10)
{
var links = Dbset.Where(p => p.Active).ToPagedList(pageNumber, pageSize);
return links.ToMappedPagedList<Link, LinkDto>();
}
public virtual LinkDto FindById(int id)
{
var link = Dbset.FirstOrDefault(p => p.Id == id);
return Mapper.Map<LinkDto>(link);
}
public LinkDto Test()
{
var list = (from l in Context.Links
from o in Context.Other.Where(p => p.LinkId == l.Id)
select new OtherDto
{ l.Id, l.Title, l.Url, o.Other1... }).ToList();
return list;
}
}
As you see we use AutoMapper (version 5 which has changed a little) to transform from Entities to DTOs the data.
One of the doubts we have is if the use of "Dbset.Find" or "Dbset.FirstOrDefault" is correct and also if the use of "Context.Links" (for any entity).
WEB
FInally the web project where we receive the DTOs and transform those DTOs into ModelViews to show in our views.
We need to call, in the Global.asax Application_Start, AutoFac to do the DI so we can use our services.
protected void Application_Start()
{
...
Dependencies.RegisterDependencies();
AutoMapperBootstrapper.Configuration();
...
}
public class Dependencies
{
public static void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
builder.RegisterModule(new ServiceModule());
builder.RegisterModule(new EfModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
public class ServiceModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.Load("MyProject.Service")).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();
}
}
public class EfModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType(typeof(MyDataContext)).As(typeof(IMyContext)).InstancePerLifetimeScope();
}
}
As you see we also call AutoMapper to configure the different maps.
Then in our controllers we have this.
public class LinksController : Controller
{
private readonly ILinkService _linkService;
public LinksController(ILinkService linkService)
{
_linkService = linkService;
}
public ActionResult Index()
{
var links = _linkService.GetAllLinks();
return View(links.ToMappedPagedList<LinkDto, LinksListModelAdmin>());
}
...
public ActionResult Create(LinksEditModelAdmin insertedModel)
{
try
{
if (!ModelState.IsValid) return View("Create", insertedModel);
var insertedEntity = Mapper.Map<LinkDto>(insertedModel);
_linkService.Create(insertedEntity);
return RedirectToAction("Index");
}
catch (Exception ex)
{
throw ex;
}
}
}
Well, this is it...I hope this can be useful for somebody...and also I hope we can have a little help with the questions we have.
1) Although we are separating database from the web project we do need a reference in the web project to initialize the database and also to inject dependencies, is this correct?
2) Is it correct the approach we have done having our Entities->DTOs->ViewModels? It's a little more work but we have everything separated.
3) In the Service project, when we need to reference a different entity than the main one we are using in the service, is it correct to call Context.Entity?
For example, if we need to retrieve also data from the News entity in the links service, is it correct to call "Context.News.Where..."?
4) We do have a little problem with Automapper and EF proxy, because when we call "Dbset" to retrieve data, it gets a "Dynamic proxies" object so Automapper can't find the proper map so, in order to work, we have to set ProxyCreationEnabled = false in the DataContext definition. This way we can get an Entity in order to map it to the DTO. This disables LazyLoading, which we don't mind, but is this a correct approach or there is a better way to solve this?
Thanks in advance for your comments.
For Question no. 2
Entities->DTOs->ViewModels? is good approach
because you are doing the clean separation, the programmer can work together with ease.
The person who design ViewModels, Views and Controllers don't have to worry about the service layer or the DTO implementation because he will make the mapping when the others developpers finish their implementation.
For Question no. 4
When the flag ProxyCreationEnabled is set to false, the proxy instance will not be created with creating a new instance of an entity. This might not be a problem but we can create a proxy instance using the Create method of DbSet.
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create();
}
The Create method has an overloaded version that accepts a generic type. This can be used to create an instance of a derived type.
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create<Student>();
}
The Create method just creates the instance of the entity type if the proxy type for the entity would have no value (it is nothing to do with a proxy). The Create method does not add or attach the entity with the context object.
Also i read some where if you set ProxyCreationEnabled = false the child element will not loaded for some parent object unless Include method is called on parent object.
I am implementing loosely coupled architecture.
MVC as Presentation layer(ProjectName.Web) and all the Business Logic will be handled in a seperate C# project ProjectName.BL.
I will be consuming the webservice from my BL layer(ProjectName.BL). So the requestparameter class objects will be visible to BL since i am making servicereference.
1) The issue i am facing is how will i send my request parameter from Controller to BL layer.
2) Next issue is how will map the ViewModel objects in BL once i get response from service, as the viewmodels are in my web projects.
Request your assistance i have no clue to achieve this one.
This is like any other question on layering and separation of concerns.
Use a DTO. In your Business Layer, introduce types for performing the action you want to perform, and perform mapping between types.
Your question isn't quite concrete, so I'll go with Foo:
Service Layer:
public class ServiceFooRequest
{
public int ID { get; set; }
}
public class ServiceFooResponse
{
public string Bar { get; set; }
}
public ServiceFooResponse GetFoo(ServiceFooRequest request)
{
return new ServiceFooResponse
{
Bar = "Baz"
};
}
Business Layer:
public class BLFooResponse
{
public string Bar { get; set; }
}
public class BLL
{
public BLFooResponse GetFoo(int id)
{
var serviceResponse = _serviceReferenceClient.GetFoo(new ServiceFooRequest
{
ID = id
});
return new BLFooResponse
{
Bar = serviceResponse.Bar
};
}
}
MVC:
public class FooViewModel
{
public string Bar { get; set; }
}
public ActionResult GetFoo(int id)
{
var businessFooResponse = _bll.GetFoo(id);
var fooViewModel = new FooViewModel
{
Bar = businessFooResponse.Bar
};
return View(fooViewModel);
}
You need separate project (DLL) Models. It will store shared between layers models.
Each controller should have reference to BL object or BLL factory.
Hint - use Automapper to copy DAO objects to BDO.
Web project
public class FoodController : BaseController
{
private IFoodBll _foodBll = null;
public FoodController(IFoodBll foodBll)
{
// Make DI of your BLL
_foodBll = foodBll;
}
[HttpPost]
public ActionResult Edit(FoodEditModel model)
{
_foodBll.Save(model);
}
Your Edit.cshtml should look smth like this
#model MyProjects.Web.Models.Foods.FoodEditModel
#Html.HiddenFor(x => Model.Id)
#Html.EditorFor(x => Model.Name)
I have my Entity Framework Entities split out into a separate class library from my web project and data access layer. In my controller I make a call to my repository to get an IEnumerable<RobotDog.Entities.Movie> and then try to serialize into json using JavaScriptSerializer but I get a circular reference even though I'm using the [ScriptIgnore] attribute.
IMPORTANT: Originally I had my entities, data access and web all under one project and I was able to successfully serialize my entites without a circular reference. When I created separate layers that's when I started having problems. I did not change any of the entities.
An example of one of my entities in the RobotDog.Entities namespace:
namespace RobotDog.Entities {
public class Character {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(200)]
public string Name { get; set; }
public virtual Person Person { get; set; }
[ScriptIgnore]
public virtual Movie Movie { get; set; }
}
}
My controller:
namespace RobotDog.Web.Controllers {
public class MoviesController : Controller {
private UnitOfWork _unitOfWork = new UnitOfWork();
[HttpGet]
public ActionResult Index() {
var user = Membership.GetUser(User.Identity.Name);
if(user != null) {
var movies = _unitOfWork.UserMovieRepository.Get(u => u.UserId == (Guid) user.ProviderUserKey).Select(m => m.Movie);
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(movies);
return View(json);
}
return View();
}
}
}
My Repository:
namespace RobotDog.DataAccess.Movies {
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class {
internal MovieContext Context;
internal DbSet<TEntity> DbSet;
public Repository(MovieContext context) {
if (context == null)
throw new ArgumentNullException("context");
Context = context;
DbSet = Context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> predicate = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null ) {
IQueryable<TEntity> query = DbSet;
if (predicate != null)
query = query.Where(predicate);
return orderBy != null ? orderBy(query).ToList() : query.ToList();
}
}
}
Maybe kinda late response, but I had similar problem with POCO Classes for Entity Framework Code-Firts. The problem was that may properties were declared as virtual. In this case EF creates proxy class which overrides the virtual property. It seems that ScriptIgnore attribute is not by default applied on overriden properties, unless you use it like this:
[ScriptIgnore(ApplyToOverrides=true)]
Circular object graphs cannot be JSON serialized. And when you give it a second thought it actually makes sense. The correct way to handle this is to use view models. You should never pass your domain entities directly to your views. Always define a view model containing only the necessary properties that you want to be exposed.
I am sure that the client consuming this JSON doesn't care about having this circular object graph. So simply define a view model breaking this circular dependency and including only the properties you need.
Then all you have to do is map your domain model to the view model and pass this view model to a JsonResult (yeah that's another issue in your code - you are manually JSON serializing and writing plumbing code in your controller action instead of delegating this to the framework).
So:
[HttpGet]
public ActionResult Index()
{
var user = Membership.GetUser(User.Identity.Name);
if(user != null)
{
IEnumerable<Movie> movies = _unitOfWork
.UserMovieRepository.Get(u => u.UserId == (Guid) user.ProviderUserKey)
.Select(m => m.Movie);
IEnumerable<MovieViewModel> moviesVm = ... map the domain model to your view model
return Json(moviesVm, JsonRequestBehavior.AllowGet);
}
// return an empty movies array
var empty = Enumerable.Empty<MovieViewModel>();
return Json(empty, JsonRequestBehavior.AllowGet);
}
The important thing you should be focusing right now on is defining the MovieViewModel class which will contain only the information that you want to expose to the client as JSON. Break all circular references. Feel free to have additional view models that this main view model is referencing in order to map other entities.
And most importantly : never pass your domain models to the view. Always define view models. This way your application is completely independent of the underlying data access technology you are using. You could modify your DAL layer as much as you like without impacting the UI part because this UI is represented by view models.
I've been using the repository pattern described in Bob Cravens blog to create my application, but I'm a bit new and still finding my way around it. I want to inject my DataService object into the constructor of my ViewModel so I can create a SelectList object, and create a drop down box in my view. However I can't seem to get the bindings to work, every time I create the ViewModel it looks for / executes the parameterless constructor! I've tried various ways using answers here on SO but to no avail. Help would be greatly appreciated.
ViewModel:
public class ServerCreateViewModel
{
public SelectList Companies { get; private set; }
public ServerCreateViewModel()
{
}
public ServerCreateViewModel(DataService _dataService)
{
Companies = new SelectList(_dataService.Companies.All(), "Id", "CompanyName");
}
Ninject module:
Bind<DataService>().ToSelf()
.InRequestScope();
var _dataService = Kernel.Get<DataService>();
Bind<ServerCreateViewModel>()
.ToSelf()
.WithConstructorArgument("_dataService", _dataService);
//Bind<ServerCreateViewModel>()
// .ToSelf()
// .WithConstructorArgument("_dataService", ctx => ctx.Kernel.Get<DataService>());
Controller:
public ActionResult Create(ServerCreateViewModel viewModel)
{
return View(viewModel);
}
You shouldn't be doing that!
View Models (all models, in fact) should be just buckets with some data. They should not depend on any business logic, services, etc.
It is controller's responsibility to populate models and pass them to views.
public class ServerCreateViewModel
{
public SelectList Companies { get; private set; }
}
public ActionResult Create()
{
var viewModel = new ServerCreateViewModel
{
Companies = new SelectList(_dataService.Companies.All(), "Id", "CompanyName")
};
return View(viewModel);
}
DataService should be injected into the controller, not view model.
How can I mock a DataServiceQuery for unit testing purpose?
Long Details follow:
Imagine an ASP.NET MVC application, where the controller talks to an ADO.NET DataService that encapsulates the storage of our models (for example sake we'll be reading a list of Customers). With a reference to the service, we get a generated class inheriting from DataServiceContext:
namespace Sample.Services
{
public partial class MyDataContext : global::System.Data.Services.Client.DataServiceContext
{
public MyDataContext(global::System.Uri serviceRoot) : base(serviceRoot) { /* ... */ }
public global::System.Data.Services.Client.DataServiceQuery<Customer> Customers
{
get
{
if((this._Customers==null))
{
this._Customers = base.CreateQuery<Customer>("Customers");
}
return this._Customers;
}
}
/* and many more members */
}
}
The Controller could be:
namespace Sample.Controllers
{
public class CustomerController : Controller
{
private IMyDataContext context;
public CustomerController(IMyDataContext context)
{
this.context=context;
}
public ActionResult Index() { return View(context.Customers); }
}
}
As you can see, I used a constructor that accepts an IMyDataContext instance so that we can use a mock in our unit test:
[TestFixture]
public class TestCustomerController
{
[Test]
public void Test_Index()
{
MockContext mockContext = new MockContext();
CustomerController controller = new CustomerController(mockContext);
var customersToReturn = new List<Customer>
{
new Customer{ Id=1, Name="Fred" },
new Customer{ Id=2, Name="Wilma" }
};
mockContext.CustomersToReturn = customersToReturn;
var result = controller.Index() as ViewResult;
var models = result.ViewData.Model;
//Now we have to compare the Customers in models with those in customersToReturn,
//Maybe by loopping over them?
foreach(Customer c in models) //*** LINE A ***
{
//TODO: compare with the Customer in the same position from customersToreturn
}
}
}
MockContext and MyDataContext need to implement the same interface IMyDataContext:
namespace Sample.Services
{
public interface IMyDataContext
{
DataServiceQuery<Customer> Customers { get; }
/* and more */
}
}
However, when we try and implement the MockContext class, we run into problems due to the nature of DataServiceQuery (which, to be clear, we're using in the IMyDataContext interface simply because that's the data type we found in the auto-generated MyDataContext class that we started with). If we try to write:
public class MockContext : IMyDataContext
{
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers { get { /* ??? */ } }
}
In the Customers getter we'd like to instantiate a DataServiceQuery instance, populate it with the Customers in CustomersToReturn, and return it. The problems I run into:
1~ DataServiceQuery has no public constructor; to instantiate one you should call CreateQuery on a DataServiceContext; see MSDN
2~ If I make the MockContext inherit from DataServiceContext as well, and call CreateQuery to get a DataServiceQuery to use, the service and query have to be tied to a valid URI and, when I try to iterate or access the objects in the query, it will try and execute against that URI. In other words, if I change the MockContext as such:
namespace Sample.Tests.Controllers.Mocks
{
public class MockContext : DataServiceContext, IMyDataContext
{
public MockContext() :base(new Uri("http://www.contoso.com")) { }
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers
{
get
{
var query = CreateQuery<Customer>("Customers");
query.Concat(CustomersToReturn.AsEnumerable<Customer>());
return query;
}
}
}
}
Then, in the unit test, we get an error on the line marked as LINE A, because http://www.contoso.com doesn't host our service. The same error is triggered even if LINE A tries to get the number of elements in models.
Thanks in advance.
I solved this by creating an interface IDataServiceQuery with two implementations:
DataServiceQueryWrapper
MockDataServiceQuery
I then use IDataServiceQuery wherever I would have previously used a DataServiceQuery.
public interface IDataServiceQuery<TElement> : IQueryable<TElement>, IEnumerable<TElement>, IQueryable, IEnumerable
{
IDataServiceQuery<TElement> Expand(string path);
IDataServiceQuery<TElement> IncludeTotalCount();
IDataServiceQuery<TElement> AddQueryOption(string name, object value);
}
The DataServiceQueryWrapper takes a DataServiceQuery in it's constructor and then delegates all functionality to the query passed in. Similarly, the MockDataServiceQuery takes an IQueryable and delegates everything it can to the query.
For the mock IDataServiceQuery methods, I currently just return this, though you could do something to mock the functionality if you want to.
For example:
// (in DataServiceQueryWrapper.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
return new DataServiceQueryWrapper<TElement>(_query.Expand(path));
}
// (in MockDataServiceQuery.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
return this;
}
[Disclaimer - I work at Typemock]
Have you considered using a mocking framework?
You can use Typemock Isolator to create a fake instance of DataServiceQuery:
var fake = Isolate.Fake.Instance<DataServiceQuery>();
And you can create a similar fake DataServiceContext and set it's behavior instead of trying to inherit it.