use of expand in breeze when mapping to DTOs on the server - breeze

I have had to move queries off the main database in order to meet requirements for complex authorization - for example a user with a given authorization role can only view data for individuals in the same institution.
I am using the Breeze .net DocCode sample for guidance, and have copied the premise for the mapping of domain models to DTOs.
get { return ForCurrentUser(Context.Orders).Select(o => new Order {
OrderID = o.OrderID,
....
OrderDetails = o.OrderDetails.Select(od => new OrderDetail
{
ProductID = od.ProductID,
UnitPrice = od.UnitPrice
...
})
The problem is that which mapped properties to .include(entity framework method)/.expand (breeze method) is now a concern of the mapping function (for example, the above code will always return the OrderDetails collection, whether I want them or not). I would like to still only eagerly load/expand properties if the javascript client generated predicate has a .expand directive for that property.
Is this at all possible, or am I stuck with manually defining different mapping functions on the server, depending on what properties I want expanded? (I am happy to use tools such as automapper if that would solve or simplify the problem)
Thank you

You will need to use the ODataQueryOptions as a parameter to your controller method. This gives you the details of the query predicates in your server method, so that you can apply them as needed rather that having them applied automatically. This will let you expand, or not, based upon the query.
See this answer and this answer to see how it works.

Related

Multi Layer Solution - The type 'Customer' is defined in an assembly that is not referenced

I have the following layout for my solution.
Data Layer
Entities Layer (POCO's to create code-first db)
Services Layer (Web API)
Web Layer (MVC Layer for presentation)
I have created models in the Web API Layer that mimic the entities in the Entities layer so I can more easily reference properties. I have those called Models. I would like to have the API layer do the data work in case I want to upgrade the Web Layer later or go a different direction. I have used the VS 2019 Controller creation tool and I have referenced my Services Layer Model as the model and the Data Context from my Data Layer. I get the following error:
"Error CS0012 The type 'Customer' is defined in an assembly that is not referenced. You must add a reference to assembly 'XX.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.XX.Services"
I can see why I get that, the Model in the Service layer (though matches property wise) does not match the namespace. How does one reference the Data Layer without using something like AutoMapper? It seems like I am missing something obvious, but maybe not.
If your Services layer is going to contain your logic and interact with your entities, and your MVC Controllers are largely anemic, passing through to the Services, it sounds like you want your Services to return Models (DTOs or ViewModels) which decouple the data returned from your entities.
To do this, Services will need a reference to your Entities because that is what your data layer is. Ideally the data layer should be returning IQueryable<TEntity> as opposed to IEnumerable<TEntity> or even TEntity so that the services can refine queries for efficiency without bringing back more data than they need, or adding lots of complexity or a large number of similar single-purpose methods in the data layer.
I'm not sure what your aversion to Automapper is, but it is perfectly suited to handling the conversion and copying of data between Entity and ViewModel. You can certainly do it without Automapper using Linq's Select method.
For instance, to get a list of OrderModels out of your service:
public IEnumerable<OrderModel> GetOrdersForCustomer(int customerId, int pageNumber, int pageSize)
{
var orders = OrderRepository.GetByCustomerId(customerId)
.OrderByDescending(x => x.CreatedAt)
.Select(x => new OrderModel
{
OrderId = x.OrderId,
OrderNumber = x.OrderNumber,
CreatedAt = x.CreatedAt,
// ... et al,
OrderItems = x.OrderItems.Select( oi => new OrderItemModel
{
OrderItemId = oi.OrderItemId,
ProductId = oi.Product.ProductId,
ProductName = oi.Product.Name,
Quantity = oi.Quantity,
UnitPrice = oi.Product.Price,
// ...
}).ToList()
}).Skip(pageNumber*pageSize)
.Take(pageSize)
.ToList();
return orders;
}
Clumsy, but flexible and gets the job done. With Automapper mapping configured:
public IEnumerable<OrderModel> GetOrdersForCustomer(int customerId, int pageNumber, int pageSize)
{
var orders = OrderRepository.GetByCustomerId(customerId)
.OrderByDescending(x => x.CreatedAt)
.ProjectTo<OrderModel>()
.Skip(pageNumber*pageSize)
.Take(pageSize)
.ToList();
return orders;
}
The key to something like this working efficiently in terms of performance and memory use is that OrderRepository.GetCustomerById(int) returns IQueryable<Order> not IEnumerable<Order> This allows the Select/ProjectTo and Skip & Take to be compiled down to the SQL which returns just the columns needed to populate your models.
ViewModels/DTOs do not need to match their Entity counterparts 1:1. You only need to include fields you know the consumers will use. This helps protect your schema but also can streamline the amount of data that gets sent over the wire, boosting performance and reducing server memory usage. You can define as many view models as you need for an entity, and use inheritance to extend them if desired.
When going the other way, such as performing an insert or update, Automapper can help simplify data transitions, and even cover rules to ensure that data that should not be changed, never gets changed. You can load entity graphs in your service similar to above, but using Include to pre-fetch related data that might be updated and selecting the particular entity(ies) to update. With Automapper you can handle both insert scenarios and updates:
Inserts:
var newOrder = _mapper.Map<Order>(orderViewModel);
_context.Orders.Add(newOrder);
_context.SaveChanges();
Updates:
var existingOrder = _context.Orders.Single(x => x.OrderId = orderViewModel.OrderId);
_mapper.Map(orderViewModel, existingOrder);
_context.SaveChanges();
... and for singular entities you're pretty much done. A little more work may be needed for updating related entities with the Order.

How to limit the amount of data from an OData request?

I have a Users table of 76 users and UserGroups table.
Using MVC, OData, a generic repository and EF, I am trying to optimize data retrieval when filtering based on the user group:
/api/Users?$filter=USERGROUPS/any(usergroup: usergroup/ID eq 'Group1')
On the client side, I get the right number of users - 71 (as OData is filtering based on the result), however I want to limit the number of records being returned form the actual query - ie. I do not want to return all records then filter (not optimal for very large data sets).
My API controller method is as follows:
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<USER> Get()
{
var unitOfWork = new ATMS.Repository.UnitOfWork(_dbContext);
var users = unitOfWork.Repository<USER>()
.Query()
.Include(u => u.USERGROUPS)
.Get()
.OrderBy(order => order.USERNAME);
unitOfWork.Save(); // includes Dispose()
return users.AsQueryable();
}
I read in this post that:
Entity framework takes care of building dynamic query based on the
request.
However, using a SQL Server profiler, the query executed is requesting all the records, rather than a filtered query.
Adding a .Take() to the query does not accomplish the desired result, as we also need the actual number of records returned for paging purposes.
I was thinking of using the grabbing some properties through ODataQueryOptions, but that doesn't seem quite right either.
Is my implementation of Unit of Work and Repository incorrect, in relation to what I am trying to accomplish, and if so, how can this be corrected?
Simple - Just set the Page size for the Queryable atrribute [Queryable(PageSize=10)]
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options#server-paging
If You'd tell the EF where to apply the options, it would work.
Like this :
//[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<USER> Get(ODataQueryOptions<USER> options)
{
var users = options.ApplyTo(_dbContext.Set<USER>()
.Query()
.Include(u => u.USERGROUPS)
.Get()
.OrderBy(order => order.USERNAME));
return users;
}
Your code didn't work, because it tried to apply the options onto the last line "users.AsQueryable()", so what actually happened, is that EF pull the FULL dataset, and then applied the query onto the last line (that being a in memory collection). And that's why You didn't see that "filter" not being passed to the SQL.
The mechanics are such, that EF tries to apply the Query, to the IQueryable collection that it finds in the code (there's still a question how does it find the correct line).

Onion Architecture - Repository Vs Service?

I am learning the well-known Onion Architecture from Jeffrey Palermo.
Not specific to this pattern, but I cannot see clearly the separation between repositories and domain services.
I (mis)understand that repository concerns data access and service are more about business layer (reference one or more repositories).
In many examples, a repository seems to have some kind of business logic behind like GetAllProductsByCategoryId or GetAllXXXBySomeCriteriaYYY.
For lists, it seems that service is just a wrapper on repository without any logic.
For hierarchies (parent/children/children), it is almost the same problem : is it the role of repository to load the complete hierarchy ?
The repository is not a gateway to access Database. It is an abstraction that allow you to store and load domain objects from some form of persistence store. (Database, Cache or even plain Collection). It take or return the domain objects instead of its internal field, hence it is an object oriented interface.
It is not recommended to add some methods like GetAllProductsByCategoryId or GetProductByName to the repository, because you will add more and more methods the repository as your use case/ object field count increase. Instead it is better to have a query method on the repository which takes a Specification. You can pass different implementations of the Specification to retrieve the products.
Overall, the goal of repository pattern is to create a storage abstraction that does not require changes when the use cases changes. This article talks about the Repository pattern in domain modelling in great detail. You may be interested.
For the second question: If I see a ProductRepository in the code, I'd expect that it returns me a list of Product. I also expect that each of the Product instance is complete. For example, if Product has a reference to ProductDetail object, I'd expect that Product.getDetail() returns me a ProductDetail instance rather than null. Maybe the implementation of the repository load ProductDetail together with Product, maybe the getDetail() method invoke ProductDetailRepository on the fly. I don't really care as a user of the repository. It is also possible that the Product only returns a ProductDetail id when I call getDetail(). It is perfect fine from the repository's contract point of view. However it complicates my client code and forces me to call ProductDetailRepository myself.
By the way, I've seen many service classes that solely wrap the repository classes in my past. I think it is an anti-pattern. It is better to have the callers of the services to use the repositories directly.
Repository pattern mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
So, repositories is to provide interface for CRUD operation on domain entities. Remember that Repositories deals with whole Aggregate.
Aggregates are groups of things that belong together. An Aggregate Root is the thing that holds them all together.
Example Order and OrderLines:
OrderLines have no reason to exist without their parent Order, nor can they belong to any other Order. In this case, Order and OrderLines would probably be an Aggregate, and the Order would be the Aggregate Root
Business logic should be in Domain Entities, not in Repository layer , application logic should be in service layer like your mention, services in here play a role as coordinator between repositoies.
While I'm still struggling with this, I want to post as an answer but also I accept (and want) feedback about this.
In the example GetProductsByCategory(int id)
First, let's think from the initial need. We hit a controller, probably the CategoryController so you have something like:
public CategoryController(ICategoryService service) {
// here we inject our service and keep a private variable.
}
public IHttpActionResult Category(int id) {
CategoryViewModel model = something.GetCategoryViewModel(id);
return View()
}
so far, so good. We need to declare 'something' that creates the view model.
Let's simplify and say:
public IHttpActionResult Category(int id) {
var dependencies = service.GetDependenciesForCategory(id);
CategoryViewModel model = new CategoryViewModel(dependencies);
return View()
}
ok, what are dependencies ? We maybe need the category tree, the products, the page, how many total products, etc.
so if we implemented this in a repository way, this could look like more or less like this :
public IHttpActionResult Category(int id) {
var products = repository.GetCategoryProducts(id);
var category = repository.GetCategory(id); // full details of the category
var childs = repository.GetCategoriesSummary(category.childs);
CategoryViewModel model = new CategoryViewModel(products, category, childs); // awouch!
return View()
}
instead, back to services :
public IHttpActionResult Category(int id) {
var category = service.GetCategory(id);
if (category == null) return NotFound(); //
var model = new CategoryViewModel(category);
return View(model);
}
much better, but what is exactly inside service.GetCategory(id) ?
public CategoryService(ICategoryRespository categoryRepository, IProductRepository productRepository) {
// same dependency injection here
public Category GetCategory(int id) {
var category = categoryRepository.Get(id);
var childs = categoryRepository.Get(category.childs) // int[] of ids
var products = productRepository.GetByCategory(id) // this doesn't look that good...
return category;
}
}
Let's try another approach, the unit of work, I will use Entity framework as the UoW and Repositories, so no need to create those.
public CategoryService(DbContext db) {
// same dependency injection here
public Category GetCategory(int id) {
var category = db.Category.Include(c=> c.Childs).Include(c=> c.Products).Find(id);
return category;
}
}
So here we are using the 'query' syntax instead of the method syntax, but instead of implementing our own complex, we can use our ORM. Also, we have access to ALL repositories, so we can still do our Unit of work inside our service.
Now we need to select which data we want, I probably don't want all the fields of my entities.
The best place I can see this is happening is actually on the ViewModel, each ViewModel may need to map it's own data, so let's change the implementation of the service again.
public CategoryService(DbContext db) {
// same dependency injection here
public Category GetCategory(int id) {
var category = db.Category.Find(id);
return category;
}
}
so where are all the products and inner categories?
let's take a look at the ViewModel, remember this will ONLY map data to values, if you are doing something else here, you are probably giving too much responsibility to your ViewModel.
public CategoryViewModel(Category category) {
Name = category.Name;
Id = category.Id;
Products = category.Products.Select(p=> new CategoryProductViewModel(p));
Childs = category.Childs.Select(c => c.Name); // only childs names.
}
you can imagine the CategoryProductViewModel by yourself right now.
BUT (why is there always a but??)
We are doing 3 db hits, and we are fetching all the category fields because of the Find. Also Lazy Loading must be enable. Not a real solution isn't it ?
To improve this, we can change find with where... but this will delegate the Single or Find to the ViewModel, also it will return an IQueryable<Category>, where we know it should be exactly one.
Remember I said "I'm still struggling?" this is mostly why. To fix this, we should return the exact needed data from the service (also know as the ..... you know it .... yes! the ViewModel).
so let's back to our controller :
public IHttpActionResult Category(int id) {
var model = service.GetProductCategoryViewModel(id);
if (category == null) return NotFound(); //
return View(model);
}
inside the GetProductCategoryViewModel method, we can call private methods that return the different pieces and assemble them as the ViewModel.
this is bad, now my services know about viewmodels... let's fix that.
We create an interface, this interface is the actual contract of what this method will return.
ICategoryWithProductsAndChildsIds // quite verbose, i know.
nice, now we only need to declare our ViewModel as
public class CategoryViewModel : ICategoryWithProductsAndChildsIds
and implement it the way we want.
The interface looks like it has too many things, of course it can be splitted with ICategoryBasic, IProducts, IChilds, or whatever you may want to name those.
So when we implement another viewModel, we can choose to do only IProducts.
We can have our services having methods (private or not) to retrieve those contracts, and glue the pieces in the service layer. (Easy to say than done)
When I get into a fully working code, I might create a blog post or a github repo, but for now, I don't have it yet, so this is all for now.
I believe the Repository should be only for CRUD operations.
public interface IRepository<T>
{
Add(T)
Remove(T)
Get(id)
...
}
So IRepository would have: Add, Remove, Update, Get, GetAll and possibly a version of each of those that takes a list, i.e, AddMany, RemoveMany, etc.
For performing search retrieval operations you should have a second interface such as an IFinder. You can either go with a specification, so IFinder could have a Find(criteria) method that takes criterias. Or you can go with things like IPersonFinder which would define custom functions such as: a FindPersonByName, FindPersonByAge etc.
public interface IMyObjectFinder
{
FindByName(name)
FindByEmail(email)
FindAllSmallerThen(amount)
FindAllThatArePartOf(group)
...
}
The alternative would be:
public interface IFinder<T>
{
Find(criterias)
}
This second approach is more complex. You need to define a strategy for the criterias. Are you going to use a query language of some sort, or a more simple key-value association, etc. The full power of the interface is also harder to understand from simply looking at it. It's also easier to leak implementations with this method, because the criterias could be based around a particular type of persistence system, like if you take a SQL query as criteria for example. On the other hand, it might prevent you from having to continuously come back to the IFinder because you've hit a special use case that requires a more specific query. I say it might, because your criteria strategy will not necessarily cover 100% of the querying use cases you might need.
You could also decide to mix both together, and have an IFinder defining a Find method, and IMyObjectFinders that implement IFinder, but also add custom methods such as FindByName.
The service acts as a supervisor. Say you need to retrieve an item but must also process the item before it is returned to the client, and that processing might require information found in other items. So the service would retrieve all appropriate items using the Repositories and the Finders, it would then send the item to be processed to objects that encapsulates the necessary processing logic, and finally it would return the item requested by the client. Sometime, no processing and no extra retrievals will be required, in such cases, you don't need to have a service. You can have clients directly call into the Repositories and the Finders. This is one difference with the Onion and a Layered architecture, in the Onion, everything that is more outside can access everything more inside, not only the layer before it.
It would be the role of the repository to load the full hierarchy of what is needed to properly construct the item that it returns. So if your repository returns an item that has a List of another type of item, it should already resolve that. Personally though, I like to design my objects so that they don't contain references to other items, because it makes the repository more complex. I prefer to have my objects keep the Id of other items, so that if the client really needs that other item, he can query it again with the proper Repository given the Id. This flattens out all items returned by the Repositories, yet still let's you create hierarchies if you need to.
You could, if you really felt the need to, add a restraining mechanism to your Repository, so that you can specify exactly which field of the item you need. Say you have a Person, and only care for his name, you could do Get(id, name) and the Repository would not bother with getting every field of the Person, only it's name field. Doing this though, adds considerable complexity to the repository. And doing this with hierarchical objects is even more complex, especially if you want to restrict fields inside fields of fields. So I don't really recommend it. The only good reason for this, to me, would be cases where performance is critical, and nothing else can be done to improve the performance.
In Domain Driven Design the repository is responsible for retrieving the whole Aggregate.
Onion and Hexagonal Architectures purpose is to invert the dependency from domain->data access.
Rather than having a UI->api->domain->data-access,
you'll have something like UI->api->domain**<-**data-access
To make your most important asset, the domain logic, is in the center and free of external dependencies.
Generally by splitting the Repository into Interface/Implementation and putting the interface along with the business logic.
Now to services, there's more that one type of services:
Application Services: your controller and view model, which are external concerns for UI and display and are not part of the domain
Domain Services: which provide domain logic. In you're case if the logic you're having in application services starts to do more that it's presentation duties. you should look at extracting to a domain service
Infrastructure Services: which would, as with repositories, have an interface within the domain, and an implementation in the outer layers
#Bart Calixto, you may have a look at CQRS, building your view model is too complex when you're trying to use Repositories which you design for domain logic.
you could just rewrite another repo for the ViewModel, using SQL joins for example, and it doesn't have to be in the domain
is it the role of repository to load the complete hierarchy ?
Short answer: yes, if the repository's outcome is a hierarchy
The role of repository is to load whatever you want, in any shape you need, from the datasource (e.g. database, file system, Lucene index, etc).
Let's suppose a repository (interface) has the GetSomeHierarchyOrListBySomeCriteria operation - the operation, its parameters and its outcome are part of the application core!
Let's focus on the outcome: it doesn't matter it's shape (list, hierarchy, etc), the repository implementation is supposed to do everything necessary to return it.
If one is using a NoSql database than GetSomeHierarchyOrListBySomeCriteria implementation might need only one NoSql-database-query with no other conversions or transformations to get the desired outcome (e.g. hierarchy). For a SQL database on the other hand, the same outcome might imply multiple queries and complex conversions or transformations - but that's an implementation detail, the repository interface is the same.
repositories vs domain services
According to The Onion Architecture : part 1, and I'm pointing here about the official page, not someone's else interpretation:
The first layer around the Domain Model is typically where we would
find interfaces that provide object saving and retrieving behavior,
called repository interfaces. [...] Only the interface is in the application core.
Notice the Domain Services layer above Domain Model one.
Starting with the second official page, The Onion Architecture : part 2, the author forgets about Domain Services layer and is depicting IConferenceRepository as part of the Object Services layer which is right above Domain Model, replacing Domain Services layer! The Object Services layer continues in The Onion Architecture : part 3, so I ask: what Domain Services? :)))
It seems to me that author's intent for Object Services or Domain Services is to consist only of repositories, otherwise he leaves no clue for something else.

Entity Framework testing with IRepository - problem with lazy loading

I am refactoring an MVC project to make it testable. Currently the Controller uses the Entity Framework's context objects directly to ask for the required data. I started abstract this and it just doesn't work. Eventually I have an IService and an IRepository abstraction, but to describe the problem let's just look at the IRepository. Many people advise an interface with functions which return some of these: IQueriable<...>, IEnumerable<...>, IList<...>, SomeEntityObject, SomeDTO. Then when one wants to test the service layer they can implement the interface with a class which doesn't go to the database to return these.
Problem: Using linq to entities I have lazy (deferred) loading in my toolset. This is actually very useful, because my controller action functions know which data they need for the view and I didn't ask for more than required. However linq to anythingelse doesn't have lazy loading. So when my IRepository functions return any of the above mentioned things I lose lazy loading. I extended my interface with functions like "GetAnything" and "GetAnythingDeep" but it's not enough: it has to be much more fine-grained. Which would result about 5-6 functions for the same type of object, depending on the properties I want to get in the result. Maybe could be a general function with some "include properties" parameter, but I don't like that too.
Eventually atm I think if I want to make it testable that will result either much less efficient or much more complicated code. Sounds not right.
Btw I was thinking about to change the data source behind the entity model to either xml or some object data soruce, and so I could keep the linq to entities. I found that it's not supported out of the box... which is also sad: this means that entity framework means database source - not a really useful abstraction.
Specific example:
Entity objects:
Article, Language, Person. Relations: Article can have 1-N languages, and one Person (publisher).
ViewModel object:
ArticleDeepViewModel: Contains all the properties of the article, including the languages and the Name of the Person (it's for view the article, so no need for the other properties of the person).
Controller action which will return this view should get the data from somewhere.
Code before modifications:
using (var context = new Entities.Articles())
{
var article = (from a in context.Articles.Include("Languages")
where a.ID == ID
select new ViewArticleViewModel()
{
ID = a.ID,
Headline = a.Headline,
Summary = a.Summary,
Body = a.Body,
CreatedBy = a.CreatedByEntity.Name,
CreatedDate = a.CreatedDate,
Languages = (from l in context.Languages select new ViewLanguagesViewModel() { ID = l.ID, Name = l.Name, Selected = a.Languages.Contains(l) })}).Single();
this.ViewData.Model = article;
}
return View();
Code after modifications could be something like:
var article = ArticleService.GetArticleDeep(ID);
var viewModel = /* mapping */
this.ViewData.Model = viewModel;
return View();
Problem is that GetArticleDeep should return an Article object with Languages included and the entire Person object included (it shouldn't know that the viewmodel needs just the Name of the Person). Also I have so far 3 different viewmodels for an article. For example if someone wants to see the list of articles, then it's unnecessary to get the languages, the body and some other properties, however it might be useful to get the Name of the publisher (which is in the deep). Before "testable" code the controller actions could just contain the linq to entities query and get whichever data they need using lazy loading, Include function, using subqueries, referencing foreign properties (Publisher.Name) ... So there is no unnecessary query to the database and no unnecessary data transferred from the database.
What should be the IService or IRepository interface provide to get the 3-4 different level of Article objects or sometimes list of these objects?
Not sure if you are planning to stick with lazy loading, but if you want a flexible way to integrate eager loading into your repository and service layers first check out this article:
http://blogs.msdn.com/b/alexj/archive/2009/07/25/tip-28-how-to-implement-include-strategies.aspx
He basically gives you a way to build a strongly-typed include strategy like this:
var strategy = new IncludeStrategy<Article>();
strategy.Include(a => a.Author);
Which can then be passed into a general method on your repository or service layers. This way you don't have to have a separate method for each circumstance (i.e. your GetArticleDeep method).
Here is an example repository method using the above include strategy:
public IQueryable<Article> Find(Expression<Func<Article, bool>> criteria, IncludeStrategy<Article> includes)
{
var query = includes.ApplyTo(context.Articles).Where(criteria);
return query;
}

Best practices when limiting changes to specific fields with LINQ2SQL

I was reading Steven Sanderson's book Pro ASP.NET MVC Framework and he suggests using a repository pattern:
public interface IProductsRepository
{
IQueryable<Product> Products { get; }
void SaveProduct(Product product);
}
He accesses the products repository directly from his Controllers, but since I will have both a web page and web service, I wanted to have add a "Service Layer" that would be called by the Controllers and the web services:
public class ProductService
{
private IProductsRepository productsRepsitory;
public ProductService(IProductsRepository productsRepository)
{
this.productsRepsitory = productsRepository;
}
public Product GetProductById(int id)
{
return (from p in productsRepsitory.Products
where p.ProductID == id
select p).First();
}
// more methods
}
This seems all fine, but my problem is that I can't use his SaveProduct(Product product) because:
1) I want to only allow certain fields to be changed in the Product table
2) I want to keep an audit log of each change made to each field of the Product table, so I would have to have methods for each field that I allow to be updated.
My initial plan was to have a method in ProductService like this:
public void ChangeProductName(Product product, string newProductName);
Which then calls IProductsRepository.SaveProduct(Product)
But there are a few problems I see with this:
1) Isn't it not very "OO" to pass in the Product object like this? However, I can't see how this code could go in the Product class since it should just be a dumb data object. I could see adding validation to a partial class, but not this.
2) How do I ensure that no one changed any other fields other than Product before I persist the change?
I'm basically torn because I can't put the auditing/update code in Product and the ProductService class' update methods just seem unnatural (However, GetProductById seems perfectly natural to me).
I think I'd still have these problems even if I didn't have the auditing requirement. Either way I want to limit what fields can be changed in one class rather than duplicating the logic in both the web site and the web services.
Is my design pattern just bad in the first place or can I somehow make this work in a clean way?
Any insight would be greatly appreciated.
I split the repository into two interfaces, one for reading and one for writing.
The reading implements IDisposeable, and reuses the same data-context for its lifetime. It returns the entity objects produced by linq to SQL. For example, it might look like:
interface Reader : IDisposeable
{
IQueryable<Product> Products;
IQueryable<Order> Orders;
IQueryable<Customer> Customers;
}
The iQueryable is important so I get the delayed evaluation goodness of linq2sql. This is easy to implement with a DataContext, and easy enough to fake. Note that when I use this interface I never use the autogenerated fields for related rows (ie, no fair using order.Products directly, calls must join on the appropriate ID columns). This is a limitation I don't mind living with considering how much easier it makes faking read repository for unit tests.
The writing one uses a separate datacontext per write operation, so it does not implement IDisposeable. It does NOT take entity objects as input or out- it takes the specific fields needed for each write operation.
When I write test code, I can substitute the readable interface with a fake implementation that uses a bunch of List<>s which I populate manually. I use mocks for the write interface. This has worked like a charm so far.
Don't get in a habit of passing the entity objects around, they're bound to the datacontext's lifetime and it leads to unfortunate coupling between your repository and its clients.
To address your need for the auditing/logging of changes, just today I put the finishing touches on a system I'll suggest for your consideration. The idea is to serialize (easily done if you are using LTS entity objects and through the magic of the DataContractSerializer) the "before" and "after" state of your object, then save these to a logging table.
My logging table has columns for the date, username, a foreign key to the affected entity, and title/quick summary of the action, such as "Product was updated". There is also a single column for storing the change itself, which is a general-purpose field for storing a mini-XML representation of the "before and after" state. For example, here's what I'm logging:
<ProductUpdated>
<Deleted><Product ... /></Deleted>
<Inserted><Product ... /></Inserted>
</ProductUpdated>
Here is the general purpose "serializer" I used:
public string SerializeObject(object obj)
{
// See http://msdn.microsoft.com/en-us/library/bb546184.aspx :
Type t = obj.GetType();
DataContractSerializer dcs = new DataContractSerializer(t);
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
XmlWriter writer = XmlWriter.Create(sb, settings);
dcs.WriteObject(writer, obj);
writer.Close();
string xml = sb.ToString();
return xml;
}
Then, when updating (can also be used for logging inserts/deletes), grab the state before you do your model-binding, then again afterwards. Shove into an XML wrapper and log it! (or I suppose you could use two columns in your logging table for these, although my XML approach allows me to attach any other information that might be helpful).
Furthermore, if you want to only allow certain fields to be updated, you'll be able to do this with either a "whitelist/blacklist" in your controller's action method, or you could create a "ViewModel" to hand in to your controller, which could have the restrictions placed upon it that you desire. You could also look into the many partial methods and hooks that your LTS entity classes should have on them, which would allow you to detect changes to fields that you don't want.
Good luck! -Mike
Update:
For kicks, here is how I deserialize an entity (as I mentioned in my comment), for viewing its state at some later point in history: (After I've extracted it from the log entry's wrapper)
public Account DeserializeAccount(string xmlString)
{
MemoryStream s = new MemoryStream(Encoding.Unicode.GetBytes(xmlString));
DataContractSerializer dcs = new DataContractSerializer(typeof(Product));
Product product = (Product)dcs.ReadObject(s);
return product;
}
I would also recommend reading Chapter 13, "LINQ in every layer" in the book "LINQ in Action". It pretty much addresses exactly what I've been struggling with -- how to work LINQ into a 3-tier design. I'm leaning towards not using LINQ at all now after reading that chapter.

Resources