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.
Related
In my ViewModel i want o load the Picker source RegionName data from an Azure Region table. I extract data from table in an async method but the Picker displays an empty List even after ObservableCollection or List has changed or crashes.
When using PropertyChanged on the ListRegion list itself the app crashes.
In my models:
public class Region
{
public string Id { get; set; }
public string RegionName { get; set; }
}
In my RegisterPage.xaml:
<Picker SelectedIndex="{Binding RegionsSelectedIndex, Mode=TwoWay}"
ItemsSource="{Binding Regions}"
Margin="0,15,0,0"
Title="Select a region">
</Picker>
in my RegisterPage.cs:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class RegisterPage : ContentPage
{
RegisterViewModel registerVM;
public RegisterPage ()
{
InitializeComponent ();
registerVM = new RegisterViewModel();
this.BindingContext = registerVM;
}
protected override void OnAppearing()
{
base.OnAppearing();
}
in my RegisterPageViewModel:
public class RegisterViewModel: INotifyPropertyChanged
{
ApiServices _apiServices = new ApiServices();
public RegisterViewModel()
{
initializePickerAsync();
}
async private void initializePickerAsync()
{
try
{
var regionsList = await App.MobileService.GetTable<Models.Region>().ToListAsync();
List<string> regionsStringList = new List<string>();
foreach (Models.Region reg in regionsList)
{
regionsStringList.Add(reg.RegionName);
}
Regions = regionsStringList;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/*
private RegisterViewModel (ObservableCollection<Models.Region> regionData)
{
ObservableCollection<string> regionDataAsStringList = new ObservableCollection<string>();
foreach (Models.Region r in regionData)
{
regionDataAsStringList.Add(r.RegionName);
}
this.Regions = regionDataAsStringList;
}
async public static Task<RegisterViewModel> BuildViewModelAsync()
{
ObservableCollection<Models.Region> tmpRegionData = new ObservableCollection<Models.Region>(await App.MobileService.GetTable<Models.Region>().ToListAsync());
return new RegisterViewModel(tmpRegionData);
}
*/
int regionsSelectedIndex = 0;
public int RegionsSelectedIndex
{
get
{
return regionsSelectedIndex;
}
set
{
if (regionsSelectedIndex != value)
{
regionsSelectedIndex = value;
OnPropertyChanged(nameof(RegionsSelectedIndex));
if (regionsSelectedIndex >= 0)
{
Region = Regions[regionsSelectedIndex];
}
}
}
}
// public ObservableCollection<Region> Regions {get;set}
public List<string> Regions
{
get
{
return Regions;
}
set
{
if (Regions != value)
{
Regions = value;
OnPropertyChanged(nameof(Regions));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
you seem to be doing a lot of unnecessary creating and assigning of different lists of data. You should be able to create your ObservableCollection ONCE and then add your data to it, something like this
in your ViewModel
ObservableCollection<Region> Regions { get; set; }
public RegisterViewModel()
{
Regions = new ObservableCollection<Region>();
}
public async void GetData()
{
var regionsList = await App.MobileService.GetTable<Models.Region>().ToListAsync();
foreach (Models.Region reg in regionsList)
{
Regions.Add(reg);
}
}
in your page
protected async override void OnAppearing()
{
base.OnAppearing();
await registerVM.GetData();
}
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.
This is my IGenericRepository
public interface IGenericRepository<T>
{
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Func<T, bool> predicate);
void Insert(T obj);
void Save();
}
and here is its implementation
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private ApplicationDbContext db;
private DbSet<T> table = null;
public GenericRepository(ApplicationDbContext db)
{
this.db = db;
table = db.Set<T>();
}
public IEnumerable<T> GetAll()
{
return table.ToList();
}
public IEnumerable<T> GetMany(Func<T, bool> predicate)
{
var data=table.Where(predicate);
return data;
}
public void Insert(T obj)
{
table.Add(obj);
}
public void Save()
{
db.SaveChanges();
}
}
Here is the repository class for Department
public interface IDepartmentRepository : IGenericRepository<Department>
{
IEnumerable<Department> GetAlldepartment();
void Save1();
}
public class DepartmentRepository : GenericRepository<Department>, IDepartmentRepository
{
private ApplicationDbContext db;
public DepartmentRepository(ApplicationDbContext db):base(db)
{
this.db = db;
}
public IEnumerable<Department> GetAlldepartment()
{
var v= from c in db.Departments
select c;
return v;
}
public void Save1()
{
db.SaveChanges();
}
}
As same I have another repository for Customer
public interface ICustomerRepository : IGenericRepository<Customer>
{
IEnumerable<Customer> SelectAll();
void Update(Customer obj);
void Delete(string id);
}
public class CustomerRepository : GenericRepository<Customer>, ICustomerRepository
{
private ApplicationDbContext db;
//public CustomerRepository()
//{
// this.db = new ApplicationDbContext();
//}
public CustomerRepository(ApplicationDbContext db)
: base(db)
{
this.db = db;
}
public IEnumerable<Customer> SelectAll()
{
var data = this.GetMany(a => a.Id == 1);
return data;
}
public void Update(Customer obj)
{
db.Entry(obj).State = EntityState.Modified;
}
public void Delete(string id)
{
Customer existing = db.Customers.Find(id);
db.Customers.Remove(existing);
}
}
and finally my controller is
public class DepartmentController : Controller
{
DepartmentRepository _departmentRepository=null;
ICustomerRepository _customerRepository=null;
ApplicationDbContext _context = new ApplicationDbContext();
public DepartmentController()
{
this._departmentRepository = new DepartmentRepository(_context);
this._customerRepository = new CustomerRepository(_context);
}
public ActionResult Index()
{
var data = _departmentRepository.GetAlldepartment();
return View(data);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Department Department)
{
_departmentRepository.Insert(Department);
List<Customer> list = new List<Customer>();
for (int i = 0; i < 5; i++)
{
list.Add(new Customer
{
Id = i,
Name = i + " Hi"
});
_customerRepository.Insert(list[i]);
}
_departmentRepository.Save();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
If I want to save Department and Customer from DepartmentController, then I just create an instance of my DBcontext object and pass same object to both repository classes. Is there any problem? If so please help me how I can do this.
Create a TransactionScope in an ActionFilter and put it on your controller action
[AttributeUsage(AttributeTargets.Method)]
public class TransactionScopeAttribute : ActionFilterAttribute
{
private TransactionScope TransactionScope { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
TransactionScope =
new TransactionScope(TransactionScopeOption.Required, new TransactionOptions
{
IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted
});
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (TransactionScope == null)
return;
if (filterContext.Exception == null)
{
TransactionScope.Complete();
return;
}
TransactionScope.Dispose();
}
}
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?
I am using MVC2 with Entity Framework 4 and am trying to implement a Repository and UnitofWork pattern. My Adds and Deletes work fine, however when the edit is called _context.SaveChanges() does save the new changes to the database. I have stepped through the code in debug and see the new values are in the object being passed to the Edit function in the controller, but when the commit is called, nothing is updated.See my code below: Thanks for your help.
Here is my IRepository
namespace EventScheduling.DataModel.Custom
{
public interface IRepository<T>
{
void Add(T newEntity);
void Remove(T entity);
IQueryable<T> Find(Expression<Func<T, bool>> predicate);
IQueryable<T> FindAll();
}
}
SQLRepository Implementation
namespace EventScheduling.DataModel.Custom
{
public class SQLRepository<T>:IRepository<T> where T : class
{
protected ObjectSet<T> _objectSet;
public SQLRepository(ObjectContext context)
{
_objectSet = context.CreateObjectSet<T>();
}
public IQueryable<T> Find(Expression<Func<T, bool>> predicate){
return _objectSet.Where(predicate);
}
public void Add(T newEntity)
{
_objectSet.AddObject(newEntity);
}
public void Remove(T entity)
{
_objectSet.DeleteObject(entity);
}
public IQueryable<T> FindAll()
{
return _objectSet;
}
}
}
Unit of Work Implementation
namespace EventScheduling.DataModel.Custom
{
public interface IUnitOfWork
{
IRepository<utility_event> utility_event { get; }
IRepository<event_activity> event_activity { get; }
IRepository<employee> employee{ get; }
IRepository<activity_resource> activity_resource { get; }
IRepository<Elmah_Error> Elmah_Error { get; }
IRepository< location> location { get; }
IRepository<location_station> location_station { get; }
IRepository<registration_type> registration_type { get; }
IRepository< resource> resource { get; }
IRepository<shift> shift { get; }
IRepository<shift_person> shift_person{ get; }
IRepository<event_type> event_type { get; }
IRepository<status> status { get; }
void Commit();
}
}
SqlUnitOfWork Implementation
namespace EventScheduling.DataModel.Custom
{
public class SqlUnitOfWork: IUnitOfWork
{
readonly ObjectContext _context;
const String ConnectionStringName = "EventEntities";
public SqlUnitOfWork()
{
var connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
_context = new ObjectContext(connectionString);
_context.ContextOptions.LazyLoadingEnabled = true;
}
public IRepository<utility_event> utility_event
{
get {
if (_utilityEvent == null)
{
_utilityEvent = new SQLRepository<utility_event>(_context);
}
return _utilityEvent;
}
}
public IRepository<event_activity> event_activity
{
get
{
if (_eventActivities == null)
{
_eventActivities = new SQLRepository<event_activity>(_context);
}
return _eventActivities;
}
}
public IRepository<employee> employee
{
get
{
if (_employees == null)
{
_employees = new SQLRepository<employee>(_context);
}
return _employees;
}
}
public IRepository<activity_resource> activity_resource
{
get
{
if (_activityResources == null)
{
_activityResources = new SQLRepository<activity_resource>(_context);
}
return _activityResources;
}
}
public IRepository<location> location
{
get
{
if (_locations == null)
{
_locations = new SQLRepository<location>(_context);
}
return _locations;
}
}
public IRepository<location_station> location_station
{
get
{
if (_locationStations == null)
{
_locationStations = new SQLRepository<location_station>(_context);
}
return _locationStations;
}
}
public IRepository<registration_type> registration_type
{
get
{
if (_registrationTypes == null)
{
_registrationTypes = new SQLRepository<registration_type>(_context);
}
return _registrationTypes;
}
}
public IRepository<resource> resource
{
get
{
if (_resources == null)
{
_resources = new SQLRepository<resource>(_context);
}
return _resources;
}
}
public IRepository<shift> shift
{
get
{
if (_shifts == null)
{
_shifts = new SQLRepository<shift>(_context);
}
return _shifts;
}
}
public IRepository<shift_person> shift_person
{
get
{
if (_shiftPersons == null)
{
_shiftPersons = new SQLRepository<shift_person>(_context);
}
return _shiftPersons;
}
}
public IRepository<Elmah_Error> Elmah_Error
{
get
{
if (_ElmahError == null)
{
_ElmahError = new SQLRepository<Elmah_Error>(_context);
}
return _ElmahError;
}
}
public IRepository<event_type> event_type
{
get
{
if (_eventTypes == null)
{
_eventTypes = new SQLRepository<event_type>(_context);
}
return _eventTypes;
}
}
public IRepository<status> status
{
get
{
if (_status == null)
{
_status = new SQLRepository<status>(_context);
}
return _status;
}
}
public void Commit()
{
_context.SaveChanges();
}
SQLRepository<utility_event> _utilityEvent = null;
SQLRepository<event_activity> _eventActivities = null;
SQLRepository<employee> _employees = null;
SQLRepository<activity_resource> _activityResources = null;
SQLRepository<Elmah_Error> _ElmahError = null;
SQLRepository<location> _locations = null;
SQLRepository<location_station> _locationStations = null;
SQLRepository<registration_type> _registrationTypes = null;
SQLRepository<resource> _resources = null;
SQLRepository<shift> _shifts = null;
SQLRepository<shift_person> _shiftPersons = null;
SQLRepository<event_type> _eventTypes = null;
SQLRepository<status> _status = null;
}
}
Controller Edit Implementation
public ActionResult Edit(int id, shift e)
{
if (!ModelState.IsValid)
{
return View(e);
//return to view
}
else
{
e.shift_begin = (DateTime)e.shift_date.Value.Add(e.shift_begin.Value.TimeOfDay);
e.shift_end = (DateTime)e.shift_date.Value.Add(e.shift_end.Value.TimeOfDay);
_unitOfWork.Commit();
return RedirectToAction("Details", "EventActivity", new { id = e.activity_id });
}
}
When you want to update entity in EF you must first Attach entity to ObjectContext instance or instance of related ObjectSet and use ObjectStateManager.ChangeObjectState to set state of entity to EntityState.Modified.
Other possibility is to load entity first from database and merge changes directly into this entity.
Check link in comment for examples or try to use search box. This question is one of the most frequent in EF tag.