I am building a Windows 8 app and I’m running into trouble with async calls. I am going to try to provide as many details as possible because I think there I 2 outcomes to this :
I am doing stuff completely wrong when it comes to the async calls
Or I am doing it wrong but also, it might me the wrong architecture that puts me up with this problem which shouldn’t be there in the first place.
I am a newby to Windows Azure and to MVVM but here’s the situation…
The app is now built for Windows 8 but I also want to be able to use other platforms, so what I have done first is creating a WebAPI project which is published to a Windows Azure Web Site. That way, I can use JSON to transfer data and the WebAPI controllers connect to a Repository that is handling data requests to and from Window Azure Table Storage. The second part is a MVVM Light Windows 8 app that requests data from the Azure Web Site.
So let’s take a more detailed look at the WebAPI project. Here I have a category model to start with.
public class Category : TableServiceEntity
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
public string Parent { get; set; }
}
The category model simply contains a Name and Description (The id is the RowKey of the
TableServiceEntity). Also a string reference is added to the parent category in case the categories are nested. First question arises : Should the Parent be of the type Category instead of string and should the Category model on the backend side have a collection of child categories??
Then I have my IRepository interface to define repositories. (Work in progress ;-)) It is also using the Specification pattern to pass query ranges. This is all working as you can test using your browser and browse to : http://homebudgettracker.azurewebsites.net/api/categories
public interface IRepository<T> where T : TableServiceEntity
{
void Add(T item);
void Delete(T item);
void Update(T item);
IEnumerable<T> Find(params Specification<T>[] specifications);
IEnumerable<T> RetrieveAll();
void SaveChanges();
}
Now that the repositories are clear, let’s take a look at the controller. I have a CategoriesController which is just an ApiController containing an IRepository repository. (Injected with Ninject but irrelevant here)
public class CategoriesController : ApiController
{
static IRepository<Category> _repository;
public CategoriesController(IRepository<Category> repository)
{
if (repository == null)
{
throw new ArgumentNullException("repository");
}
_repository = repository;
}
The controller contains a few methods like for example:
public Category GetCategoryById(string id)
{
IEnumerable<Category> categoryResults =_repository.Find(new ByRowKeySpecification(id));
if(categoryResults == null)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
if (categoryResults.First<Category>() == null)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
return categoryResults.First<Category>();
}
By now, we have seen the backend and let’s move on to the actual problem here: The MvvmLight client and the async http request to the WebAPI controller.
On the client side project, I also have a category model.
public class Category
{
[JsonProperty("PartitionKey")]
public string PartitionKey { get; set; }
[JsonProperty("RowKey")]
public string RowKey { get; set; }
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("Description")]
public string Description { get; set; }
[JsonProperty("Timestamp")]
public string Timestamp { get; set; }
[JsonProperty("Parent")]
public string ParentRowKey { get; set; }
public ObservableCollection<Category> Children { get; set; }
}
Don’t mind the PartitionKey and RowKey properties, the partition key should be left out because it does not concern the application what Azure table service entitiy properties exist. The RowKey could actually be renamed to Id. But not actually relevant here.
The main view’s ViewModel looks like this:
public class MainViewModel : CategoryBasedViewModel
{
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IBudgetTrackerDataService budgetTrackerDataService)
: base(budgetTrackerDataService)
{
PageTitle = "Home budget tracker";
}
}
It extends from a ViewModel that I created to share the logic for pages that contain a Category Observable collection. The important stuff in this ViewModel:
A IBudgetTrackerDataService that is injected into the ViewModel which is a high level data service
An ObservableCollection containing a collection of category-
ViewModel-wrapped categories
Some properties for binding (fe: IsLoadingCategories to handle ProgressRing on the view)
A getCategoriesCompleted callback method called by the IBudgetTrackerDataService after
the async call will be completed
So the code is as follows:
public abstract class CategoryBasedViewModel : TitledPageViewModel
{
private IBudgetTrackerDataService _dataService;
private ObservableCollection<CategoryViewModel> _categoryCollection;
private Boolean isLoadingCategories;
public const string CategoryCollectionPropertyName = "CategoryCollection";
public const string IsLoadingCategoriesPropertyName = "IsLoadingCategories";
public Boolean IsLoadingCategories
{
get
{
return isLoadingCategories;
}
set
{
if (isLoadingCategories != value)
{
isLoadingCategories = value;
RaisePropertyChanged(IsLoadingCategoriesPropertyName);
}
}
}
public ObservableCollection<CategoryViewModel> CategoryCollection
{
get
{
return _categoryCollection;
}
set
{
_categoryCollection = value;
RaisePropertyChanged(CategoryCollectionPropertyName);
}
}
public CategoryBasedViewModel(IBudgetTrackerDataService budgetTrackerDataService)
{
wireDataService(budgetTrackerDataService);
}
public CategoryBasedViewModel(IBudgetTrackerDataService budgetTrackerDataService, string pageTitle)
{
PageTitle = pageTitle;
wireDataService(budgetTrackerDataService);
}
private void wireDataService(IBudgetTrackerDataService budgetTrackerDataService)
{
_dataService = budgetTrackerDataService;
CategoryCollection = new ObservableCollection<CategoryViewModel>();
IsLoadingCategories = true;
_dataService.GetCategoriesAsync(GetCategoriesCompleted);
}
private void GetCategoriesCompleted(IList<Category> result, Exception error)
{
if (error != null)
{
throw new Exception(error.Message, error);
}
if (result == null)
{
throw new Exception("No categories found");
}
IsLoadingCategories = false;
CategoryCollection.Clear();
foreach (Category category in result)
{
CategoryCollection.Add(new CategoryViewModel(category, _dataService));
// Added the dataService as a parameter because the CategoryViewModel will handle the search for Parent Category and Children catagories
}
}
}
This is all working but now I want the Parent/Children relation to work on categories. For this, I have
added logic to the CategoryViewModel so that it is fetching child categories when constructed…
public CategoryViewModel(Category categoryModel, IBudgetTrackerDataService
budgetTrackerDataService)
{
_category = categoryModel;
_dataService = budgetTrackerDataService;
// Retrieve all the child categories for this category
_dataService.GetCategoriesByParentAsync(_category.RowKey,
GetCategoriesByParentCompleted);
}
So the construction of a CategoryBasedViewModel is fetching categories and invokes the callback method GetCategoriesCompleted:
_dataService.GetCategoriesAsync(GetCategoriesCompleted);
That callback method is also calling the constructor of the CategoryViewModel. There, another async method is used to fetch the children of a category.
public CategoryViewModel(Category categoryModel, IBudgetTrackerDataService
budgetTrackerDataService)
{
_category = categoryModel;
_dataService = budgetTrackerDataService;
// Retrieve all the child categories for this category
_dataService.GetCategoriesByParentAsync(_category.RowKey,
GetCategoriesByParentCompleted);
}
And there is my problem! The GetCategoriesByParentAsync is an async call happening inside the other async call and the code just breaks out of the call and does nothing.
The data service implements the interface :
public interface IBudgetTrackerDataService
{
void GetCategoriesAsync(Action<IList<Category>, Exception> callback);
void GetCategoriesByParentAsync(string parent, Action<IList<Category>,
Exception> callback);
}
The async methods contain the following code:
public async void GetCategoriesAsync(Action<IList<Category>, Exception> callback)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetAllCategories();
// Invoke the callback function passed to this operation
callback(categoryEnumerable.ToList<Category>(), null);
}
public async void GetCategoriesByParentAsync(string parent, Action<IList<Category>,
Exception> callback)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await
_client.GetCategoriesWithParent(parent);
// Invoke the callback function passed to this operation
callback(categoryEnumerable.ToList<Category>(), null);
}
Long story short:
Why do these calls fail when nesting the calls?
Secondly, Am I being stupid and should I handle the parent / children
relation of the cagegories differently?
I'm going to sidestep the parent/child relation question for now and just address the async issues.
First, there are a couple general guidelines for async code that I explain in detail in my async/await intro blog post:
Avoid async void (return Task or Task<T> instead).
Use ConfigureAwait(false) when applicable.
I've seen the callback delegate approach taken by others but I'm not sure where it's coming from. It doesn't work well with async and only serves to complicate the code, IMO. The Task<T> type was designed to represent a result value coupled with an Exception and it works seamlessly with await.
So first, your data service:
public interface IBudgetTrackerDataService
{
Task<IList<Category>> GetCategoriesAsync();
Task<IList<Category>> GetCategoriesByParentAsync(string parent);
}
public async Task<IList<Category>> GetCategoriesAsync()
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetAllCategories().ConfigureAwait(false);
return categoryEnumerable.ToList();
}
public async Task<IList<Category>> GetCategoriesByParentAsync(string parent)
{
// Let the HTTP client request the data
IEnumerable<Category> categoryEnumerable = await _client.GetCategoriesWithParent(parent).ConfigureAwait(false);
return categoryEnumerable.ToList();
}
or better yet, if you don't actually need IList<T>:
public interface IBudgetTrackerDataService
{
Task<IEnumerable<Category>> GetCategoriesAsync();
Task<IEnumerable<Category>> GetCategoriesByParentAsync(string parent);
}
public Task<IEnumerable<Category>> GetCategoriesAsync()
{
// Let the HTTP client request the data
return _client.GetAllCategories();
}
public Task<IEnumerable<Category>> GetCategoriesByParentAsync(string parent)
{
// Let the HTTP client request the data
return _client.GetCategoriesWithParent(parent);
}
(and at that point, you may question what purpose your data service is serving).
Moving on to the MVVM async issues: async doesn't play especially well with constructors. I've got a blog post coming out in a few weeks that will address this in detail, but here's the gist:
My personal preference is to use asynchronous factory methods (e.g., public static async Task<MyType> CreateAsync()) but this isn't always possible especially if you're using DI/IoC for your VMs.
In this case, I like to expose a property on my types that require asynchronous initialization (actually, I use an IAsyncInitialization interface, but for your code a convention will work just as well): public Task Initialized { get; }.
This property is set only once in the constructor, like this:
public CategoryViewModel(Category categoryModel, IBudgetTrackerDataService budgetTrackerDataService)
{
_category = categoryModel;
_dataService = budgetTrackerDataService;
// Retrieve all the child categories for this category
Initialized = InitializeAsync();
}
private async Task InitializeAsync()
{
var categories = await _dataService.GetCategoriesByParentAsync(_category.RowKey);
...
}
You then have the option of having your "parent" VM wait for its "child" VMs to initialize. It's not clear that this is what you want, but I'll assume that you want IsLoadingCategories to be true until all the child VMs have loaded:
public CategoryBasedViewModel(IBudgetTrackerDataService budgetTrackerDataService)
{
_dataService = budgetTrackerDataService;
CategoryCollection = new ObservableCollection<CategoryViewModel>();
IsLoadingCategories = true;
Initialized = InitializeAsync();
NotifyOnInitializationErrorAsync();
}
private async Task InitializeAsync()
{
var categories = await _dataService.GetCategoriesAsync();
CategoryCollection.Clear();
foreach (var category in categories)
{
CategoryCollection.Add(new CategoryViewModel(category, _dataService));
}
// Wait until all CategoryViewModels have completed initializing.
await Task.WhenAll(CategoryCollection.Select(category => category.Initialized));
IsLoadingCategories = false;
}
private async Task NotifyOnInitializationErrorAsync()
{
try
{
await Initialized;
}
catch
{
NotifyPropertyChanged("InitializationError");
throw;
}
}
public string InitializationError { get { return Initialized.Exception.InnerException.Message; } }
I added the InitializationError and NotifyOnInitializationErrorAsync to demonstrate one way to surface any errors that may happen during initialization. Since Task doesn't implement INotifyPropertyChanged, there's no automatic notification if/when the initialization fails, so you have to surface it explicitly.
Related
I want to implement a certain functionality, but I do not know where to start. I will describe what I have.
Backend
public enum SourceType { Database, Folder }
public class DatabaseSource
{
public string ServerName { get; set; }
public string DatabaseName { get; set; }
}
public class FolderSource
{
public string FolderName { get; set; }
}
public class TestController : ApiController
{
[HttpPost]
[Route("source")]
public void Post([FromBody]DatabaseSource source) //method one
{
}
[HttpPost]
[Route("source")]
public void Post([FromBody]FolderSource source) //method two
{
}
}
Frontend
export enum SourceType {
Database,
Folder
}
export class DatabaseSource {
public ServerName: string;
public DatabaseName: string;
}
export class FolderSource {
public FolderName: string;
}
var source = new DatabaseSource();
source.ServerName = "serverName";
source.DatabaseName = "dbName";
var obj = {
sourceType: SourceType.Database,
source: source
};
Now imagine that I will send obj to the server. I want that specific controller method to be called depending on the enum. How can I do this?
P.S. The example is greatly simplified.
Your implementation is inconsistent for what you've specified in code.
On the front-end you are describing an object which has a sourceType field and a source object property, while on the backend you're overloading the ApiController method and mapping different REST object resources to a single HTTP method and endpoint (which I believe will not work).
There is no magic way for the ApiController to use your enum property to differentiate between the object types automatically.
A simpler (and better) implementation would be to have separate ApiController classes for your Database and Folder source object POST calls. This follows the principle of REST API design where you are essentially mapping basic CRUD operations to the HTTP methods with object types.
If your intention is to perform an operation based on these parameter objects, then clarify the intention via the API routing for the endpoint as below:
public class TestController : ApiController
{
[HttpPost]
[Route("ETLLoad/Database/source")]
public void Post([FromBody]DatabaseSource source) //method one
{
}
[HttpPost]
[Route("ETLLoad/Folder/source")]
public void Post([FromBody]FolderSource source) //method two
{
}
}
I'm trying to achieve maybe something that might be impossible.
We have a big MVC 5 application. I created a small MVC project to simulate and explain what I want to apply into that big MVC project.
I have a controller that has unique Id. In this sample project the unique Id is regenerated for each request. In the MVC project, it is a bit more complex and different. However it's not relevant in the scope of this example.
public class FooController : Controller
{
public string UniqueId = Guid.NewGuid().ToString("N");
public ActionResult Index()
{
var worker = new WorkerA();
worker.DoWork();
return View();
}
}
The FooController creates WorkerA which creates WorkerB which creates WorkerC and so on. The workers are not the same. They don't have the same interface/implementation. To make the example simple I made them look similar.
Here's the Workers:
public class WorkerA
{
public string UniqueId = string.Empty;
public void DoWork()
{
var worker = new WorkerB();
worker.DoWork();
//...
//...
}
}
public class WorkerB
{
public string UniqueId = string.Empty;
public void DoWork()
{
var worker = new WorkerC();
worker.DoWork();
}
}
I want to have inject the property UniqueId into the worker without having to passing it as a parameter.
I want to avoid having to do this:
public WorkerA(string uniqueId)
{
UniqueId = uniqueId;
}
But I need to do the same for all the other workers.
EDIT
Is there a way to acheive that with ninject?
You can achieve what you want using Microsoft.Practices.Unity in the following manner:
public class WorkerA
{
[Dependency]
public string UniqueId { get; set; }
}
public class WorkerB
{
[Dependency]
public string UniqueId { get; set; }
}
And after that :
var container = new UnityContainer();
container.RegisterType<WorkerA>(new InjectionProperty(nameof(WorkerA.UniqueId),"WorkerAValue"));
container.RegisterType<WorkerA>(new InjectionProperty(nameof(WorkerB.UniqueId), "WorkerBValue"));
Later, you can request the instances from the container with the desired properties configured:
var workerA = container.Resolve<WorkerA>();
var workerB = container.Resolve<WorkerB>();
You can do something like:
worker.GetType().GetField("prop")?.SetValue(worker, "guid");
You could create a singleton class to manage the GUID and deliver it to the child classes that way. This way you can still do it in a constructor but not have to pass it as a parameter
public class GUIDManager
{
private static GUIDManager _instance;
private Guid _activeGuid;
public Guid ActiveGuid {
get { return _activeGuid; }
set { _activeGuid = value; }
}
private GUIDManager()
{
if (_activeGuid == null)
_activeGuid = new Guid();
}
public static GUIDManager GetInstance()
{
if(_instance == null)
{
_instance = new GUIDManager();
}
return _instance;
}
}
public class WorkerB
{
public string UniqueId = string.Empty;
public WorkerB()
{
var manager = GUIDManager.GetInstance();
UniqueId = manager.ActiveGuid.ToString();
}
public void DoWork()
{
var worker = new WorkerC();
worker.DoWork();
}
}
From your question i'm not entirely clear about all the workers in the same request getting the same ID or not. If they all should get the same ID then it's simple:
Wrap the ID in a class and use InRequestScope():
public class BrowserTabId
{
public string browserTabId;
public BrowserTabId(string tabId)
{
if(string.IsNullOrEmpty(tabId))
{
throw new NullArgumentException();
}
this.browserTabId = tabId;
}
public string Id { get { return this.browserTabId; } }
}
Bind<BrowserTabId>()
.ToMethod(ctx =>
new BrowserTabId(HttpContext.Items["BrowserTabId"] as string)))
.InRequestScope();
For testability reasons you can also slap on an interface IUniqueRequestId and create the binding for that.
This will result in all workers / objects created during the same request receiveing the same BrowserTabId. If you don't want to use c-tor injection you can use property injection instead. If you don't want to inject the value all the type, then use a When(..) condition to specify when to inject and when not to. Combine this with the null-object pattern to keep ninject from complaining that it can't inject a requested type.
Property Injection
Adapt a worker as follows:
public class WorkerA
{
[Inject]
public BrowserTabId BrowserTabId { get; set; }
....
}
Note, however, for this to work, like normal constructor injection, it is necessary that either the WorkerA is instanciated by ninject or that Ninject is informed about its existence by Ninject.Inject(workerAInstance)
Scoping
Since you mention that the lifetime of the ID in your actual application is somewhat more complicated, I guess you will have to use something different than InRequestScope - maybe roll your own scope (by using InScope(...)). Or Maybe, InCallScope() is as viable alternative. However, without knowing what exactly it is what you need, it's a bit difficult to advise you properly.
I have a Prototype using WPF + MVVM + PRISM + ENTITY FRAMEWORK
The problem is that im very confuse if i use the ENTITY FRAMEWORK Entities as the Model of the MVVM pattern. I have a Business Logic Layer, and i had problems using mappers on this layer, because im very unhappy on the conversion (Map problem).
What i can do to simplify the code, use a real Model not the Entitie object(for me use the Entitie as model is incorrect on the frontend), with the MVVM pattern on mind... and stay good for changes on the future, it will have 200+ entities on the final version...
Thats my layers...(Please forget about Mapping, since i taked it off putting the EF entities on the ViewModel, but the image represents the correct layers)
Im not using the repository too since i can add it on the end with changes only on the BLL.
VIEW MODEL:
my current prototype do a getall, put it on a grid, and on selectchanged of the grid i put selected item on the textbox, and the save button update this changes to the database.
public class CadastroClienteViewModel : BindableBase, ICadastroClienteViewModel
{
private readonly IClienteBLL _clienteService;
#region Model
//public Cliente ObCliente { get; private set; }
public int ClienteID
{
get { return ((Cliente)cliItems.CurrentItem).ClienteID; }
set
{
((Cliente)cliItems.CurrentItem).ClienteID = value;
OnPropertyChanged("ClienteID");
}
}
public string Nome
{
get { return ((Cliente)cliItems.CurrentItem).Nome; }
set
{
((Cliente)cliItems.CurrentItem).Nome = value;
OnPropertyChanged("Nome");
}
}
#endregion
public CadastroClienteViewModel(IClienteBLL ServiceCliente)
{
//ObCliente = new Cliente();
_clienteService = ServiceCliente;
this.SaveCommand = new DelegateCommand(ExecuteMethodSave);
this.RefreshCommand = new DelegateCommand(ExecuteMethodRefresh, CanExecuteMethodRefresh);
RefreshCommand.Execute(null);
}
private void ExecuteMethodSave()
{
_clienteService.ClienteBLL_Update(((Cliente)cliItems.CurrentItem));
RefreshCommand.Execute(null);
}
private bool CanExecuteMethodRefresh()
{
return true;
}
private void ExecuteMethodRefresh()
{
var personViewModels = _clienteService.ClienteBLL_GetAll();
//cliente = new ObservableCollection<Cliente>(personViewModels);
cliItems = new ListCollectionView(personViewModels.ToList());
cliItems.CurrentChanged += CliItemsOnCurrentChanged;
//OnPropertyChanged("cliente");
OnPropertyChanged("cliItems");
}
private void CliItemsOnCurrentChanged(object sender, EventArgs eventArgs)
{
//OnPropertyChanged("ObCliente");
}
public ICommand SaveCommand { get; private set; }
public ICommand RefreshCommand { get; private set; }
//public ObservableCollection<Cliente> cliente { get; private set; }
public ICollectionView cliItems { get; private set; }
}
MODEL(Im not using it... but i would like):
public class MCliente
{
public int ClienteID { get; set; }
public string Nome { get; set; }
}
EF Entitie:
namespace Sistema.DataEntities.Models
{
public class Cliente
{
public Cliente()
{
}
public int ClienteID { get; set; }
public string Nome { get; set; }
}
BLL:
public class ClienteBLL : IClienteBLL
{
readonly ISistemaContext _context;
public ClienteBLL(ISistemaContext context)
{
_context = context;
}
public IEnumerable<Cliente> ClienteBLL_GetAll()
{
return _context.Cliente.AsEnumerable();
}
public Cliente ClienteBLL_GetByID(int id)
{
return _context.Cliente.Find(id);
}
public bool ClienteBLL_Adicionar(Cliente Obcliente)
{
_context.Cliente.Add(Obcliente);
return _context.SaveChanges() > 0;
}
public bool ClienteBLL_Update(Cliente Obcliente)
{
_context.Cliente.Attach(Obcliente);
_context.Entry(Obcliente).State = EntityState.Modified;
return _context.SaveChanges() > 0;
}
public bool ClienteBLL_Delete(int id)
{
var clubMember = _context.Cliente.Find(id);
_context.Cliente.Remove(clubMember);
return _context.SaveChanges() > 0;
}
I'm adding this as an answer (not a comment) even if it's not a final answer to your question (cause it's opinion-based) but it doesn't fit as a comment. That's just what I would do for a WPF application that requires a database.
I would entirely drop the idea of directly connecting your WPF application to your database. I would build a 3-tiers architecture, i.e. I would create a stateless webservice that does all the stuff on server side.
So you would have:
the database
the webservice (using WCF), that is connected to the database, that does all the data stuff for you (I would even make it responsible of the business stuff too)
the WPF application, that is connected to the webservice:
the View layer is your XAML + your code-behind
the ViewModel layer is, well, your ViewModels (out of scope of your question, but feel free to ask if you have any question about that layer). The ViewModels asynchronously call the webservice
the Model is the client WCF proxy
Some benefits of this approach:
depending on the hardware/network harchitecture, could be a huge performance benefit to only make ONE call to the server instead of N calls (assuming the latency between the DB and the webservice (both on "server side") is lower than the one between the WPF application and the database)
more scalable
all benefits of the stateless approach: one Entity Framework context instantiation per webservice requests, so much easier to deal with concurrency issues (in case you have N WPF instances running concurrently)
easier to maintain (loose coupling between tiers)
easier to test (assuming you actually build tests)
better security (no need to expose a direct access to the database over the network)
I have an existing bank application classes as shown below. The banks account can be of SavingsBankAccount or FixedBankAccount. There is an operation called IssueLumpSumInterest. For FixedBankAccount, the balance need to be updated only if the owner of the account has no other account.
This demands the FixedBankAccount object to know about other accounts of the account owner. How to do this by following SOLID/DDD/GRASP/Information Expert pattern?
namespace ApplicationServiceForBank
{
public class BankAccountService
{
RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
ApplicationServiceForBank.IBankAccountFactory bankFactory;
public BankAccountService(RepositoryLayer.IRepository<RepositoryLayer.BankAccount> repo, IBankAccountFactory bankFact)
{
accountRepository = repo;
bankFactory = bankFact;
}
public void IssueLumpSumInterest(int acccountID)
{
RepositoryLayer.BankAccount oneOfRepositroyAccounts = accountRepository.FindByID(p => p.BankAccountID == acccountID);
int ownerID = (int) oneOfRepositroyAccounts.AccountOwnerID;
IEnumerable<RepositoryLayer.BankAccount> accountsForUser = accountRepository.FindAll(p => p.BankUser.UserID == ownerID);
DomainObjectsForBank.IBankAccount domainBankAccountObj = bankFactory.CreateAccount(oneOfRepositroyAccounts);
if (domainBankAccountObj != null)
{
domainBankAccountObj.BankAccountID = oneOfRepositroyAccounts.BankAccountID;
domainBankAccountObj.AddInterest();
this.accountRepository.UpdateChangesByAttach(oneOfRepositroyAccounts);
//oneOfRepositroyAccounts.Balance = domainBankAccountObj.Balance;
this.accountRepository.SubmitChanges();
}
}
}
public interface IBankAccountFactory
{
DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount);
}
public class MySimpleBankAccountFactory : IBankAccountFactory
{
public DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount)
{
DomainObjectsForBank.IBankAccount acc = null;
if (String.Equals(repositroyAccount.AccountType, "Fixed"))
{
acc = new DomainObjectsForBank.FixedBankAccount();
}
if (String.Equals(repositroyAccount.AccountType, "Savings"))
{
//acc = new DomainObjectsForBank.SavingsBankAccount();
}
return acc;
}
}
}
namespace DomainObjectsForBank
{
public interface IBankAccount
{
int BankAccountID { get; set; }
double Balance { get; set; }
string AccountStatus { get; set; }
void FreezeAccount();
void AddInterest();
}
public class FixedBankAccount : IBankAccount
{
public int BankAccountID { get; set; }
public string AccountStatus { get; set; }
public double Balance { get; set; }
public void FreezeAccount()
{
AccountStatus = "Frozen";
}
public void AddInterest()
{
//TO DO: Balance need to be updated only if the person has no other accounts.
Balance = Balance + (Balance * 0.1);
}
}
}
READING
Issue in using Composition for “is – a “ relationship
Implementing Business Logic (LINQ to SQL)
http://msdn.microsoft.com/en-us/library/bb882671.aspx
Architecting LINQ to SQL applications
Exploring N-Tier Architecture with LINQ to SQL
http://randolphcabral.wordpress.com/2008/05/08/exploring-n-tier-architecture-with-linq-to-sql-part-3-of-n/
Confusion between DTOs (linq2sql) and Class objects!
Domain Driven Design (Linq to SQL) - How do you delete parts of an aggregate?
The first thing I noticed was the improper use of the bank account factory. The factory, pretty much as you have it, should be used by the repository to create the instance based on the data retrieved from the data store. As such, your call to accountRepository.FindByID will return either a FixedBankAccount or SavingsBankAccount object depending on the AccountType returned from the data store.
If the interest only applies to FixedBankAccount instances, then you can perform a type check to ensure you are working with the correct account type.
public void IssueLumpSumInterest(int accountId)
{
var account = _accountRepository.FindById(accountId) as FixedBankAccount;
if (account == null)
{
throw new InvalidOperationException("Cannot add interest to Savings account.");
}
var ownerId = account.OwnerId;
if (_accountRepository.Any(a => (a.BankUser.UserId == ownerId) && (a.AccountId != accountId)))
{
throw new InvalidOperationException("Cannot add interest when user own multiple accounts.");
}
account.AddInterest();
// Persist the changes
}
NOTE: FindById should only accept the ID parameter and not a lambda/Func. You've indicated by the name "FindById" how the search will be performed. The fact that the 'accountId' value is compared to the BankAccountId property is an implementation detail hidden within the method. Name the method "FindBy" if you want a generic approach that uses a lambda.
I would also NOT put AddInterest on the IBankAccount interface if all implementations do not support that behavior. Consider a separate IInterestEarningBankAccount interface that exposes the AddInterest method. I would also consider using that interface instead of FixedBankAccount in the above code to make the code easier to maintain and extend should you add another account type in the future that supports this behavior.
From reading your requirement, here is how I would do it:
//Application Service - consumed by UI
public class AccountService : IAccountService
{
private readonly IAccountRepository _accountRepository;
private readonly ICustomerRepository _customerRepository;
public ApplicationService(IAccountRepository accountRepository, ICustomerRepository customerRepository)
{
_accountRepository = accountRepository;
_customerRepository = customerRepository;
}
public void IssueLumpSumInterestToAccount(Guid accountId)
{
using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
{
Account account = _accountRepository.GetById(accountId);
Customer customer = _customerRepository.GetById(account.CustomerId);
account.IssueLumpSumOfInterest(customer);
_accountRepository.Save(account);
}
}
}
public class Customer
{
private List<Guid> _accountIds;
public IEnumerable<Guid> AccountIds
{
get { return _accountIds.AsReadOnly();}
}
}
public abstract class Account
{
public abstract void IssueLumpSumOfInterest(Customer customer);
}
public class FixedAccount : Account
{
public override void IssueLumpSumOfInterest(Customer customer)
{
if (customer.AccountIds.Any(id => id != this._accountId))
throw new Exception("Lump Sum cannot be issued to fixed accounts where the customer has other accounts");
//Code to issue interest here
}
}
public class SavingsAccount : Account
{
public override void IssueLumpSumOfInterest(Customer customer)
{
//Code to issue interest here
}
}
The IssueLumpSumOfInterest method on the Account aggregate requires the Customer aggregate to help decide whether interest should be issued.
The customer aggregate contains a list of account IDs - NOT a list of account aggregates.
The base class 'Account' has a polymorphic method - the FixedAccount checks that the customer doesn't have any other accounts - the SavingsAccount doesn't do this check.
2 min scan answer..
Not sure why there is a need for 2 representations of a BankAccount
RepositoryLayer.BankAccount and DomainObjectsForBank.IBankAccount. Hide the persistence layer coupled one.. deal with just the domain object in the service.
Do not pass/return Nulls - I think is good advice.
The finder methods look like the LINQ methods which select items from a list of collection. Your methods look like they want to get the first match and exit..in which case your parameters can be simple primitives (Ids) vs lambdas.
The general idea seems right. The service encapsulates the logic for this transaction - not the domain objects. If this changes, only one place to update.
public void IssueLumpSumInterest(int acccountID)
{
var customerId = accountRepository.GetAccount(accountId).CustomerId;
var accounts = accountRepository.GetAccountsForCustomer(customerId);
if ((accounts.First() is FixedAccount) && accounts.Count() == 1)
{
// update interest
}
}
Things that strike me as weird:
Your IBankAccount has a method FreezeAccount, but I presume that all accounts would have quite similar behavior? Perhaps a BankAccount class is warranted that implements some of the interface?
AccountStatus should probably be an enum? What should happen if an account is "Forzen"?
I'm trying to build a notifications provider (alerts) for my application. Currently I only need to generate notifications between requests, but having this functionality wrapped in a provider will allow me to hook it up to the database later.
I have 3 types of notifications:
public enum NotificationType
{
Success,
Error,
Info
}
and a Notification object:
public class Notification
{
public NotificationType Type { get; set; }
public string Message { get; set; }
}
I would like to put all notifications in List<Notification> and load it into ViewData["Notifications"]
I could then use the helper to read ViewData["Notifications"] and render it:
I want to implement my own NotificationProvider which would maintain the List<Notification> object.
I want the provider to read TempData["Notifications"] and load it into List<Notification> Notifications variable. I could then load notifications into ViewData["Notifications"] for my helper to use.
Code below isn't working but I think it shows what I'm trying to do.
public class NotificationProvider
{
public List<Notification> Notifications { get; set; }
private Controller _controller;
public NotificationProvider(Controller controller /* How to pass controller instance? */)
{
_controller = controller;
if (_controller.TempData["Notifications"] != null)
{
Notifications = (List<Notification>)controller.TempData["Notifications"];
_controller.TempData["Notifications"] = null;
}
}
public void ShowNotification(NotificationType notificationType, string message)
{
Notification notification = new Notification();
notification.Type = notificationType;
notification.Message = message;
Notifications.Add(notification);
_controller.TempData["Notifications"] = Notifications;
}
public void LoadNotifications()
{
_controller.ViewData["Notifications"] = Notifications;
}
}
And in each controller a NotificationProvider instance:
public class HomeController
{
private NotificationProvider notificationProvider;
public HomeController()
{
notificationProvider = new NotificationProvider(/* Controller instance */);
notificationProvider.LoadNotifications();
}
}
Question:
How do I pass the controller instance to NotificationProvider class so it can access TempData and ViewData objects. Or if it's possible, how can I access those objects directly from NotificationProvider instance?
I think you only want to pass this, like that. Also, back from comments, TempData will only be available in actions:
public class HomeController
{
public ActionResult Index()
{
var notificationProvider = new NotificationProvider(this);
notificationProvider.LoadNotifications();
return View();
}
}