How to pass global variable to a referenced assembly?
I am modifying an asp.net app. It is required to log all Employee (the current user of the website) actions like saving a new customer or update invoice data. The UI layer is calling a referenced assembly BLL.dll.
I want to pass current Emplyee to the referenced assembly. The passed Employee should be shared accross all static methods in that dll. It should be thread safe because the Employee can be changed accross requests.
I can't expose static field in the BLL because the Employee is stored in session state.
I need something not static, Global, accessible by both assemblies (UI layer and BLL.dll), and thread safe.
I am thinking about using some variable stored in current thread object. but I don't know what exactly I should do??
Any workarrounds ??
Thanks
Basically you need something in your BLL that can get the reference. You can use a strategy pattern with an interface.
// IN BLL.dll
public interface IEmployeeContextImplementation
{
Employee Current { get; }
}
public static EmployeeContext
{
private static readonly object ImplementationLock = new object();
private static IEmployeeContextImplementation Implementation;
public static void SetImplementation(IEmployeeContextImplementation impl)
{
lock(ImplementationLock)
{
Implementation = impl;
}
}
public static Employee Current { get { return Implementation.Current; }
}
Then in your web app, implement IEmployeeContextImplementation with the session state and call SetImplementation only once in application start.
However, Session state is only good enough for within the context of a request. If you need it to go on a different thread, you will have to explicitly pass it to a different thread.
Related
Our Asp.net web application is using LINQ-to-SQL (Stored Procs are dragged on dropped on dbml file to create classes) and 3 tier architecture is similar to the one below. I have just created rough methods to give reader proper idea so that he can answer well.
namespace MyDataLayer
{
public class MyDataAccess
{
// global instance of datacontext
MyDataModelDataContext myDB = new MyDataModelDataContext(); (#1)
public void GetUserIDByUsername(string sUserName, ref int iUserID)
{
int? iUserIDout = 0;
// this will make call to SP in SQL DB
myDB.USP_RP_GETUSERIDBYUSERNAME(sUserName, "", ref iUserIDout);
iUserID = (int)iUserIDout;
}
public List<USP_APP_USERDETAILSResult> GetUserDetails(string sUserIDs)
{
// this will make call to SP in SQL DB
return myDB.USP_APP_USERDETAILS(sUserIDs).ToList();
}
...
... // several CRUD methods
}
}
namespace MyBusinessLayer
{
public class SiteUser
{
// global DataAccess instance
MyDataLayer.MyDataAccess myDA = new MyDataAccess(); (#2)
public void GetUserIDByUsername(string sUserName, ref int iUserID)
{
myDA.GetUserIDByUsername(sUserName, ref iUserID);
}
public List<USP_APP_USERDETAILSResult> GetUserDetails(string sUserIDs)
{
// this will make call to SP in SQL DB
return myDA.GetUserDetails(sUserIDs);
}
...
... // several CRUD methods
}
}
namespace MyWebApplication
{
public class BaseWebPage : System.Web.UI.Page
{
// static business layer instance
public static MyBusinessLayer.SiteUser UserBLInstance = new SiteUser(); (#3)
...
}
}
// Index.aspx.cs code fragment
namespace MyWebApplication
{
public class Index : BaseWebPage
{
public void PopulateUserDropDown()
{
// using static business layer instance declared in BaseWebPage
List<USP_APP_USERDETAILSResult> listUsers = UserBLInstance.GetUserDetails("1,2,3");
// do databinding and so on ...
}
...
}
}
Questions
(Ref.#1) Is having global datacontext in DataAccess good approach? yes/no why?
If your suggestion is having datacontext per request what is the best practice for that
(Ref.#2) Is having global DataAccess instance in BusinessLayer good approach? yes/no why?
If your suggestion is having DataAccess instance per request what is the best practice for that
(Ref. #3) Is static business layer instance declared in BaseWebPage good approach? yes/no why?
Best approach to manage life time of BL instance and DL instance in general
We are facing periodic InvalidCastException on production server for a very simple method which works fine if I restart my application from IIS. When this problem is there we can access the same database from SQL Management Studio and can execute same SP
Our prime suspect about this issue is poor DataContext management and I have read many articles on net about managing life time of DataContext but I am now confused about various approach.
That's why I have elaborated my questions so that many in same situation can get clear idea about problem/answer.
(Ref.#1) Is having global datacontext in DataAccess good approach? yes/no why?
Yes.
However, creating it manually inside the dataaccess class means that you can't control the lifetime of the datacontext. Instead, make it then a constructor parameter so that it is injected into the data access
(Ref.#2) Is having global DataAccess instance in BusinessLayer good approach? yes/no why?
Yes. But refer to 1. - make it injectable via the constructor.
(Ref. #3) Is static business layer instance declared in BaseWebPage good approach? yes/no why?
No. Avoid static for complex objects as usually such objects has non-trivial state. And this is when a lot of nasty issues can happen if you share such objects in a concurrent environment.
To summarize.
public class DataAccess {
public DataAccess( DataContext context ) { ... }
}
public class BusinessLayer {
public BusinessLayer( DataAccess access ) { ... }
}
public class MyPage : Page {
...
var ctx = TheDataContext.Current;
var bl = new BusinessLayer( new DataAccess( ctx ) );
}
with data context shared in a request scope:
public partial class TheDataContext {
// Allow the datacontext to be shared in a request-scope
public static TheDataContext Current {
get {
if ( HttpContext.Current.Items["context"] == null )
HttpContext.Current.Items.Add( "context", new TheDataContext() );
return (TheDataContext)HttpContext.Current.Items["context"];
}
}
}
In your sample - your MyDataLayer usually has name Repository. Definitely it is good to have DataContext instance in Repositories and do not try to use them outside. So, only in repositories you will have dependency on Linq-To-Sql, which means that you can create Stub objects for these Repositories and really easy test other parts of your application.
Definitely you should Dispose your Data Context instances, DataContext contains too many objects to keep them alive and let GC to kill them. As you can see you don't create any transaction objects when you are working with DataContextes, so I think that LinqToSql based on idea that you should have everything per transaction (of course you can also try to handle transaction manually, but do you really want to do this?). Disposing datacontextes in methods of Repository is a good approach, because this will not allow you to use cool feature of all ORM frameworks: Lazy Load. If you will try to use Lazy Load - you will like it, but usually it is just one of possible performance degradation cause.
Definitely your should use DataContextes for shorter or the same time of Request, don't try to use LongSession (it is when you trying to keep DataContext for more than one Http Request, it is just pain in ass, nothing else, if you want to read about this, just try to read couple articles about Long Running Session in Hibernate, I tried with nHibernate - don't do this at home ;) ).
I have a DAL project, with an entity data model context bound with EF 4.1
I provide methods to access this context through a "Business" project.
My question is the following:
I'm thinking I shouldn't reference the DAL project from somewhere other than the Business project, so my thinking is, I could add "DataContract" interfaces to the Common project I already have, reference this project within the DAL project, extend the partial classes EF has autogenerated for me, inherit the DataContracts corresponding to each of them, and use these DataContracts as the return value of operations in the Business project, thus isolating the DAL project and avoiding to tightly couple it to either the business, or web projects
Is my thinking correct or am I way off?
Update In re-reading my own question I find this kind of hard to follow, here's an example of how I'd do it, with one of the entities in the model:
The entity is:
namespace Application.Website.Main.Common.DataContracts
{
public interface IServerApplication
{
string Id { get; set; }
string ApplicationName { get; set; }
byte MaxAccountsPerUser { get; set; }
bool Enabled { get; set; }
}
}
This would be in the Application.Website.Main.Common project.
In the DAL project, I would reference the Common project, and extend the partial class of the ServerApplication to inherit from that interface, no further changes required.
namespace Application.Website.Main.DAL
{
public partial class ServerApplication : IServerApplication
{
}
}
In the Business project, I would reference both the DAL and the Common projects, and I'd use these DataContracts as return types:
namespace Application.Website.Main.Business.Entities
{
public class ServerApplication
{
public static IEnumerable<IServerApplication> Enabled()
{
var context = HttpContext.Current.GetDataContext();
return context.ServerApplications.Where(a => a.Enabled);
}
}
}
For completeness, this is the GetDataContext() extension method, placed in the DAL project:
public static class HttpContextExtensions
{
internal const string _contextDataKey = "dataContext";
public static EntityDataModelContext GetDataContext(this HttpContext httpContext)
{
if (httpContext.Items[_contextDataKey] == null)
httpContext.Items.Add(_contextDataKey, new EntityDataModelContext());
return (EntityDataModelContext)httpContext.Items[_contextDataKey];
}
}
By the way, where should I be doing the try/catching?
Should that be done on each of the Business-level methods that use the data context?
Or somewhere else?
What would be the best way to handle exceptions at this level?
Update I found a hole in my logic. If I wanted for instance to have a method in the Business class for a given entity, it would either have to be static and/or an extension method to the interface. Like this:
namespace Application.Website.Main.Business.Entities
{
public class MembershipUser
{
public static int GetRequestCount(this IMembershipUser user)
{
var context = HttpContext.Current.GetDataContext();
return context.ServerAccountRequests.Count(r => r.MembershipUserId == user.UserId);
}
}
}
Or, alternatively, just passing the least information I require for the query:
namespace Application.Website.Main.Business.Entities
{
public class MembershipUser
{
public static int GetRequestCount(Guid userId)
{
var context = HttpContext.Current.GetDataContext();
return context.ServerAccountRequests.Count(r => r.MembershipUserId == userId);
}
}
}
But how could I make a class so I can just use instance methods, like user.GetRequestCount()? Then the DataContract would need to be a class and not an interface... Or maybe I should have this class to also inherit from the DataContract, but then I would have to implement all of it's members, which I don't intend to do again, since that's already handled by EF at DAL level
I'm thinking I shouldn't reference the DAL project from somewhere
other than the Business project...
Depends how you reference it. The short answer is even you Business Logic (BL) shouldn't reference the DAL's physical implementation, it should reference an interface that defines the DAL; the concrete impelentation should then be loaded dynamically.
This is on the assumption that the project is beyond trivial in size - you can still write small PoC's, etc, anyway you like.
I could add "DataContract" interfaces to the Common project I already
have
I'd define the data contracts in their own assembly and not in a common project, otherwise everything that references the common "knows" about the data contracts (is that really what you want), and if you have any dependencies in the data contract then everything that references the common will get those extra unwanted dependencies as well.
Although I defining the data contracts should be it a separate assembly, the DTO/POCO objects could be in the common; I've done that before and had good results, because I use these objects to throw data between different layers - not just between the DAL and BL.
Your amin should be to keep all data access & EF references (like SQL) in the concrete DAL implementation.
How do I create a globally accessible Context object similar to the HttpContext object?
I want to create a custom class library which I want to reference from a website project. In the website project I want to be able to call the following globally:
ClassLibraryName.Context
I cannot create a global property directly in my classlibrary, so how should this be implemented? (I've seen other applications/products use this approach, one of which is Sitecore which has a custom Sitecore.Context object available)
Edit
Might this be a 'valid' solution?
namespace MyLibrary
{
public class Context
{
public static object ContextualObject
{
get;
set;
}
}
}
Yes, this is not hard to implement, if you always run this class in the context of an ASP.NET application, use this approach:
namespace MyLibrary
{
public class Context
{
public static object ContextualObject
{
get
{
var ctx = System.Web.HttpContext.Current.Items[typeof(Context)];
if (ctx == null)
{
ctx = new Context();
System.Web.HttpContext.Current.Items.Add(typeof(Context), ctx);
}
return ctx;
}
set { System.Web.HttpContext.Current.Items[typeof(Context)] = ctx; }
}
}
}
Essentially wrapping the existing HTTP context to provide your own service. This approach also doesn't store the object while the app lives, it only creates it for the current context, and when that response ends, it will die, and be regenerated during the next lifecycle. If that is not OK, store a static reference to context.
I've used this approach similarly in a class library I have at http://nucleo.codeplex.com, it works well.
HTH.
It depends on the lifetime you want the Context object to have. If you want all clients to use the same context, you can go with a singleton implementation.
If you want the context to be unique for each thread or http request you have to use a per request/thread implementation. One way to implement a per http request implementation would be to have a HttpModule create the object at every BeginRequest event and stick it in the HttpContext Items collection.
public static object ContextualObject
{
get { return HttpContext.Current.Items["MyContext"];}
}
You could create an instance of the object on Session_Start in the Global.asax.
I'm trying to work out how to complete my implementation of the Repository pattern in an ASP.NET web application.
At the moment, I have a repository interface per domain class defining methods for e.g. loading and saving instances of that class.
Each repository interface is implemented by a class which does the NHibernate stuff. Castle Windsor sorts out the DI of the class into the interface according to web.config. An example of an implemented class is provided below:
public class StoredWillRepository : IStoredWillRepository
{
public StoredWill Load(int id)
{
StoredWill storedWill;
using (ISession session = NHibernateSessionFactory.OpenSession())
{
storedWill = session.Load<StoredWill>(id);
NHibernateUtil.Initialize(storedWill);
}
return storedWill;
}
public void Save(StoredWill storedWill)
{
using (ISession session = NHibernateSessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(storedWill);
transaction.Commit();
}
}
}
}
As pointed out in a previous thread, the repository class needs to accept an unit of work container (i.e. ISession) rather than instantiating it in every method.
I anticipate that the unit of work container will be created by each aspx page when needed (for example, in a property).
How do I then specify that this unit of work container instance is to be passed into the constructor of StoredWillRepository when Windsor is creating it for me?
Or is this pattern completely wrong?
Thanks again for your advice.
David
I have a persistence framework built on top of NHibernate that is used in a few Web apps. It hides the NH implementation behind an IRepository and IRepository<T> interface, with the concrete instances provided by Unity (thus I could in theory swap out NHibernate for, say, Entity Framework fairly easily).
Since Unity doesn't (or at least the version I'm using doesn't) support the passing in of constructor parameters other than those that are dependency injections themselves, passing in an extant NH ISession isn't possible; but I do want all objects in the UOW to share the same ISession.
I solve this by having a controlling repository class that manages access to the ISession on a per-thread basis:
public static ISession Session
{
get
{
lock (_lockObject)
{
// if a cached session exists, we'll use it
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY];
}
else
{
// must create a new session - note we're not caching the new session here... that's the job of
// BeginUnitOfWork().
return _factory.OpenSession(new NHibernateInterceptor());
}
}
}
}
In this example, PersistenceFrameworkContext.Current.Items accesses an IList<object> that is stored either ThreadStatic if not in a Web context, or within HttpContext.Current.Items if it is in a Web context (to avoid thread-pool problems). The first call to the property instantiates the ISession from the stored factory instance, subsequent calls just retrieve it from storage. The locking will slow things down slightly but not as much as just locking an appdomain-scoped static ISession instance.
I then have BeginUnitOfWork and EndUnitOfWork methods to take care of the UOW - I have specifically disallowed nested UOWs because frankly they were a pain to manage.
public void BeginUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
EndUnitOfWork();
ISession session = Session;
PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session);
}
}
public void EndUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY];
PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY);
session.Flush();
session.Dispose();
}
}
}
Finally, a pair of methods provide access to the domain-type-specific repositories:
public IRepository<T> For<T>()
where T : PersistentObject<T>
{
return Container.Resolve<IRepository<T>>();
}
public TRepository For<T, TRepository>()
where T : PersistentObject<T>
where TRepository : IRepository<T>
{
return Container.Resolve<TRepository>();
}
(Here, PersistentObject<T> is a base class providing ID and Equals support.)
Access to a given repository is thus in the pattern
NHibernateRepository.For<MyDomainType>().Save();
This is then facaded over such that you can use
MyDomainType.Repository.Save();
Where a given type has a specialised repository (ie needs more than it can get from IRepository<T>) then I create an interface deriving from IRepository<T>, an extending implementation inheriting from my IRepository<T> implementation, and in the domain type itself I override the static Repository property using new
new public static IUserRepository Repository
{
get
{
return MyApplication.Repository.For<User, IUserRepository>();
}
}
(MyApplication [which is called something less noddy in the real product] is a facade class which takes care of supplying the Repository instance via Unity so you have no dependency on the specific NHibernate repository implementation within your domain classes.)
This gives me full pluggability via Unity for the repository implementation, easy access to the repository in code without jumping through hoops, and transparent, per-thread ISession management.
There's lots more code than just what's above (and I've simplified the example code a great deal), but you get the general idea.
MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("wibble#wobble.com");
user.FirstName = "Joe"; // change something
user.LastName = "Bloggs";
// you *can* call User.Repository.Save(user), but you don't need to, because...
MyApplication.Repository.EndUnitOfWork();
// ...causes session flush which saves the changes automatically
In my Web app, I have session-per-request, so BeginUnitOfWork and EndUnitOfWork get called in BeginRequest and EndRequest respectively.
I have a pretty similar structure to yours, and here's how I solve your question:
1) To specify my container on each method, I have a separate class ("SessionManager") which I then invoke via a static property. By doing so, here's an example using my Save implementation:
private static ISession NHibernateSession
{
get { return SessionManager.Instance.GetSession(); }
}
public T Save(T entity)
{
using (var transaction = NHibernateSession.BeginTransaction())
{
ValidateEntityValues(entity);
NHibernateSession.Save(entity);
transaction.Commit();
}
return entity;
}
2) My container is not created on each ASPX page. I instantiate all of my NHibernate goodness on the global.asax page.
** A few more things spring up **
3) You don't need to have a helper to instantiate the Load. You might as well use Get instead of Load. More information # Difference between Load and Get.
4) Using your current code, you would have to repeat pretty much the same code for each domain object you need (StoredWillRepository, PersonRepository, CategoryRepository, etc..?), which seems like a drag. You could very well use a generic class to operate over NHibernate, like:
public class Dao<T> : IDao<T>
{
public T SaveOrUpdate(T entity)
{
using (var transaction = NHibernateSession.BeginTransaction())
{
NHibernateSession.SaveOrUpdate(entity);
transaction.Commit();
}
return entity;
}
}
In my implementation, I could then use something like:
Service<StoredWill>.Instance.SaveOrUpdate(will);
Technically, the answer to my question is to use the overload of container.Resolve which allows you to specify the constructor argument as an anonymous type:
IUnitOfWork unitOfWork = [Code to get unit of work];
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork });
But let's face it, the answers provided by everyone else have been much more informative.
I currently have an application which consists of:
User Interface (web page)
BLL (Manager & Domain Objects)
DAL (DataAccess class for each of my Domain Objects).
I use the following in the UI to search for a domain object.
protect sub Button1_Click()
{
IBook book = BookManager.GetBook(txtID.Text);
}
Here is my BLL
public class BookManager
{
public static IBook GetBook(string bookId)
{
return BookDB.GetBook(bookId);
}
}
public class Book : IBook
{
private int? _id
private string _name;
private string _genre;
public string Name
{
get { return _name; }
private set
{
if (string.IsNullOrEmpty(value))
throw new Exception("Invalid Name");
_name = value;
}
}
public string Genre
{
get { return _serial; }
private set
{
if (string.IsNullOrEmpty(value))
throw new Exception("Invalid Genre");
_genre = value;
}
}
// Other IBook Implementations
}
And finally here is my DAL
public class BookDB
{
public static IBook GetBook(int id)
{
// Get Book from database using sproc (not allowed to use any ORM)
// ?? Create IBook Item?
// return IBook
}
How would one create a IBook Object and return it to the Manager?
I'm thinking of returning a DataTable from BookDB to BookManager and having it create the Book Object and return it, but that doesn't seem right.
Is there another way to do this?
Edit:
I decided to seperate each layer into a project and ran into a circular dependency problem in the DAL layer when trying to add a reference to the BLL.
I can't access the Book Class or Interface or anything in BLL from DAL.
Should i just use ado.net objects here and have my manager create the actual object from the ado.net object?
Here's how its layed out
BLL.Managers - BookManager
BLL.Interfaces IBook
BLL.Domain - Book
DAL - BookDB.
Thanks!
You could create dummy Book objects that contain only data. Get, set properties and member values. This book, has 1 property for each field in the database, but doesn't validate anything.
You fill the object from the db, then send it to the BLL.
When you want to save the object, you also send it to the BLL.
Your classes in the BLL could wrap aroud those objects, if that makes sense. This way, it is easy to just send it back to the DAL.
Dummy Book:
public class DummyBook:IBook
{
private nullable<int> _id;
private string _name;
private string _genre;
public string Id
{
get {return _id;}
set {_id = value;}
}
public string Name
{
get {return _name;}
set {_name = value;}
}
public string Genre
{
get {return _genre;}
set {_genre= value;}
}
}
DAL Book:
public class DALBook
{
public static IBook:GetBook(int id)
{
DataTable dt;
DummyBook db = new DummyBook();
// Code to get datatable from database
// ...
//
db.Id = (int)dt.Rows[0]["id"];
db.Name = (string)dt.Rows[0]["name"];
db.Genre = (string)dt.Rows[0]["genre"];
return db;
}
public static void SaveBook(IBook book)
{
// Code to save the book in the database
// you can use the properties from the dummy book
// to send parameters to your stored proc.
}
}
BLL Book:
public class Book : IBook
{
private DummyBook _book;
public Book(int id)
{
_book = DALBook.GetBook(id);
}
public string Name
{
get {return _book.Name;}
set
{
if (string.IsNullOrEmpty(value))
{
throw new Exception("Invalid Name");
}
_book.Name = value;
}
}
// Code for other Properties ...
public void Save()
{
// Add validation if required
DALBook.Save(_book);
}
}
Edit1: The dummy classes should go in their own project(Model, just as stated in the comments is fine). The references would work as follow:
The DAL References the Model Project.
The BLL References the Model and the DAL.
The UI References the BLL.
BookDB should return the IBook instance. I like the repository pattern, which is all about mapping from the db to the domain.
The repository implementation returns instances of the domain objects. This shields the rest of the code from the particular persistence implementation, which can be affected by the technology (database type, web service, [insert something else]) and the format used to save the data.
I would probably use ExecuteReader to create an object in code from the database. The reason for this is that the datatable has more overhead than a reader, because it has more functionality (and was probably created by a reader). Since you aren't doing updates/deletes using the datatable, you don't need the overhead.
That being said, I would make a static helper method in the BookManager class.
internal static IBook BookFromReader(IDataReader reader)
{
Book B = new Book();
B.Prop = reader.GetString(0);
B.Rinse = reader.Repeat();
return B;
}
The reason for this is because the reason you have an interface is because you might want to change the implementation. You may eventuallu have INovel : IBook, IReference : IBook etc and then you'll want to have an abstract factory implementation in your data layer.
public static IBook GetBook(int id)
{
// SqlCommand Command = new Command("SQL or sproc", ValidConnection);
using(IDataReader DR = Command.ExecuteReader(id))
{
// checking omitted
switch(DR.GetInt32(1))
{
case 0:
return BookManager.BookFromReader(DR);
case 1:
return BookManager.NovelFromReader(DR);
etc
}
}
}
Another benefit of the DAL here is that you can cache lookups. You can have a Dictionary that holds books you've looked up, to reduce extra db calls on objects you've already returned. When an update takes place, you remove the cached entity... That's another post though.
If you're using multiple assemblies, interfaces and helper methods will need to reside in a neutral (non-dependent) assembly. Right now in the blog-o-sphere, there is movement towards less assemblies, which means less dependencies, etc.
Here is a link from a blog I read on this topic:
http://codebetter.com/blogs/patricksmacchia/archive/2008/12/08/advices-on-partitioning-code-through-net-assemblies.aspx
Ultimately, I think the answer is that the data layer returns an instance of your interface to the business layer.
Good luck :-)
In my opinion you should never let DAL access BLL. That is an unnecessarily dependency.
Putting the Book class into a new project (perhaps named DomainModel) will fix the circular reference. You could do something like this:
Project BLL reference DAL and DomainModel
Project DAL reference DomainModel
Project UI reference BLL and DomainModel
Project DomainModel reference nothing
The DataTable you want to return is database related, and for BLL, it shouldn't care about what database you are using and what the schema is.
You may use a DB-Object Mapper to map the dbtable to an object in DAL.
If you don't want to return a DataTable, you can pass in an IBook implementation from BookManager for the DAL to populate.
To follow the intended model. the Data Access Layer (DAL) is responsible for retrieving and sending data from and to the data source.
The DAL must not care about any of the business entities your BLL is using as its only job is to retrieve data and return it in a neutral object. It must be neutral for generic reuability, otherwise you might as well not separate the layers as you are defiting its purpose.
Your Business Logic Layer (BLL) must not care how the DAL achieves retrieveing or writing data.
To communicate between the BLL and the DAL you must use neutral objects.
Your BLL passes an object's properties as individual paramters to the methods in the DAL.
the parameters in the DAL are neutral using strings, int, bool, any other .NET objects which are neither specific to a version of the database you are communicating with nor are specific types only existing in your BLL.
The DAL will retrieve the data from where ever by what ever means and return a neutral data object to the caller. This for example could be a DataSet or DataTable or any other object NOT specific to a database type/version your are using. Hence DataSet and DataTable are objects within the System.Data namespace and not the System.Data.SQL,etc... namespace.
In essence:
- BLL passes neutral types to the DAL (e.g.: string, int, bool, long,float, etc..)
- DAL is responsible for converting those types to database specifci types if required before passing them on to the data source
DAL returns neutral data types to the BLL (e.g.: DataSet, DataTable,etc..)
- BLL is responsible for using the content of those neutral data types to create, populate and return specifci Business Entities
Your BLL must reference your DAL. that's it.
You can off course completly ignore this model and hack about as many suggested previously using IBOOK,etc... but than your are not using the intended model and might as well throw it all into a single assembly as you won't be able to maintain it independantly anyway.