I'm currently building a large solution for a client where code reuseability is the keyword since different types of projects (i.e. Websites, WCF services, WPF etc) should use the exact same businesslogic.
Right now, my solution looks like this:
Businesslogic layer (all of the logic that defines business rules is added here)
Interfaces
Model (ViewModels, DTOs etc)
Repository (currently using Entity Framework 6. This is where all database transactions go)
Webservices (WCF services)
MVC website
The point is that any presentation layer (i.e. the MVC website) will use the businesslogic layer which then uses the repository to make any database transaction needed.
While this works the way I want to, I find myself constantly battleing the ObjectContext from the Entity Framework, simply because, when querying for data in the repository, the entities doesn't get transferred to the businesslogic (which is logical because of EFs lazy-loading) I'm aware that I can make use of the .Include(x => x.MyOtherTable), but since the database is fairly large, this approach gets bloated quickly and queries can be rather large if the included table has a lot of records.
I've then made a DbContextManager class which looks like this:
public static class DbContextManager
{
//Unique context key per request and thread
private static string Key
{
get
{
return string.Format("MyDb_{0}{1}", HttpContext.Current.GetHashCode().ToString("x"), Thread.CurrentContext.ContextID);
}
}
//Get and set request context
private static MyEntities Context
{
get { return HttpContext.Current.Items[Key] as MyEntities ; }
set { HttpContext.Current.Items[Key] = value; }
}
//Context per request
public static MyEntities Current
{
get
{
//if null, create new context
if (Context == null)
{
Context = new MyEntities ();
HttpContext.Current.Items[Key] = Context;
}
return Context;
}
}
//Dispose any created context at the end of a request - called from Global.asax
public static void Dispose()
{
if (Context != null)
{
Context.Dispose();
}
}
}
And in my global.asax, I'm disposing the context when a request is ended:
private void Application_EndRequest(object sender, EventArgs e)
{
DbContextManager.Dispose();
}
This works perfect, I can now make my initial database call in the repository and then make the businesslogic rules in my businesslogic layer, simply because the ObjectContext lives for the http request.
However, when I need to call the same businesslogic methods from the WCF services project (or i.e. a WPF project), I won't be able to make use of my DbContextManager class since it relies on the HttpContext.
I feel I'm doing this completely wrong at the moment and I'm fighting an unnessecary battle with the Entity Framework. What have I missed? Is Entity Framework the right ORM for these types of solutions? It certainly doesn't feel like that right now :-)
Any help or hints are greatly appreciated!
Thanks in advance.
I'm confused here. It looks like you are using your entity layer objects directly in your view layer.
This is generally a bad idea. Your model should encapsulate these. Your business logic layer should use your model layer to build ViewModel and DTO objects, which the UI layer can produce/consume. This will allow you to fully separate your UI layer from your database/entity layer.
That will make re-using your business logic in a web service layer trivial. It should know nothing of your database layer. Otherwise adding or changing a field in your entity layer will have a direct impact on all the UI layers.
Related
In my project using .NET framework 4.6.1, EF 6.1.4 and IdentityServer3, I set the following DbContext:
public class ValueContext : DbContext
{
public IValueContext(bool lazyLoadingEnabled = false) : base("MyConnectionString")
{
Database.SetInitializer<IValueContext>(null);
Configuration.LazyLoadingEnabled = lazyLoadingEnabled;
}
public DbSet<NetworkUser> NetworkUser { get; set; }
public DbSet<User> User { get; set; }
[...]
And my Entity model User:
[Table("shared.tb_usuarios")]
public class NetworkUser
{
[Column("id")]
[Key()]
public int Id { get; set; }
[Required]
[StringLength(255)]
[Column("email")]
public string Email { get; set; }
[...]
public virtual Office Office { get; set; }
[...]
So far I think its all good.
Then I set this following query in my UserRepository (using DI)
protected readonly ValueContext Db;
public RepositoryBase(ValueContext db)
{
Db = db;
}
public async Task<ImobUser> GetUser(string email)
{
//sometimes I get some error here
return await Db.User.AsNoTracking()
.Include(im => im.Office)
.Include(off => off.Office.Agency)
.Where(u => u.Email == email &&
u.Office.Agency.Active)
.FirstOrDefaultAsync();
}
And everything runs well, until it starts to get many sequential requests, then I begin to get these type of errors, randomly in any function that uses my ValueContext as data source:
System.NotSupportedException: 'A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.'
This is my last hope, as I tried a bunch of different things. Some of them work, and some dont, like:
Convert dbContext to use DI: no difference.
Use context lifetime to run the queries: works, but isnt the solution I want.
Remove asyncronous from requests: works, but also I feel is not the correct way to do.
What Im doing wrong?
EDIT 1
This is how I set up DI in Startup.cs:
private void AddAuth()
{
Builder.Map("/identity", app =>
{
var factory = new IdentityServerServiceFactory()
{
//here I implemented the IdentityServer services to work
ClientStore = new Registration<IClientStore>(typeof(ClientStore)),
[...]
};
AddDependencyInjector(factory);
}
[...]
}
private void AddDependencyInjector(IdentityServerServiceFactory factory)
{
//here I inject all the services I need, as my DbContext
factory.Register(new Registration<ValueContext>(typeof(ValueContext)));
[...]
}
And this is how my UserService is working:
public class UserService : IUserService
{
[Service injection goes here]
//this is a identityServer method using my dbContext implementation on UserRepository
public async Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
SystemType clientId;
Enum.TryParse(context.SignInMessage.ClientId, true, out clientId);
switch (clientId)
{
case 2:
result = await _userService.GetUser(context.UserName);
break;
case 3:
//also using async/await correctly
result = await _userService.Authenticate(context.UserName, context.Password);
break;
default:
result = false;
break;
}
if (result)
context.AuthenticateResult = new AuthenticateResult(context.UserName, context.UserName);
}
Update - After code posted
When using ASP.Net DI and IdentityServer DI together, we have to be careful to make sure that both the IdentityServer and the underlying DbContext are scoped to the OWIN request context, we do that by Injecting the DbContext into the IdentityServer context. this answer has some useful background: https://stackoverflow.com/a/42586456/1690217
I suspect all you need to do is resolve the DbContext, instead of explicitly instantiating it:
private void AddDependencyInjector(IdentityServerServiceFactory factory)
{
//here I inject all the services I need, as my DbContext
factory.Register(new Registration<ValueContext>(resolver => new ValueContext()));
[...]
}
Supporting dicussion, largely irrelevant now...
With EF it is important to make sure that there are no concurrent queries against the same DbContext instance at the same time. Even though you have specified AsNoTracking() for this endpoint there is no indication that this endpoint is actually the culprit. The reason for synchronicity is so that the context can manage the original state, there are many internals that are simply not designed for multiple concurrent queries, including the way the database connection and transactions are managed.
(under the hood the DbContext will pool and re-use connections to the database if they are available, but ADO.Net does this for us, it happens at a lower level and so is NOT an argument for maintaining a singleton DbContext)
As a safety precaution, the context will actively block any attempts to re-query while an existing query is still pending.
EF implements the Unit-Of-Work pattern, you are only expected to maintain the same context for the current operation and should dispose of it when you are done. It can be perfectly acceptable to instantiate a DbContext scoped for a single method, you could instantiate multiple contexts if you so need them.
There is some anecdotal advice floating around the web based on previous versions of EF that suggest there is a heavy initialization sequence when you create the context and so they encourage the singleton use of the EF context. This advice worked in non-async environments like WinForms apps, but it was never good advice for entity framework.
When using EF in a HTTP based service architecture, the correct pattern is to create a new context for each HTTP request and not try to maintain the context or state between requests. You can manually do this in each method if you want to, however DI can help to minimise the plumbing code, just make sure that the HTTP request gets a new instance, and not a shared or recycled one.
Because most client-side programming can create multiple concurrent HTTP requests (this of a web site, how many concurrent requests might go to the same server for a single page load) it is a frivolous exercise to synchronise the incoming requests, or introduce a blocking pattern to ensure that the requests to the DbContext are synchronous or queued.
The overheads to creating a new context instance are expected to be minimal and the DbContext is expected to be used in this way especially for HTTP service implementations, so don't try to fight the EF runtime, work with it.
Repositories and EF
When you are using a repository pattern over the top of EF... (IMO an antipattern itself) it is important that each new instance of the repository gets its own unique instance of the DbContext. Your repo should function the same if you instead created the DbContext instance from scratch inside the Repo init logic. The only reason to pass in the context is to have DI or another common routine to pre-create the DbContext instance for you.
Once the DbContext instance is passed into the Repo, we lose the ability to maintain synchronicity of the queries against it, this is an easy pain point that should be avoided.
No amount of await or using synchronous methods on the DbContext will help you if multiple repos are trying to service requests at the same time against the same DbContext.
I am using EntityFramework.Extended library to perform batch updates. The only problem is EF does not keep track of the batch updates performed by the library. So when I query the DbContext again it does not return the updated entities.
I found that using AsNoTracking() method while querying disables the tracking and gets fresh data from the database. However, since EF does not keep track of the entities queried with AsNoTracking(), I am not able to perform any update on the queried data.
Is there any way to force EF to get the latest data while tracking changes?
Please try this to refresh a single entity:
Context.Entry<T>(entity).Reload()
Edit:
To get fresh data for a collection of entities is worth trying to dispose the DbContext instance after each request.
I stumbled upon this question while searching for a solution to a problem I was having where the navigation properties were not populating after updating the entity. Whenever I attempted to reload the entity from the database, it would grab the entry from the local store instead which would not populate the navigation properties via lazy loading. Instead of destroying the context and recreating one, I found this allowed me to get fresh data with the proxies working:
_db.Entry(entity).State = EntityState.Detached;
The logic behind it was - my update attached the entity so it would track changes to it. This adds it to the local store. Thereafter, any attempts to retrieve the entity with functional proxies would result in it grabbing the local one instead of going out to the db and returning a fresh, proxy-enabled entity. I tried the reload option above, which does refresh the object from the database, but that doesn't give you the proxied object with lazy-loading. I tried doing a Find(id), Where(t => t.Id = id), First(t => t.Id = id). Finally, I checked the available states that were provided and saw there was a "Detached" state. Eureka! Hope this helps someone.
I declared the entity variable, without assignment, as part of the class. This allowed me to dispose of an instance without losing the variable for reference by other methods. I just came across this so it doesn't have alot of runtime under it's belt, but so far it seems to be working fine.
public partial class frmMyForm
{
private My_Entities entity;
public frmMyForm()
{
InitializeComponent();
}
private void SomeControl_Click(object sender, EventArgs e)
{
db.SaveChanges();
db.Dispose();
entity = new My_Entities();
//more code using entity ...
}
Stumbled onto this problem. My app wasn't returning fresh data from the database.
These seems to be 3 solutions:
Reload on select: first you select the object, then reload. Loading it twice if it's not cached?
Detach after use: if you forget to detach an object after use, it's going to cause bugs in completely separate parts of the application that are going to be extremely hard to track down.
Disposing the DbContext after use. Definitely seems like the way to go.
I was creating my DbContext instance in the Repository class. If the DbContext is declared at the Repository level, then I have no control over how it gets disposed. That's a no-no. If I create a new DbContext on every call, then I cannot call Select, modify data, and then call Update.
Seems like something is fundamentally missing in my Repository pattern.
After some research on fundamental Repository pattern, I found the solution: Unit of Work pattern alongside the Repository pattern.
This is an excellent article on the Unit of Work pattern
Or this article from Microsoft. What I currently have is the Repository further up in the page, and what's missing is the section "Implement a Generic Repository and a Unit of Work Class"
Basically, instead of injecting repositories into your services, you access all repositories via a UnitOfWork that you inject into your service. It will solve many problems.
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationContext _context;
public UnitOfWork(ApplicationContext context)
{
_context = context;
Developers = new DeveloperRepository(_context);
Projects = new ProjectRepository(_context);
}
public IDeveloperRepository Developers { get; private set; }
public IProjectRepository Projects { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
Remains the question: how to create the IUnitOfWork instance?
If I create it in the class constructor to be injected just like the repository, then it gets created and destroyed exactly the same way and we're back to the same problem. In ASP.NET and MVC, class instances are short-lived so injecting in the constructor may be fine, but in Blazor and desktop apps, class instances are much more long-lived and it's more of a problem.
This article from Microsoft clearly states that Dependency Injection isn't suitable to manage the lifetime of DbContext in Blazor:
In Blazor Server apps, scoped service registrations can be problematic
because the instance is shared across components within the user's
circuit. DbContext isn't thread safe and isn't designed for concurrent
use. The existing lifetimes are inappropriate for these reasons:
Singleton shares state across all users of the app and leads to
inappropriate concurrent use.
Scoped (the default) poses a similar
issue between components for the same user.
Transient results in a new
instance per request; but as components can be long-lived, this
results in a longer-lived context than may be intended.
They suggest using the Factory pattern, which can be implemented like this
/// <summary>
/// Creates instances of UnitOfWork. Repositories and UnitOfWork are not automatically injected through dependency injection,
/// and this class is the only one injected into classes to give access to the rest.
/// </summary>
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private readonly IDateTimeService _dateService;
private readonly DbContextOptions<PaymentsContext> _options;
public UnitOfWorkFactory(IDateTimeService dateService, DbContextOptions<PaymentsContext> options)
{
_dateService = dateService;
_options = options;
}
/// <summary>
/// Creates a new Unit of Work, which can be viewed as a transaction. It provides access to all data repositories.
/// </summary>
/// <returns>The new Unit of Work.</returns>
public IUnitOfWork Create() => new UnitOfWork(CreateContext(), _dateService);
/// <summary>
/// Creates a new DbContext.
/// </summary>
/// <returns>The new DbContext.</returns>
public PaymentsContext CreateContext() => new(_options);
}
Neither IWorkOfUnit nor any repository will be registered into the IoC container. Only IWorkOfUnitFactory.
And finally... how to share a transaction between various services?
I have a SetStatus method that updates the status field in the database. How is this method supposed to know whether it's a stand-alone operation or part of a larger transaction?
Since class-level dependency injection isn't suitable to manage and share the Work of Unit, then the only option is to pass it as parameters to the methods that need it.
I add an optional IUnitOfWork? workScope = null parameter to every method that needs it, and call Save only if this parameter is null. Here's an implementation.
public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workScope = null)
{
using var unitOfWork = _workFactory.Create();
var work = workScope ?? unitOfWork;
var order = await work.Orders.GetByIdAsync(orderId);
if (order != null)
{
order.Status = status;
work.Orders.Update(order); // DateModified gets set here
if (workScope == null)
{
await work.SaveAsync();
}
}
return order;
}
Another option is to have IUnitOfWorkFactory.Create take the workScope parameter, and when set:
Re-use the existing DbContext
Do not dispose
IUnitOfWork.Save won't submit
My final implementation can be used like this
public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workScope = null)
{
using var unitOfWork = _workFactory.Create(workScope);
var order = await unitOfWork.Orders.GetByIdAsync(orderId);
if (order != null)
{
order.Status = status;
work.Orders.Update(order); // DateModified gets set here
await unitOfWork.SaveAsync(); // Ignored if workScope != null
}
return order;
}
Pheww! That bug was a rabbit hole. It's a pretty long solution but should solve it for good with a solid architecture.
Making the code run on the same context will not yield you updated entities. It will only append new entities created in the database between runs. EF force reload can be done like this:
ObjectQuery _query = Entity.MyEntity;
_query.MergeOption = MergeOption.OverwriteChanges;
var myEntity = _query.Where(x => x.Id > 0).ToList();
For me ...
I access my DbContext like this:
_viewModel.Repo.Context
To force EF to hit the database I do this:
_viewModel.Repo.Context = new NewDispatchContext();
Overwriting the current DbContext with a new instance. Then the next time I use my data services they get the data from the database.
Reloading specific entities was not an option for me because I didn't know the exact entity. I also did not want to create a new DbContext as it is injected by DI. So I resorted to the following trick to "reset" the whole context.
foreach (var entry in db.ChangeTracker.Entries())
{
entry.State = EntityState.Detached;
}
I am using Entity Framework code first in my data layer using the repository pattern. I'm currently designing my WCF web services to connect to the data layer and I'm just a little confused about how to link up with the data layer.
In my test project for the data layer I create a new DbContext class for each test, wrap that in a using block, within that I create the repository class, passing in the context as the constructor parameter. I can then call my methods on the repository to get the data back.
Is this correct for a start and then do I do the same in the WCF service methods?
Eg would I have
public class UserService : IUserService
{
public bool CheckEmailAvailability(string email)
{
try
{
using (var context = new MyDbContext())
{
var repository = new UserDataRepository(context);
var emailAvailable =
repository.GetItems().Count(
u => u.EmailAddress.Equals(email, StringComparison.InvariantCultureIgnoreCase)) == 0;
return emailAvailable;
}
}
}
}
Or am I using the context/repository concept wrong?
It also strikes me that it would be handy to use DI here so I could mock the data context/repository objects in a WCF service test project. Is this something usually done and if so, does anyone have any links to an example or tutorial on this?
First of all, yes it would be better to inject the repository, or (if your chosen DI framework is unable to resolve them) a factory for the repository.
Additionally it might be a good idea to remove the idea of the context from your service. To my mind the repository and it's helpers should deal with the context, and if necessary with sharing the context between various DB calls required to assemble your entities. Then the services can request entities from the repositories without worrying about whether they're being source from a DB or some other data-store.
public class UserService: IUserService
{
IUserRepository userRepository;
... // ctor to inject repository
public bool CheckEmailAvailability(string email)
{
var emailAvailable = !userRepository.GetUserEmails().Any(u => u.EmailAddress.Equals(email, StringComparison.InvariantCultureIgnoreCase));
return emailAvailable;
}
}
public class UserRepository: IUserRepository
{
...
public IEnumerable GetUserEmails()
{
// actually this context should be handled more centrally, included here for sake of illustration
using (var context = new MyDbContext())
{
return repository.GetItems();
}
}
}
Finally, with regard to WCF services, I'm not certain what the best advice is. I've seen code where WCF services were created in the middle of business logic, which was awful, but a limitation of the oldish WPF code-base and limited DI. The better situations are where a ChannelFactory or service factory can be injected into your service layer. You may find this answer on Dependency Injection wcf helpful.
And as always, if you're planning to TDD your code, I'd definitely recommend wrapping the intersection points between layers of your application in interfaces so that they're easier to mock out. It sounds like you've got the right idea there.
I have created a win form solution in C#. The solution has three projects, front layer (FL) business layer (BL) and data layer (DL).
Front layer has all forms. All of the forms implement IForms interface
Business layer has all business logic. All of the business logic classes implement IController interface
Data layer has all data logic that communicates with the sql database. All of the data logic classes inherit IModel interface
I am trying to use MVC design pattern but I am struggling a bit.
Target: Call a method in a class of BL, when I press a button in a form.
If I add BL dll to FL, then I can call the methods of BL.
BL.Class classObject = new BL.Class(this);
classObject.CallMethod();
In the BL, I want to have the reference of the object that called the method so I tried to add reference of the FL dll but it says Circular dll reference.
I want to have a constructor in the BL class:
public BL.Class(IView view)
{
MessageBox(view.Name);
}
How do I architect my solution to achieve this? Please note that the solution is big so I cannot merge all classes into one dll.
Take care,
FM
You are trying to inject presentation logic into your business layer. Your BL should not be creating message boxes - that is the reponsibility of the presentation layer.
You can define events in BL for FL to register. Whenever there's a change in the model, BL should fire an event. It's up for the view to handle it.
Your BL is created a message box. What if your view is a WebForm? BL shouldn't know the detail of view.
Also, your BL project shouldn't have a dll reference to FL. You need to declare IView in BL, not FL.
EDIT:
This is the structure I used before. Your BL defines the interface. Your view has to implement the interface if it wants to use the BL. BL uses the interface to interact with the view. This way, you can build different views that use the same BL.
BL.dll
public interface IView
{
/// <summary>Update the view</summary>
void UpdateView(object myBusinessObject);
/// <summary>Display message</summary>
void ShowMessage(string msg);
}
public class BL
{
// Model and View
private IView _view;
/// <summary>Constructor</summary>
public BL (IView view)
{
_view = view;
}
public void foo()
{
// Do something
// Show message
_view.ShowMessage("Hello World");
}
}
FL.dll
public class FL
{
private BL _myBL;
/// <summary>Constructor</summary>
public FL ()
{
_myBL = new BL(this);
}
/// <summary>Handles user event</summary>
public void handleEvent()
{
// Call BL to do something
_myBL.foo();
}
public void UpdateView(object myBusinessObject)
{
// Update your view
}
public void ShowMessage(string msg)
{
// Display message to user
}
}
I know your programming winforms but you might like to have a look at the WebFormsMVP or MVVM patterns to get some idea's.
"In the BL, I want to have the reference of the object that called the method so I tried to add reference of the FL dll but it says Circular dll reference."
Specifically in the WebFormsMVP project, arguments are passed between between layers based on the way they are in the .Net framework eg (object sender, mySpecialEventArgs e) - the second argument in the parameter "mySpecialEventArgs" has all the info to pass to and fro.
The basic idea behind layered architecture is nth layer should talk to n+1th layer but never cross a layer in between so nth layer cannot jump and directly talk to n+2th layer.
this was a architecture Principle, now coming down to your case.
1- 1st you are talking about the 3 layered architecture , it's not the 3 tier architecture because you are not hosting your DAL OR BL in sepreate server but you are creating individual layers in your solution ( by creating seperate project)
2- As you have three DLLS (FL, BL ,DAL) so now your layering must be FL -- > BL----> DAL where ---> represents talked to.
How you will attain above path (FL -- > BL----> DAL) in your application?
For this , you can add reference of DAL in the BL project and BL reference to your FL project , in this way you can adhere to the architectural Principle stated above.
How to implement MVC in your case?
1- 'C' stands for Controller in the MVC , Controller is a class which is responsible for updating FL and Your Model. So youneed to create a individual unique controller for individual unique functionality in your application ( please note , you don't need to create a controller for each form in your application) so if you have Order Module than create OrderBaseController and pass this controller as a reference to your views (Winform)
2- Controller created in the step 1 can expose model (remember , we have the reference of BL in our FL) which can be consumed by the OrderView.
3- View can change the model through controller( note model is in BL but we have the reference in our FL) but actual database changes still not commited.
How will you do the step 3 above?
interface IOrderController
{
bool SaveOrder(order order);
bool ValidateOrder(order order);
order GetOrder();
}
public class OrderBaseController : IOrderController
{
private OrderServiceFacade Orderhelper { get; set; }
public OrderBaseController()
{
Orderhelper = new OrderServiceFacade();
}
public bool ValidateOrder(order objOrder)
{
}
#region IOrderController Members
public bool SaveOrder(order order)
{
bool success = false;
if (ValidateOrder(order))
{
success = Orderhelper.SaveOrder(order);
}
return success;
}
#endregion
#region IOrderController Members
public order GetOrder()
{
throw new NotImplementedException();
}
#endregion
}
we have just implemented a ordercontroller.Now its time to attach this controller to the view.
public partial class Form1 : Form
{
IOrderController Controller;
public order OrderToBeSaved { get; set; }
public Form1()
{
InitializeComponent();
Controller = new OrderBaseController(); // you have the controller ,
// controller creation can also be delegated to some other component but that's totally different issue.
OrderToBeSaved = Controller.GetOrder(); // You got the order object here , once you get the order object you can bind its properties to the different control.
}
}
you got the fully loaded order object, you can use it's properties to bind the different control on the form.
now it's time to save the order
private void btnSave_Click(object sender, EventArgs e)
{
Controller.SaveOrder(OrderToBeSaved);
}
here we have deleagted saving logic to the controller , now let's see how to save the from the controller. Befor Controller save the order , it has to go through the chain i.e. it must have some logic to talk to the BL (Controller is in FL)
Since we have the reference of BL in our FL , we can directly talk to BL but we go through one more design concept which we call ServiceFacade , A ServiceFacade a class which will be used to delegate the task from one layer to another layer.
class OrderServiceFacade
{
public bool SaveOrder(order order)
{
return OrderDAO.SaveOrder(order);
}
}
Finally we need to save the order in the database so we need to define a class in our DAL we call it OrderDAO.
static class OrderDAO
{
static public bool SaveOrder(order order)
{
// put logic to access database and save the data.
}
static DataTable GetOrder(int OrderID);
}
it is not complete but it gives you the idea how MVC works in a layered scenarion.
There are actually suggested designs for this type of thing. Most of the documentation on this is specifically directed at WPF which you will find if you look up MVVM (another name for MVC).
Basically, you will need a fourth dll which is kind of a framework layer. This is what you will need:
a ServiceProvider class - something that implements IServiceProvider.
Why do we need this? Because your FL is going to provide UI services. In your case a MessageBox service. However, you may discover this is so useful, you will want to create other services. Like something to browse for a file or folder. There's more but we'll stick with this for now.
Your ServiceProvider class should also keep a list of the services it knows about, and provide a way to add to this list. A full service provider would probably include the following.
private readonly Dictionary<Type,object> mServices = new Dictionary<Type,object>();
public void Add(Type type,object value) { ...}
public object GetService(Type serviceType) { ... }
public void Remove(Type type) { ... }
public T Resolve<T>() { return (T)this.GetService(typeof(T)); }
If you make it a singleton class, then you can use this service provide anywhere.
The next job is to create services. In your new framework dll create an interface like IMessageBoxService with a bunch of methods that you might want to call to trigger message to the user. You can start simple with just a function like the following, and add as necessary later.
void ShowError(string message);
You will then need to implement this interface in your FL dll. This implementation will do the actual calls to MessageBox.
The next step is to register your new service implementation with the service provider. So somewhere before the GUI opens call the ServiceProvider.add(typeof(IMessageBoxService), new MyMessageBoxService());
Now in your business logic every time you need to popup a message you can ask the service provider for the message box service, and call the method you want in the IMessageBoxService. Something like this...
IMessageBoxService wMess = ServiceProvider.GetInstance().Resolve<IMessageBoxService>();
wMess.ShowError("My Error Message");
Now you have a clean and beautiful MVC compliant design for popping up error messages without any nasty circular dependencies.
If you want to see more about MVVM and what it is all about you can check out the Microsoft link. Or there is a full MVVM framework that someone has taken the trouble to create for WPF that has a lot of interesting features. If you have time you might want to look at that as well.
Microsoft Introduces MVVM
MVVM Framework
Why BL needs to know about FL? A real BL is independent of FL, At the moment FL is being implemented using WindowsForms, if later the FL was needed to be changed to WPF then, BL needs to change as well. Then what was layered if change in one layer effects the other layer as well? The idea of BL is that it is independent of presentation layer but can be used by different FL. BL should not make any assumption or need to know about FL.
You won't be able to have both layers know about each other if they're implemented as separate projects (causes circular dependencies).
One way to do it would be to use the observer pattern. Setup events in the business classes, and have the presentation layer subscribe to those events. When the business layer completes its actions, it can trigger the events and pass the results back to the view. The business class don't need to know about the presentation layer, it just knows that there are objects subscribed to it, and avoids creating circular dependencies.
Update: Here's one way to have the BL call the FL using events. It's kind of circular logic, but if you've already structured your code around the 2 layers knowing about each other, then you can consider this.
class BL
{
public void CallFromFL (int parameter)
{
int result = DoSomeWork (parameter);
if (OnComplete != null)
OnComplete (result);
}
public event Action <int> OnComplete;
}
class FL
{
void Foo ()
{
BL bl = new BL ();
bl.OnComplete += this.getResult;
bl.CallFromFL (5);
}
void GetResult (int result) {...}
}
I need presentation layer in silverlight, asp.net etc , so everything is through wcf services.
I have number of doubts in my implementation of repository layer, service layer, wcf services
things i currently do
I have repository , its not per table its created per aggregate root
I have service layer its for doing a group of actions involving multiple repository
WCF service wraps the methods in service layer and repository layer
The entities are auto generated by EF
The entities are passed and returned to service layer as complete graph
6.I have two concrete class with all repository , and service called repositorycontainer and service container , Repository container is passed to the service
My repository base
public class RepositoryBase
{
public DataBaseContext _Context;
public RepositoryContainer _RepositoryContainer;
public RepositoryBase(RepositoryContainer repositoryContainer)
{
_RepositoryContainer = repositoryContainer;
_Context = repositoryContainer.Context;
}
public RepositoryBase()
{
_RepositoryContainer = new RepositoryContainer();
_Context = _RepositoryContainer.Context;
}
}
My repository container
public class RepositoryContainer
{
public RepositoryContainer()
{
Context = new DataBaseContext();
}
public RepositoryContainer(DataBaseContext context)
{
Context = context;
}
public DataBaseContext Context
{
get;
set;
}
public SurveyRepository _SurveyRepository;
public SurveyRepository SurveyRepository
{
get
{
return _SurveyRepository ?? (_SurveyRepository = new SurveyRepository(this));
}
}
}
My service container
public class ServiceContainer
{
public ServiceContainer()
{
RepositoryContainer = new RepositoryContainer();
}
public ServiceContainer(RepositoryContainer container)
{
RepositoryContainer = container;
}
public RepositoryContainer RepositoryContainer
{
get;
set;
}
public SurveyService _SurveyService;
public SurveyService SurveyService
{
get
{
return _SurveyService?? (_SurveyService= new SurveyService(this));
}
}
}
To do an operation
I just create RepositoryContainer or ServiceContainer
then calls
RepositoryContainer.Repository.Method()
ServiceContainer.Service.Method()
My doubts are
Is that service / respository container fine ?
I already have the service layer, so as i have wcf service what i call the current service layer servicewrapper or something ?
I need to call repository methods itself eg: GetCategory() etc , also all methods in service layer, So i need to wrap both methods and service in wcf service, is it fine ?
Where to do the caching ? as i am using EF i think there is something way to use a cache provider with EF ,
Is that service / respository
container fine ?
The RepositoryContainer class contains a "SurveyRepository" - but shouldn't the SurveyRepository be an instance of a RepositoryContainer? Same for ServiceContainer and "SurveyService". It would make more sense to me if they were (although it's hard to comment accurately without being more familiar with the project).
You'd then have: ServiceContainer SurveyService = new ServiceContainer(..);
As you have it, I get the impression that "SurveyService" is a specific business concept but it's wrapped up in a more generic type (ServiceContainer); same for SurveyRepository / RepositoryContainer.
This will break SRP, Common Closure Principle and probably Common Reuse Principle.
I'm not sure what other think, but I'm also not a fan of naming instances after their types (except in the most basic of senarios - which this isn't): public SurveyRepository SurveyRepository The name of the type should reflect what the type is (or does) which will be quiote different from a specific instance of it (like ServerContainer and ServeyService).
I already have the service layer, so
as i have wcf service what i call the
current service layer servicewrapper
or something ?
and
So i need to change name of my service
(BL) layer to something service
wrapper or something , then in wcf
service layer i define methods in
repository and service then just calls
curresponding methods in service,
repository
Generally any reusable BL should be in a standalone package and not enclosed (think "hard-coded") in a service layer or WCF service, etc. You'd then create service end-points that sat on top of the BL. If you have business transactions that span different business objects within different packages then you'll need to put that higher level orchestration somewhere higher - I guess this could go in the service layer, but this isn't a trival thing to do, you'll need to carefully consider where certain responsibilities lie.
If the transaction scover different business objects within the same package then the orchestration is much simpler and can be done with another BL type designed to handle that job, which will be part of that package - and not in the service layer.
Regarding the naming - go to a whiteboard and map everything out, and then rename everything as required. At least with a single cohesive overview you'll be able to make clear sense of everything.
BL packages should be named as appropriate to what they do - in business terms. WCF services that wrap these should have a name that fits and this could include reference to the type of channel being used (JSON, WebService, etc). Because you can change the channel a WCF service uses by config (if the service is design correctly) this might not be a good idea - but assuming it doesn't then the extra clarity might be helpful.
These articles might be of help:
http://blogs.oracle.com/christomkins/2009/07/my_thoughts_on_service_naming.html
http://ea.typepad.com/enterprise_abstraction/2006/08/service_naming_.html
What naming convention do you use for the service layer in a Spring MVC application?
I need to call repository methods
itself eg: GetCategory() etc , also
all methods in service layer, So i
need to wrap both methods and service
in wcf service, is it fine ?
Wrapping a service in a service sounds a bit suspect. Only external callers should go through the services - assuming the services are designed to expose the BL to external parties. Internal callers should know which is the appropriate method to call (by virtue of being internal), presumably it's the same method that is exposed by the service.
Where to do the caching ? as i am
using EF i think there is something
way to use a cache provider with EF
I don't know if you can cache in EF4 but it wouldn't surprise me if you can. Where to do caching? - it depends on where the bottle kneck is that you're trying to eliminate.
In your RepositoryContainer, the _SurveyRepository field is public - shouldn't it be private? Otherwise why have a read-only (get) SurveyService property?
public SurveyRepository _SurveyRepository;