Custom Listener in Service Fabric + How to access from client? - azure-service-fabric

Actually I am trying to learn Service fabric and its custom listener part. For this I have created a sample Service Fabric Stateful service which does nothing but returning a number 10 here.
internal sealed class StatefulService1 : StatefulService, INumber
{
protected override IEnumerable<ServiceReplicaListener>
CreateServiceReplicaListeners()
{
return new[] { new ServiceReplicaListener(context => new
HttpCommunicationListener(context), "HttpListener") };
}
public int GetNumber()
{
return 10;
}
}
I have created a custom Listener for this as below:
class HttpCommunicationListener : ICommunicationListener
{
StatefulServiceContext serviceContext;
string publishAddress;
public HttpCommunicationListener(StatefulServiceContext
serviceContext)
{
this.serviceContext = serviceContext;
}
public void Abort()
{
throw new NotImplementedException();
}
public Task CloseAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
var codePackageContext =
this.serviceContext.CodePackageActivationContext;
EndpointResourceDescription desc =
codePackageContext.GetEndpoint("ServiceEndpoint");
var port = desc.Port;
var address = string.Format("http://Address:{0}/", port);
this.publishAddress = address.Replace("Address",
FabricRuntime.GetNodeContext().IPAddressOrFQDN);
return Task.FromResult(publishAddress);
}
}
On the client side, I am struggling to access the GetNumber method using my listener,
My Client Side Implementation:
class CustomCommunicationClient : ICommunicationClient
{
public CustomCommunicationClient(ResolvedServicePartition partition)
{
this.ResolvedServicePartition = partition;
// this.Endpoint = partition.GetEndpoint();
}
public ResolvedServicePartition ResolvedServicePartition { get; set; }
public string ListenerName { get; set; }
public ResolvedServiceEndpoint Endpoint { get; set; }
}
Client Factory impl:
class CustomCommunicationClientFactory : CommunicationClientFactoryBase<CustomCommunicationClient>
{
ResolvedServicePartition partition;
public CustomCommunicationClientFactory(ResolvedServicePartition partition)
{
this.partition = partition;
}
protected override void AbortClient(CustomCommunicationClient client)
{
}
protected override Task<CustomCommunicationClient> CreateClientAsync(string endpoint, CancellationToken cancellationToken)
{
return Task.FromResult(new CustomCommunicationClient(this.partition));
}
protected override bool ValidateClient(CustomCommunicationClient client)
{
return true;
}
protected override bool ValidateClient(string endpoint, CustomCommunicationClient client)
{
return true;
}
}
Main.cs
class Program
{
private static string ServiceName = "fabric:/CustomCommunication.StatefulService/StatefulService1";
static void Main(string[] args)
{
var servicePartition = GetServicePartitionAsync();
var clientFactory = new CustomCommunicationClientFactory(servicePartition.Result);
var partition = new ServicePartitionKey(1);
var myServicePartitionClient = new ServicePartitionClient<CustomCommunicationClient>(clientFactory,
new Uri(ServiceName), partition);
Console.WriteLine("Running request client...");
//var result =
// myServicePartitionClient.InvokeWithRetryAsync(client => client., CancellationToken.None).Result;
Console.ReadKey();
}
private static Task<ResolvedServicePartition> GetServicePartitionAsync()
{
ServicePartitionResolver resolver = ServicePartitionResolver.GetDefault();
Task<ResolvedServicePartition> partition = resolver.ResolveAsync(new Uri(ServiceName),
new ServicePartitionKey(1), CancellationToken.None);
return partition;
}
}
Can someone please guide me on how to do it? It would be great if someone provide me a code for this.

Related

Xamarin forms - Cannot get object from REST API to xaml page

I am developing an Xamarin.Forms app in VS 2019. My REST API is hosted on GoDaddy.
When I call the api I get back my json converted object fine in my viewmodel. But the object is null
from my xaml page. See this code:
public class NewOrderViewModel : BaseViewModel
{
public NewOrderDetails NewOrderDetails { get; set; }
public ICommand OkCommand { get; private set;}
public ICommand CancelCommand { get; private set; }
readonly IPageService _pageService;
public NewOrderViewModel(IPageService pageService, int custId)
{
_pageService = pageService;
OkCommand = new Command(NewOrder);
CancelCommand = new Command(CancelOrder);
NewOrderDetails = new NewOrderDetails();
LoadNewOrderDetails(custId);
}
private async void LoadNewOrderDetails(int custId)
{
using (var client = new HttpClient(new System.Net.Http.HttpClientHandler()))
{
var response = await client.GetStringAsync("http://api.lates.com.au/api/Customers/" + custId.ToString());
var customer = JsonConvert.DeserializeObject<Customer>(response);
await _pageService.DisplayAlert("Value", customer.CustomerName, "OK"); //This confirms the correct customer is returned.
NewOrderDetails.CustomerName = customer.CustomerName;
foreach (var cd in customer.CustomerDepartments)
{
NewOrderDetails.CustomerDepartments.Add(cd);
}
NewOrderDetails.OrderDate = DateTime.Today;
NewOrderDetails.DeliveryDate = DateTime.Today;
NewOrderDetails.CustomerId = custId;
}
}
private void NewOrder()
{
_pageService.PopAsync();
_pageService.PushModalAsync(new CustomerOrder());
}
private void CancelOrder()
{
_pageService.PopAsync();
}
}
public partial class NewOrder : ContentPage
{
public NewOrder()
{
InitializeComponent();
imgAddIcon.Source = FileImageSource.FromFile("AddDocument64By64.png");
}
protected override void OnAppearing()
{
BindingContext = new NewOrderViewModel(new PageService(), 1);
//If i put a break point here the NewOrderDetails property of NewOrderViewModel is null - WHY???
}
}
It seems to be something to do with asynchronous timing. Let me know if you need more info.
Malcolm
If i put a break point here the NewOrderDetails property of
NewOrderViewModel is null - WHY???
At that time your break point hit, the data in NewOrderDetails has not be set because the httpRequest is still requesting and you have to await the request finish to get the data from Api.
To solve your problem, you have to implement INotifyPropertyChanged in both NewOrderDetails and NewOrderViewModel to notify the View update value after you get the data from Api. I will give you some code snippets:
In NewOrderDetails :
public class NewOrderDetails : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public NewOrderDetails()
{
}
public string CustomerName
{
set
{
if (customerName != value)
{
customerName = value;
OnPropertyChanged("CustomerName");
}
}
get
{
return customerName;
}
}
string customerName { get; set; }
}
In NewOrderViewModel :
public class NewOrderViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public NewOrderDetails NewOrderDetaila
{
set
{
if (newOrderDetails != value)
{
newOrderDetails = value;
OnPropertyChanged("NewOrderDetaila");
}
}
get
{
return newOrderDetails;
}
}
NewOrderDetails newOrderDetails { get; set; }
public NewOrderViewModel( int custId)
{
NewOrderDetaila = new NewOrderDetails();
LoadNewOrderDetails(custId);
}
private async void LoadNewOrderDetails(int custId)
{
//...
NewOrderDetaila.CustomerName = "133";
//...
}
}
And in Xaml binding:
<Label Text="{Binding NewOrderDetaila.CustomerName}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
Try and let me know if it works for you.
One problem in your code is here:
using (var client = new HttpClient(new System.Net.Http.HttpClientHandler()))
{
var response = await client.GetStringAsync("http://api.lates.com.au/api/Customers/" + custId.ToString());
var customer = JsonConvert.DeserializeObject<Customer>(response);
await _pageService.DisplayAlert("Value", customer.CustomerName, "OK"); //This confirms the correct customer is returned.
NewOrderDetails.CustomerName = customer.CustomerName;
foreach (var cd in customer.CustomerDepartments)
{
NewOrderDetails.CustomerDepartments.Add(cd);
}
NewOrderDetails.OrderDate = DateTime.Today;
NewOrderDetails.DeliveryDate = DateTime.Today;
NewOrderDetails.CustomerId = custId;
}
HttpClient should be defined as static class, and reused during your application lifetime. Disposing and recreating HttpClient leads to socket errors. Your code is causing multiple requests. I suggest also move this method to Task, that returns the object.
Example method:
internal class SendData
{
private static HttpClient client = new HttpClient();
internal static async Task<string> MakeServerRequest(string url, string content)
{
try
{
var request = new StringContent(content, Encoding.UTF8, "application/json");
var result = await client.PostAsync(url, request);
var response = await result.Content.ReadAsStringAsync();
return response;
}
catch (Exception ex)
{
YOUR ADDITIONAL LOGIC HERE
return null;
}
}
}
This will return JSON string that you can serialize to object, and do whatever your app requires.

How can I achieve the following using IOC?

I want to use IOC with my service and I want to instead inject a class not an interface in the constructor as below in the services layer but I do not want to create a new object from the calling layer like var service = new InvoiceService(new ChangeInvoiceDueDateCommand()) instead I want to create something like this from my controller in MVC where the IInvoiceService is injected into the controller constructor but the problem I see is that
public InvoiceController(IInvoiceService invoiceService, IMapper mapper)
{
_invoiceService = invoiceService;
_mapper = mapper;
}
and then called like this
public ActionResult ChangeInvoiceDueDate(InvoiceChangeDueDateViewModel invoiceChangeDueDateViewModel )
{
var request = _mapper.Map<InvoiceChangeDueDateViewModel, ChangeInvoiceDuedateRequest>(invoiceChangeDueDateViewModel);
InvoiceChangeDueDateResponse response = _invoiceService.ChangeDueDate(request);
return View();
}
Service Layer
public class InvoiceService : IInvoiceService
{
private readonly ChangeInvoiceDueDateCommand _changeInvoiceDueDateCommand;
public InvoiceService(ChangeInvoiceDueDateCommand changeInvoiceDueDateCommand)
{
_changeInvoiceDueDateCommand = changeInvoiceDueDateCommand;
}
public InvoiceChangeDueDateResponse ChangeDueDate(ChangeInvoiceDuedateRequest invoiceChangeDueDateRequest)
{
_changeInvoiceDueDateCommand.Execute(invoiceChangeDueDateRequest);
return new InvoiceChangeDueDateResponse {Status = new Status()};
}
}
Command
public class ChangeInvoiceDueDateCommand : ICommand<ChangeInvoiceDuedateRequest>
{
private readonly IRepository<Invoice> _invoiceRepository;
readonly InvoiceDueDateChangeValidator _validator;
public ChangeInvoiceDueDateCommand(IRepository<Invoice> invoiceRepository)
{
_invoiceRepository = invoiceRepository;
_validator = new InvoiceDueDateChangeValidator();
}
public void Execute(ChangeInvoiceDuedateRequest request)
{
if (_validator.IsDuedateValid(request.NewDuedate))
{
Invoice invoice = _invoiceRepository.GetById(request.Id);
invoice.ChangedDueDate(request.NewDuedate);
_invoiceRepository.SaveOrUpdate(invoice);
}
else
{
throw new InvalidDueDateException();
}
}
}
ICommand
public interface ICommand<T> where T : IRequest
{
void Execute(T request);
}
IRequest
public interface IRequest
{
int Id { get; set; }
}
I worked it out. It was just a Windsor syntax issue. It ended up being as simple as registering the Command using the container.Register(Component.For<ChangeInvoiceDueDateCommand>());

Unit testing generic repository

I'm pretty new to unit testing and I'm having some problems with regards, to unit testing a generic repository in my application. I've implemented the unit of work pattern in my ASP.NET MVC application. My classes look like this:
public class UnitOfWork : IUnitOfWork
{
private bool disposed = false;
private IGenericRepository<Shop> _shopRespository;
public UnitOfWork(PosContext context)
{
this.Context = context;
}
public PosContext Context { get; private set; }
public IGenericRepository<Shop> ShopRepository
{
get
{
return this._shopRespository ?? (this._shopRespository = new GenericRepository<Shop>(this.Context));
}
}
public void SaveChanges()
{
this.Context.SaveChanges();
}
public void Dispose()
{
this.Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.Context.Dispose();
}
this.disposed = true;
}
}
}
public class PosContext : DbContext, IPosContext
{
public DbSet<Shop> Shops { get; private set; }
}
public class GenericRepository<T> : IGenericRepository<T>
where T : class
{
private readonly PosContext context;
private readonly DbSet<T> dbSet;
public GenericRepository(PosContext context)
{
this.context = context;
this.dbSet = context.Set<T>();
}
public virtual IEnumerable<T> Get(
Expression<Func<T, bool>> filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
string includeProperties = "")
{
IQueryable<T> query = this.dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual T Find(object id)
{
return this.dbSet.Find(id);
}
public virtual void Add(T entity)
{
this.dbSet.Add(entity);
}
public virtual void Remove(object id)
{
T entityToDelete = this.dbSet.Find(id);
this.Remove(entityToDelete);
}
public virtual void Remove(T entityToDelete)
{
if (this.context.Entry(entityToDelete).State == EntityState.Detached)
{
this.dbSet.Attach(entityToDelete);
}
this.dbSet.Remove(entityToDelete);
}
public virtual void Update(T entityToUpdate)
{
this.dbSet.Attach(entityToUpdate);
this.context.Entry(entityToUpdate).State = EntityState.Modified;
}
I'm using NUnit and FakeItEasy to write my unit tests. In my set up function, I create a UnitIfWork object with a fake PosContext object. I then populate the context with a few Shop objects.
[SetUp]
public void SetUp()
{
this.unitOfWork = new UnitOfWork(A.Fake<PosContext>());
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 1, Name = "Test name1" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 2, Name = "Test name2" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 3, Name = "Test name3" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 4, Name = "Test name4" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 5, Name = "Test name5" });
this.Controller = new ShopController(this.unitOfWork);
}
It works fine when I test the Find-method of the GenericRepository. The correct Shop object is returned and I can assert that it works fine:
[TestCase]
public void DetailsReturnsCorrectShop()
{
// Arrange
int testId = 1;
// Act
Shop shop = this.unitOfWork.ShopRepository.Find(testId);
ViewResult result = this.Controller.Details(testId) as ViewResult;
// Assert
Shop returnedShop = (Shop)result.Model;
Assert.AreEqual(testId, returnedShop.Id);
}
But when I want to test that the Get-method returns all shops from the repository, if I do not give any filter params, I get an empty list back. I can't figure out why?
[TestCase]
public void IndexReturnsListOfShops()
{
// Arrange
// Act
ViewResult result = this.Controller.Index() as ViewResult;
// Assert
List<Shop> returnedShops = (List<Shop>)result.Model;
Assert.AreEqual(5, returnedShops.Count);
}
The ShopController looks like this:
public class ShopController : Controller
{
private readonly IUnitOfWork unitOfWork;
public ShopController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
// GET: /Shop/
public ActionResult Index()
{
return View(this.unitOfWork.ShopRepository.Get());
}
// GET: /Shop/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Shop shop = this.unitOfWork.ShopRepository.Find(id);
if (shop == null)
{
return HttpNotFound();
}
return View(shop);
}
}
Can you help me figure out why I get an empty list back from the Get-method?

Instance in Caliburn Micro

We are using Caliburn Micro for the first time.
We have a AppBootstrapper inherited from ShellViewModel.
Situvation is that VieModels should have the same instance unless it is reset.
we are able to achieve shared or not shared everytime, but releasing the export whenever needed is still a mystery.
public class AppBootstrapper : Bootstrapper<ShellViewModel>
{
private static CompositionContainer _container;
protected override void Configure()
{
try
{
_container = new CompositionContainer(
new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x))));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(_container);
StyleManager.ApplicationTheme = ThemeManager.FromName("Summer");
_container.Compose(batch);
}
catch (Exception exception)
{
}
}
public static void ReleaseAll()
{
}
protected override object GetInstance(Type serviceType, string key)
{
try
{
var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
var exports = _container.GetExportedValues<object>(contract);
if (exports.Any())
return exports.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
catch (ReflectionTypeLoadException ex)
{
foreach (Exception inner in ex.LoaderExceptions)
{
// write details of "inner", in particular inner.Message
}
return null;
}
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
try
{
return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
}
catch (Exception exception)
{
return null;
}
}
protected override void BuildUp(object instance)
{
_container.SatisfyImportsOnce(instance);
}
}
ShellViewModel
[Export(typeof(ShellViewModel))]
public sealed class ShellViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public ShellViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
Items.Add(compositionContainer.GetExportedValue<AViewModel>());
Items.Add(compositionContainer.GetExportedValue<BViewModel>());
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
public void B()
{
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.B.ToString()));
}
public void A()
{
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public void RESET()
{
AppBootstrapper.ReleaseAll();
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public enum AppMessageType
{
A,
B
}
}
AViewModel
[Export(typeof(AViewModel))]
public sealed class AViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public AViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
DisplayName = ShellViewModel.AppMessageType.A.ToString();
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
}
BViewModel
[Export(typeof(BViewModel))]
public sealed class BViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public BViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
DisplayName = ShellViewModel.AppMessageType.B.ToString();
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
}
Now AViewModel and BViewModel have single instance.
Whenever Release Button is clicked i want to have new instance of AViewModel and BViewModel.
Hoping to get a reply soon.
Regards,
Vivek
When working with an IoC container, the only part of your code that should take it as a dependency should be your composition root (i.e. your AppBootstrapper in this case). You shouldn't be injecting or referencing the container anywhere else in your code (except possibly factories).
If you want your ShellViewModel to control the lifetime of your child view models (A and B), then you should consider injecting view model factories into your ShellViewModel (via constructor injection if they are required dependencies).
Your AViewModelFactory would just have a single Create method that returns a new instance of AViewModel, likewise with the BViewModelFactory. You can simply new up your view models directly in the factories. If your view models have large dependency chains themselves, then you could consider adding a reference to your container in the factories, although preferably consider looking into the MEF ExportFactory<T> type.

ASP.NET MVC2 Implementing Custom RoleManager problem

To create a custom membership provider I followed these instructions:
How do I create a custom membership provider for ASP.NET MVC 2?
and these:
http://mattwrock.com/post/2009/10/14/Implementing-custom-Membership-Provider-and-Role-Provider-for-Authinticating-ASPNET-MVC-Applications.aspx
So far, I've managed to implement custom membership provider and that part works fine. RoleManager still needs some modifications...
Project structure:
alt text http://img691.imageshack.us/img691/3875/21593096.gif
SAMembershipProvider.cs:
public class SAMembershipProvider : MembershipProvider
{
#region - Properties -
private int NewPasswordLength { get; set; }
private string ConnectionString { get; set; }
public bool enablePasswordReset { get; set; }
public bool enablePasswordRetrieval { get; set; }
public bool requiresQuestionAndAnswer { get; set; }
public bool requiresUniqueEmail { get; set; }
public int maxInvalidPasswordAttempts { get; set; }
public int passwordAttemptWindow { get; set; }
public MembershipPasswordFormat passwordFormat { get; set; }
public int minRequiredNonAlphanumericCharacters { get; set; }
public int minRequiredPasswordLength { get; set; }
public string passwordStrengthRegularExpression { get; set; }
public override string ApplicationName { get; set; }
public override bool EnablePasswordRetrieval
{
get { return enablePasswordRetrieval; }
}
public override bool EnablePasswordReset
{
get { return enablePasswordReset; }
}
public override bool RequiresQuestionAndAnswer
{
get { return requiresQuestionAndAnswer; }
}
public override int MaxInvalidPasswordAttempts
{
get { return maxInvalidPasswordAttempts; }
}
public override int PasswordAttemptWindow
{
get { return passwordAttemptWindow; }
}
public override bool RequiresUniqueEmail
{
get { return requiresUniqueEmail; }
}
public override MembershipPasswordFormat PasswordFormat
{
get { return passwordFormat; }
}
public override int MinRequiredPasswordLength
{
get { return minRequiredPasswordLength; }
}
public override int MinRequiredNonAlphanumericCharacters
{
get { return minRequiredNonAlphanumericCharacters; }
}
public override string PasswordStrengthRegularExpression
{
get { return passwordStrengthRegularExpression; }
}
#endregion
#region - Methods -
public override void Initialize(string name, NameValueCollection config)
{
throw new NotImplementedException();
}
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
throw new NotImplementedException();
}
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
{
throw new NotImplementedException();
}
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
throw new NotImplementedException();
}
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
throw new NotImplementedException();
}
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override int GetNumberOfUsersOnline()
{
throw new NotImplementedException();
}
public override string GetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
throw new NotImplementedException();
}
public override string GetUserNameByEmail(string email)
{
throw new NotImplementedException();
}
protected override void OnValidatingPassword(ValidatePasswordEventArgs e)
{
base.OnValidatingPassword(e);
}
public override string ResetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override bool UnlockUser(string userName)
{
throw new NotImplementedException();
}
public override void UpdateUser(MembershipUser user)
{
throw new NotImplementedException();
}
public override bool ValidateUser(string username, string password)
{
AccountRepository accountRepository = new AccountRepository();
var user = accountRepository.GetUser(username);
if (string.IsNullOrEmpty(password.Trim())) return false;
if (user == null) return false;
//string hash = EncryptPassword(password);
var email = user.Email;
var pass = user.Password;
if (user == null) return false;
if (pass == password)
{
//User = user;
return true;
}
return false;
}
#endregion
protected string EncryptPassword(string password)
{
//we use codepage 1252 because that is what sql server uses
byte[] pwdBytes = Encoding.GetEncoding(1252).GetBytes(password);
byte[] hashBytes = System.Security.Cryptography.MD5.Create().ComputeHash(pwdBytes);
return Encoding.GetEncoding(1252).GetString(hashBytes);
}
}
SARoleProvider.cs
public class SARoleProvider : RoleProvider
{
AccountRepository accountRepository = new AccountRepository();
public override bool IsUserInRole(string username, string roleName)
{
return true;
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override void CreateRole(string roleName)
{
throw new NotImplementedException();
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
public override string[] GetRolesForUser(string username)
{
int rolesCount = 0;
IQueryable<RoleViewModel> rolesNames;
try
{
// get roles for this user from DB...
rolesNames = accountRepository.GetRolesForUser(username);
rolesCount = rolesNames.Count();
}
catch (Exception ex)
{
throw ex;
}
string[] roles = new string[rolesCount];
int counter = 0;
foreach (var item in rolesNames)
{
roles[counter] = item.RoleName.ToString();
counter++;
}
return roles;
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
}
public override string[] GetAllRoles()
{
throw new NotImplementedException();
}
}
AccountRepository.cs
public class RoleViewModel
{
public string RoleName { get; set; }
}
public class AccountRepository
{
private DB db = new DB();
public User GetUser(string email)
{
return db.Users.SingleOrDefault(d => d.Email == email);
}
public IQueryable<RoleViewModel> GetRolesForUser(string email)
{
var result = (
from role in db.Roles
join user in db.Users on role.RoleID equals user.RoleID
where user.Email == email
select new RoleViewModel
{
RoleName = role.Name
});
return result;
}
}
webconfig
<membership defaultProvider="SAMembershipProvider" userIsOnlineTimeWindow="15">
<providers>
<clear/>
<add
name="SAMembershipProvider"
type="SA_Contacts.Membership.SAMembershipProvider, SA_Contacts"
connectionStringName ="ShinyAntConnectionString"
/>
</providers>
</membership>
<roleManager defaultProvider="SARoleProvider" enabled="true" cacheRolesInCookie="true">
<providers>
<clear/>
<add
name="SARoleProvider"
type="SA_Contacts.Membership.SARoleProvider"
connectionStringName ="ShinyAntConnectionString"
/>
</providers>
</roleManager>
AccountController.cs:
public class AccountController : Controller
{
SAMembershipProvider provider = new SAMembershipProvider();
AccountRepository accountRepository = new AccountRepository();
public AccountController()
{
}
public ActionResult LogOn()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string userName, string password, string returnUrl)
{
if (!ValidateLogOn(userName, password))
{
return View();
}
var user = accountRepository.GetUser(userName);
var userFullName = user.FirstName + " " + user.LastName;
FormsAuthentication.SetAuthCookie(userFullName, false);
if (!String.IsNullOrEmpty(returnUrl) && returnUrl != "/")
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
private bool ValidateLogOn(string userName, string password)
{
if (String.IsNullOrEmpty(userName))
{
ModelState.AddModelError("username", "You must specify a username.");
}
if (String.IsNullOrEmpty(password))
{
ModelState.AddModelError("password", "You must specify a password.");
}
if (!provider.ValidateUser(userName, password))
{
ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
}
return ModelState.IsValid;
}
}
In some testing controller I have following:
[Authorize]
public class ContactsController : Controller
{
SAMembershipProvider saMembershipProvider = new SAMembershipProvider();
SARoleProvider saRoleProvider = new SARoleProvider();
//
// GET: /Contact/
public ActionResult Index()
{
string[] roleNames = Roles.GetRolesForUser("ilija#ilija.com");
// Outputs admin
ViewData["r1"] = roleNames[0].ToString();
// Outputs True
// I'm not even sure if this method is the same as the one below
ViewData["r2"] = Roles.IsUserInRole("ilija#ilija.com", roleNames[0].ToString());
// Outputs True
ViewData["r3"] = saRoleProvider.IsUserInRole("ilija#ilija.com", "admin");
return View();
}
If I use attribute [Authorize] then everything works ok, but if I use [Authorize(Roles="admin")] then user is always rejected, like he is not in role.
Any idea of what could be wrong here?
Thanks in advance,
Ile
I suspect it's trying to call one of the methods you have yet to implement in SARoleProvider. I would look at the method RoleExists first. Put a break point on each of the methods in SARoleProvider to see which one is being called. Then you'll know which method(s) you'll need to work with.
I found this regarding the Roles and Users parameters for the [Authorize] attribute:
http://www.robertschultz.org/2009/07/29/multiple-roles-with-authorize-attribute-using-enums-in-asp-net-mvc/
Based on the code in the custom Authorize attribute, it looks to me like the name is probably case-sensitive. Have you tried:
[Authorize(Roles="Admin")]
You could also try using the custom code in that article so you can get away from using strings.