Is it a good idea to have some static (shared between service instances) Business Logic Layer for wcf services?
In case with DO you'll anyway get it shared, since tiers share the same code (actually, it can vary - e.g. conditionally, or with conditional compilation, but in general, there should be similar API).
So you can share some BLL code in statics, but this isn't a mandatory thing. If you consider this as a good practice (IMO, it is), you can do this.
Btw, a good alternative to static methods for purely BLL stuff are session-level services. An example of such service is shown further.
- Pros: such types are always bound to
Session
; transaction boundaries are automatically set for their methods.
- Cons: likely, you don't need them, if your purpose is to explicitly shape the result (i.e. fetch the graph of entities +
DisconnectedState
to send them to another tier further). Explicit transaction & Session
management is more desirable in this case.
As i understand it will make possible to make some better concurrency checks and better cache usage instead of having a session per service method call.
Well, this fully depends on implementation.
Concerning cache: probably, it's better to wait for our own implementation of caching API (that's one of our high priority tasks).
P.S.:
AuthenticationService - session-level service example (but for ASP.NET MVC app.):
[Service(typeof(IAuthenticationService))]
public class AuthenticationService : SessionBound,
IAuthenticationService
{
private static Key guestAccountKey;
private bool loginInfoIsDefined;
private LoginInfo loginInfo;
public Key GuestAccountKey {
get {
if (guestAccountKey==null) {
var guestAccount = Session.Query.All<Account>().Where(a => a.Name==Account.GuestAccountName).SingleOrDefault();
if (guestAccount!=null)
guestAccountKey = guestAccount.Key;
}
return guestAccountKey;
}
}
public Account GuestAccount {
get { return Session.Query.SingleOrDefault<Account>(guestAccountKey); }
}
public LoginInfo LoginInfo {
get {
if (!loginInfoIsDefined) {
if (Session.IsServiceSession()) {
// Account @ service session must be impersonated directly
ImpersonateAsGuest();
return loginInfo;
}
string identityName = HttpContext.Current.User.Identity.Name;
long accountId;
if (!long.TryParse(identityName, out accountId)) {
ImpersonateAsGuest();
return loginInfo;
}
var account = Session.Query.SingleOrDefault<Account>(accountId);
if (account.IsGuest || account.Status!=AccountStatus.Active) {
ImpersonateAsGuest();
return loginInfo;
}
loginInfo = new LoginInfo(account.Name, account);
loginInfoIsDefined = true;
}
return loginInfo;
}
}
public bool Login(string eMail, string password, out Account account, bool rememberMe = false)
{
account = Session.Query.All<Account>().Where(a => a.EMail==eMail).SingleOrDefault();
if (account==null)
return false;
if (account.PasswordHash!=Account.HashPassword(password))
return false;
if (account.IsGuest || (account.Status != AccountStatus.Active))
return false;
if (!Session.IsServiceSession())
FormsAuthentication.SetAuthCookie(account.Id.ToString(), rememberMe);
ImpersonateAs(account);
return true;
}
public bool LoginWithIdentity(string provider, string identity, out Account account, bool rememberMe)
{
account = (
from a in Session.Query.All<Account>()
where a.Identities.Any(i => i.Provider==provider && i.Identity==identity)
select a
).SingleOrDefault();
if (account==null)
return false;
if (account.IsGuest || (account.Status != AccountStatus.Active))
return false;
if (!Session.IsServiceSession())
FormsAuthentication.SetAuthCookie(account.Id.ToString(), rememberMe);
ImpersonateAs(account);
return true;
}
public void ImpersonateAs(Account account)
{
loginInfo = new LoginInfo(account.Name, account);
loginInfoIsDefined = true;
}
public void ImpersonateAsGuest()
{
loginInfo = new LoginInfo(Account.GuestAccountName, (Account) null);
loginInfoIsDefined = true;
}
public void Logout()
{
if (!Session.IsServiceSession())
FormsAuthentication.SignOut();
ImpersonateAsGuest();
}
// Constructors
[ServiceConstructor]
public AuthenticationService(Session session)
: base(session)
{
}
}
[Serializable]
public sealed class LoginInfo
{
[DisplayName("Имя пользователя")]
public string Name { get; private set; }
public Ref<Account> AccountRef { get; private set; }
public Account Account {
get {
if (AccountRef.Key != null)
return AccountRef.Value;
else
return Session.Demand().Services.Demand<IAuthenticationService>().GuestAccount;
}
}
[DisplayName("Логин не выполнен")]
public bool IsGuest {
get { return AccountRef.Key == null; }
}
// Constructors
public LoginInfo(string name, Ref<Account> accountRef)
{
Name = name;
AccountRef = accountRef;
}
}
public static class ServiceExtensions
{
...
public static IAuthenticationService GetAuthenticationService(this Session session)
{
return session.Services.Demand<IAuthenticationService>();
}
}
Typical usage in ASP.NET MVC application:
public abstract class LoginInfoBasedViewModel : MvcViewModelBase
{
private LoginInfo _loginInfo;
public LoginInfo LoginInfo {
get {
if (_loginInfo==null)
_loginInfo = Session.GetAuthenticationService().LoginInfo;
return _loginInfo;
}
set { _loginInfo = value; }
}
}
answered
Jan 04 '11 at 15:46
Sergey
123●3●3●9