Silverlight & RIA Services: Managing Users - silverlight-5.0

I want to manage users and Roles in the cliente side, but i cannot find the way to accomplish this. Links or code will be very appreciated.

Sorry for this abandoned question, i have solved this issue many days ago now, and i fill i should post the answer here because i couldnt find a complete answer in the web and this can help some soul in distress over there. Except for a link that i cannot remember right now but,my apologise to the blog owner for not remember even his name, however, here is the full history:
First create a class to wrap MembershipUser and another one to wrap MembsershipRole:
public class MembershipServiceUser
{
public string Comment { get; set; }
[Editable(false)]
public DateTime CreationDate { get; set; }
[Key]
[Editable(false, AllowInitialValue = true)]
public string Email { get; set; }
public bool IsApproved { get; set; }
[Editable(false)]
public bool IsLockedOut { get; set; }
[Editable(false)]
public bool IsOnline { get; set; }
public DateTime LastActivityDate { get; set; }
[Editable(false)]
public DateTime LastLockoutDate { get; set; }
public DateTime LastLoginDate { get; set; }
[Editable(false)]
public DateTime LastPasswordChangedDate { get; set; }
[Editable(false)]
public string PasswordQuestion { get; set; }
[Key]
[Editable(false, AllowInitialValue = true)]
public string UserName { get; set; }
public MembershipServiceUser() { }
public MembershipServiceUser(MembershipUser user)
{
this.FromMembershipUser(user);
}
public void FromMembershipUser(MembershipUser user)
{
this.Comment = user.Comment;
this.CreationDate = user.CreationDate;
this.Email = user.Email;
this.IsApproved = user.IsApproved;
this.IsLockedOut = user.IsLockedOut;
this.IsOnline = user.IsOnline;
this.LastActivityDate = user.LastActivityDate;
this.LastLockoutDate = user.LastLockoutDate;
this.LastLoginDate = user.LastLoginDate;
this.LastPasswordChangedDate = user.LastPasswordChangedDate;
this.PasswordQuestion = user.PasswordQuestion;
this.UserName = user.UserName;
}
public MembershipUser ToMembershipUser()
{
MembershipUser user = Membership.GetUser(this.UserName);
if (user.Comment != this.Comment) user.Comment = this.Comment;
if (user.IsApproved != this.IsApproved) user.IsApproved = this.IsApproved;
if (user.LastActivityDate != this.LastActivityDate) user.LastActivityDate = this.LastActivityDate;
if (user.LastLoginDate != this.LastLoginDate) user.LastLoginDate = this.LastLoginDate;
return user;
}
}
//Roles
public class MembershipServiceRole {
public MembershipServiceRole() { }
public MembershipServiceRole(string rolename) {
RoleName = rolename;
}
[Key]
[Editable(true, AllowInitialValue = true)]
public string RoleName { get; set; }
}
Second, create a class derived from DomainService to manipulate users and roles wrappers:
[EnableClientAccess(RequiresSecureEndpoint = false /* This should be set to true before the application is deployed */)]
public class MembershipService : DomainService
{
protected override void OnError(DomainServiceErrorInfo errorInfo)
{
TimeoutHelper.HandleAuthenticationTimeout(errorInfo, this.ServiceContext.User);
}
[RequiresRole("Administrator")]
public IEnumerable<MembershipServiceUser> GetUsers()
{
return Membership.GetAllUsers().Cast<MembershipUser>().Select(u => new MembershipServiceUser(u));
}
[RequiresRole("Administrator")]
public IEnumerable<MembershipServiceUser> GetUsersByEmail(string email)
{
return Membership.FindUsersByEmail(email).Cast<MembershipUser>().Select(u => new MembershipServiceUser(u));
}
[RequiresRole("Administrator")]
public MembershipServiceUser GetUsersByName(string userName)
{
MembershipServiceUser retVal = null;
retVal = Membership.FindUsersByName(userName)
.Cast<MembershipUser>()
.Select(u => new MembershipServiceUser(u))
.FirstOrDefault();
return retVal;
}
[Invoke(HasSideEffects = true)]
public void CreateUser(MembershipServiceUser user, string password)
{
if (string.IsNullOrEmpty(user.Email)) {
user.Email = "cambiar#dominio.com";
}
Membership.CreateUser(user.UserName, password, user.Email);
}
[RequiresRole("Administrator")]
public void DeleteUser(MembershipServiceUser user)
{
Membership.DeleteUser(user.UserName);
}
[RequiresRole("Administrator")]
public void UpdateUser(MembershipServiceUser user)
{
Membership.UpdateUser(user.ToMembershipUser());
}
[RequiresRole("Administrator")]
[Update(UsingCustomMethod = true)]
public void ChangePassword(MembershipServiceUser user, string newPassword)
{
MembershipUser u = user.ToMembershipUser();
u.ChangePassword(u.ResetPassword(), newPassword);
}
[RequiresRole("Administrator")]
public void ResetPassword(MembershipServiceUser user)
{
user.ToMembershipUser().ResetPassword();
}
[RequiresRole("Administrator")]
public void UnlockUser(MembershipServiceUser user)
{
user.ToMembershipUser().UnlockUser();
}
#region Roles
[RequiresRole("Administrator")]
public IEnumerable<MembershipServiceRole> GetRoles() {
return Roles.GetAllRoles().Cast<string>().Select(r => new MembershipServiceRole(r));
}
[RequiresRole("Administrator")]
public IEnumerable<MembershipServiceRole> GetRolesForUser(string userName) {
return Roles.GetRolesForUser(userName).Cast<string>().Select(r => new MembershipServiceRole(r));
}
[RequiresRole("Administrator")]
public void CreateRole(MembershipServiceRole role) {
Roles.CreateRole(role.RoleName);
}
[RequiresRole("Administrator")]
public void DeleteRole(MembershipServiceRole role) {
Roles.DeleteRole(role.RoleName);
}
[RequiresRole("Administrator")][Invoke]
public void AddUserToRole(string userName, string roleName) {
if (!Roles.IsUserInRole(userName,roleName))
Roles.AddUserToRole(userName, roleName);
}
[RequiresRole("Administrator")]
[Invoke]
public void RemoveUserFromRole(string userName, string roleName) {
if (Roles.IsUserInRole(userName, roleName))
Roles.RemoveUserFromRole(userName, roleName);
}
#endregion //Roles
}
Third: Use the service the same way you do for your domains clases
Cheers!!!

Related

Cloud formation custom resource creation fails

I am trying to create a custom resource which points to a lambda function and then invoke it to generate random Priority or my ELB Listener.
Code for Lambda function is as follows.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace aws_listenser_rule_priority_generator {
public class Function {
public async Task<int> FunctionHandler(FunctionParams input, ILambdaContext context) {
AmazonElasticLoadBalancingV2Client elbV2Client = new AmazonElasticLoadBalancingV2Client(RegionEndpoint.EUWest1);
var describeRulesResponse = await elbV2Client.DescribeRulesAsync(new DescribeRulesRequest {
ListenerArn = input.ListenerArn
});
var priority = 0;
var random = new Random();
do {
priority = random.Next(1, 50000);
}
while(describeRulesResponse.Rules.Exists(r => r.Priority == priority.ToString()));
return priority;
}
}
public class FunctionParams {
public string ListenerArn { get; set; }
}
}
I have tested this lambda on AWS console with the following parameters and it returns successfully.
{
"ListenerArn": "arn:aws:elasticloadbalancing:eu-west-1:706137030892:listener/app/Cumulus/dfcf28e0393cbf77/cdfe928b0285d5f0"
}
But as soon as I try to use this with Cloud Formation. The Custom Resource is stuck at creation in progress.
Resources:
ListenerPriority:
Type: Custom::Number
Properties:
ServiceToken: "arn:aws:lambda:eu-west-1:706137030892:function:GenerateListenerPriority"
ListenerArn: "arn:aws:elasticloadbalancing:eu-west-1:706137030892:listener/app/Cumulus/dfcf28e0393cbf77/cdfe928b0285d5f0"
The issue with the previous approach was the data format. When we are working with the Custom Resources, Cloud Formation sends a request in a specified format and response is expected asynchronously at a specified response URL.
I had to make following updates to the code:
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace aws_listenser_rule_priority_generator
{
public class Function
{
public async Task FunctionHandler(CustomResourceRequest<CustomResourceRequestProperties> crfRequest, ILambdaContext context)
{
var jsonResponse = string.Empty;
if(crfRequest.RequestType.ToUpperInvariant() != "CREATE") {
jsonResponse = JsonSerializer.Serialize(new CustomResourceResponse<object> {
Status = "SUCCESS",
Reason = string.Empty,
PhysicalResourceId = Guid.NewGuid().ToString(),
StackId = crfRequest.StackId,
RequestId = crfRequest.RequestId,
LogicalResourceId = crfRequest.LogicalResourceId,
Data = new {
Dummy = "Dummy"
}
});
}
else {
AmazonElasticLoadBalancingV2Client elbV2Client = new AmazonElasticLoadBalancingV2Client(RegionEndpoint.EUWest1);
var describeRulesResponse = await elbV2Client.DescribeRulesAsync(new DescribeRulesRequest {
ListenerArn = crfRequest.ResourceProperties.ListenerArn
});
var priority = 0;
var random = new Random();
do {
priority = random.Next(1, 50000);
}
while(describeRulesResponse.Rules.Exists(r => r.Priority == priority.ToString()));
jsonResponse = JsonSerializer.Serialize(new CustomResourceResponse<object> {
Status = "SUCCESS",
Reason = string.Empty,
PhysicalResourceId = Guid.NewGuid().ToString(),
StackId = crfRequest.StackId,
RequestId = crfRequest.RequestId,
LogicalResourceId = crfRequest.LogicalResourceId,
Data = new {
Priority = priority
}
});
}
var byteArray = Encoding.UTF8.GetBytes(jsonResponse);
var webRequest = WebRequest.Create(crfRequest.ResponseURL) as HttpWebRequest;
webRequest.Method = "PUT";
webRequest.ContentType = string.Empty;
webRequest.ContentLength = byteArray.Length;
using(Stream datastream = webRequest.GetRequestStream()) {
await datastream.WriteAsync(byteArray, 0, byteArray.Length);
}
await webRequest.GetResponseAsync();
}
}
public class CustomResourceRequest<T> where T : ICustomResourceRequestProperties {
public string RequestType { get; set; }
public string ResponseURL { get; set; }
public string StackId { get; set; }
public string RequestId { get; set; }
public string ResourceType { get; set; }
public string LogicalResourceId{ get; set; }
public string PhysicalResourceId { get; set; }
public T ResourceProperties { get; set; }
public T OldResourceProperties { get; set; }
}
public class CustomResourceResponse<T> {
public string Status { get; set; }
public string Reason { get; set; }
public string PhysicalResourceId { get; set; }
public string StackId { get; set; }
public string RequestId { get; set; }
public string LogicalResourceId { get; set; }
public bool NoEcho { get; set; }
public T Data { get; set; }
}
public interface ICustomResourceRequestProperties {
public string ServiceToken { get; set; }
}
public class CustomResourceRequestProperties : ICustomResourceRequestProperties
{
string ICustomResourceRequestProperties.ServiceToken { get; set; }
public string ListenerArn { get; set; }
}
}
The above code expects everything to work without any issues and there are no try catch blocks. Best practice would be to have try catch blocks and send a failure response.
Following URLs can be referred for more details:
Custom resources
Custom resource request objects
Custom resource response objects
Using AWS Lambda with AWS CloudFormation

Iterating through SharePoint 2013 REST API List Items

I am writing a Provider Hosted APP using SP 2013 and I have a data layer which uses REST to CRUD on sharepoint lists. Now I got the List items in JSON format but I am not able to iterate through the list data can you please help in doing that? (is there any class for List Items which I can deserialize into?)
This is the code
public JToken GetListData(string webUrl, string userName, SecureString password, string listTitle)
{
using (var client = new WebClient())
{
client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
client.Credentials = new SharePointOnlineCredentials(userName, password);
client.Headers.Add(HttpRequestHeader.ContentType, "application/json;odata=verbose");
client.Headers.Add(HttpRequestHeader.Accept, "application/json;odata=verbose");
var endpointUri = new Uri(new Uri(webUrl), string.Format("/sites/DTF/_api/web/lists/getbytitle('{0}')/Items", listTitle));
var result = client.DownloadString(endpointUri);
var t = JToken.Parse(result);
return t["d"];
}
}
You need to use the DataContractJsonSerializer class to deserialize the data as is demonstrated here:
http://www.codeproject.com/Articles/272335/JSON-Serialization-and-Deserialization-in-ASP-NET
To make this work though you have to create classes that match the structure of the Json. The easiest way to do this copying a raw Json response into a tool which will generate the classes like this one:
http://json2csharp.com/
The actual classes you will need to generate varies based on the structure of the data you are getting in your REST response. Here is an example I created that demonstrates making a request, parsing the Json Response and downloading a file based on the result:
public class JsonHelper
{
/// JSON Serialization
public static string JsonSerializer<T>(T t)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, t);
string jsonString = Encoding.UTF8.GetString(ms.ToArray());
ms.Close();
return jsonString;
}
/// JSON Deserialization
public static T JsonDeserialize<T>(string jsonString)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
T obj = (T)ser.ReadObject(ms);
return obj;
}
}
//Custom Json Classes
public class RootObject
{
public D d { get; set; }
}
public class D
{
public GetContextWebInformation GetContextWebInformation { get; set; }
public List<Result> results { get; set; }
}
public class GetContextWebInformation
{
public int FormDigestTimeoutSeconds { get; set; }
public string FormDigestValue { get; set; }
public string LibraryVersion { get; set; }
public string SiteFullUrl { get; set; }
public string WebFullUrl { get; set; }
}
public class Result
{
public ContentType ContentType { get; set; }
public string EncodedAbsUrl { get; set; }
public string FileLeafRef { get; set; }
public Folder Folder { get; set; }
public int FileSystemObjectType { get; set; }
public int Id { get; set; }
public string ContentTypeId { get; set; }
public string Title { get; set; }
public int? ImageWidth { get; set; }
public int? ImageHeight { get; set; }
public string ImageCreateDate { get; set; }
public object Description { get; set; }
public object Keywords { get; set; }
public string OData__dlc_DocId { get; set; }
public int ID { get; set; }
public string Created { get; set; }
public int AuthorId { get; set; }
public string Modified { get; set; }
public int EditorId { get; set; }
public object OData__CopySource { get; set; }
public int? CheckoutUserId { get; set; }
public string OData__UIVersionString { get; set; }
public string GUID { get; set; }
}
//SharePoint Calls
class Program
{
static void Main()
{
string url = "https://sharepoint.wilsonconst.com/";
string filename = "2010-07-23 13.32.22.jpg";
string digest = "";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
string cmd = "_api/contextinfo";
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("ContentType", "application/json");
client.DefaultRequestHeaders.Add("ContentLength", "0");
StringContent httpContent = new StringContent("");
HttpResponseMessage response = client.PostAsync(cmd, httpContent).Result;
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
RootObject sp = JsonHelper.JsonDeserialize<RootObject>(content);
digest = sp.d.GetContextWebInformation.FormDigestValue;
}
client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "GET");
string uri = "_api/web/lists/GetByTitle('Wilson Pictures')/Items?$select=ID,FileLeafRef,EncodedAbsUrl&$filter=FileLeafRef eq '" + filename + "'";
HttpResponseMessage response2 = client.GetAsync(uri).Result;
response2.EnsureSuccessStatusCode();
if (response2.IsSuccessStatusCode)
{
string listItems = response2.Content.ReadAsStringAsync().Result;
RootObject sp = JsonHelper.JsonDeserialize<RootObject>(listItems);
foreach (Result result in sp.d.results)
{
MemoryStream stream = (MemoryStream)client.GetAsync(result.EncodedAbsUrl).Result.Content.ReadAsStreamAsync().Result;
using(FileStream fileStream = System.IO.File.Create(#"C:\" + result.FileLeafRef))
{
stream.WriteTo(fileStream);
}
}
}
else
{
var content = response.Content.ReadAsStringAsync();
}
}
This seems like a lot of complexity, but really it makes working with Json objects quite easy and takes only moments to setup before you can start calling your custom objects to easily manipulate the data.
Assuming that you want to retrieve data from SharePoint Online the following example demonstrates how to consume SharePoint REST Interface via WebClient Class:
using (var client = new SPRestClient(webUri.ToString()))
{
client.Credentials = GetCredentials(webUri,userName,password);
client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
var data = client.GetJson("/_api/lists/getbytitle('Tasks')/items"); //get list items
//print list item's title
foreach (var item in data["d"]["results"])
{
Console.WriteLine(item["Title"]);
}
}
where
public static SharePointOnlineCredentials GetCredentials(Uri webUri, string userName, string password)
{
var securePassword = new SecureString();
foreach (var ch in password) securePassword.AppendChar(ch);
return new SharePointOnlineCredentials(userName, securePassword);
}
and
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace SharePoint.Client
{
public class SPRestClient : WebClient
{
public SPRestClient(string webUri)
{
BaseAddress = webUri;
FormatType = JsonFormatType.Verbose;
}
public JObject GetJson(string requestUri)
{
return ExecuteJson(requestUri, HttpMethod.Get, null, default(string));
}
public JObject ExecuteJson<T>(string requestUri, HttpMethod method, IDictionary<string, string> headers, T data)
{
string result;
var uri = BaseAddress + requestUri;
if (headers != null)
{
foreach (var key in headers.Keys)
{
Headers.Add(key, headers[key]);
}
}
EnsureRequest(method);
switch (method.Method)
{
case "GET":
result = DownloadString(uri);
break;
case "POST":
if (data != null)
{
var payload = JsonConvert.SerializeObject(data);
result = UploadString(uri, method.Method, payload);
}
else
{
result = UploadString(uri, method.Method);
}
break;
default:
throw new NotSupportedException(string.Format("Method {0} is not supported", method.Method));
}
return JObject.Parse(result);
}
private void EnsureRequest(HttpMethod method)
{
var mapping = new Dictionary<JsonFormatType, string>();
mapping[JsonFormatType.Verbose] = "application/json;odata=verbose";
mapping[JsonFormatType.MinimalMetadata] = "application/json; odata=minimalmetadata";
mapping[JsonFormatType.NoMetadata] = "application/json; odata=nometadata";
Headers.Add(HttpRequestHeader.ContentType, mapping[FormatType]);
Headers.Add(HttpRequestHeader.Accept, mapping[FormatType]);
if (method == HttpMethod.Post)
{
Headers.Add("X-RequestDigest", RequestFormDigest());
}
}
private string RequestFormDigest()
{
var endpointUrl = string.Format("{0}/_api/contextinfo", BaseAddress);
var result = UploadString(endpointUrl, "Post");
var contentJson = JObject.Parse(result);
return contentJson["FormDigestValue"].ToString();
}
public JsonFormatType FormatType { get; set; }
}
public enum JsonFormatType
{
Verbose,
MinimalMetadata,
NoMetadata
}
}
SPRestClient.cs

New kendo sheduler events don't call controller methods

I'm trying to add a new scheduler event to database. It's added to the scheduler datasource and its visible in scheduler, but isn't call controller Create method.
Edit or delete newly added event also don't call controller methods, but change datasource.
Already exists events work well.
Model:
public class ResourceSchedulerModel : ISchedulerEvent
{
public string Title { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public string Description { get; set; }
public bool IsAllDay { get; set; }
public string RecurrenceRule { get; set; }
public string RecurrenceException { get; set; }
public string EndTimezone { get; set; }
public string StartTimezone { get; set; }
public int BTS_Id { get; set; }
public ResourceSchedulerModel() { }
public ResourceSchedulerModel(BusyTimeSlot bts)
{
BTS_Id = bts.BTS_Id;
Start = bts.BTS_From;
End = bts.BTS_To;
Title = bts.BTS_Name;
Description = bts.BTS_Description;
ResId = bts.BTS_RES_Id;
}
}
Controller methods:
public virtual JsonResult Read([DataSourceRequest] DataSourceRequest request)
{
EntityWrapper ew = new EntityWrapper();
List<BusyTimeSlot> btss = ew.GetAllBusyTimeSlots();
List<ResourceSchedulerModel> sm = new List<ResourceSchedulerModel>();
foreach (BusyTimeSlot b in btss)
sm.Add(new ResourceSchedulerModel(b));
return Json(sm.ToDataSourceResult(request));
}
public virtual JsonResult Destroy([DataSourceRequest] DataSourceRequest request, ResourceSchedulerModel task)
{
if (ModelState.IsValid)
{
// delete
}
return Json(new[] { task }.ToDataSourceResult(request, ModelState));
}
public virtual JsonResult Create([DataSourceRequest] DataSourceRequest request, ResourceSchedulerModel task)
{
if (ModelState.IsValid)
{
//add
}
return Json(new[] { task }.ToDataSourceResult(request, ModelState));
}
public virtual JsonResult Update([DataSourceRequest] DataSourceRequest request, ResourceSchedulerModel task)
{
if (ModelState.IsValid)
{
// edit
}
return Json(new[] { task }.ToDataSourceResult(request, ModelState));
}
View:
#(Html.Kendo().Scheduler<SchedulerTry.Models.ResourceSchedulerModel>()
.Name("scheduler")
.Date(new DateTime(2014, 10, 11))
.MinorTickCount(1)
.Views(views =>
{
views.DayView();
views.WeekView(weekView => weekView.Selected(true));
views.MonthView();
views.AgendaView();
})
.DataSource(d => d
.Model(m => {
m.Id(f => f.BTS_Id);
m.Field(f => f.Title).DefaultValue("No title");
})
.Read("Read", "Resource")
.Create("Create", "Resource")
.Destroy("Destroy", "Resource")
.Update("Update", "Resource")
)
)
In addition, when I try close or cancel the edit window of new event I get an error
Uncaught TypeError: Cannot read property 'BTS_Id' of undefined kendo.all.min.js:11
Try updating the BTS_Id as below intialization as below:
public int bTS_Id;
public string BTS_Id
{
get
{
return bTS_Id;
}
set
{
bTS_Id = value;
}
}
My bad, I have another Create method in controller.

Unit test of ValidationContext is failing?

I am trying to unit test validations on a object with a base class. I am expecting 3 properties (Email,Phone and Skill>0) to fail validation, but the test is failing.
BASE CLASS
public abstract class Person : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string FullName { get { return string.Format("{0} {1}", FirstName, LastName); } }
public string Email { get; set; }
public string Phone { get; set; }
public DateTime DateAdded { get; set; }
public bool Active { get; set; }
}
DERIVED CLASS
public class Contractor : Person, IValidatableObject
{
public Address Address { get; set; }
public List<Skill> SkillSet { get; set; }
public DateTime? NextAvailableDate { get; set; }
public Contractor()
{
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(base.FirstName))
yield return new ValidationResult("The First Name field cannot be empty.");
if (string.IsNullOrWhiteSpace(base.LastName))
yield return new ValidationResult("The Last Name field cannot be empty.");
if (string.IsNullOrWhiteSpace(base.Email))
yield return new ValidationResult("The Email field cannot be empty.");
if (string.IsNullOrWhiteSpace(base.Phone))
yield return new ValidationResult("The Phone field cannot be empty.");
if (SkillSet.Count() < 1)
yield return new ValidationResult("A contractor must have at least one skill.");
}
TEST
[TestMethod]
public void contractor_is_missing_email_phone_skill_when_created()
{
//Arrange
Contractor contractor = new Contractor { FirstName = "abc", LastName = "def" };
//Act
ValidationContext context = new ValidationContext(contractor);
IEnumerable<ValidationResult> result = contractor.Validate(new ValidationContext(contractor));
List<ValidationResult> r = new List<ValidationResult>(result);
//Assert
Assert.IsTrue(r.Count == 3);
}
Initialise your list when you create a new contractor.
public Contractor()
{
SkillSet = new List<Skill>();
}

Entity Framework 5 + Ninject: AutoDetectChanges issue with foreign keys

I'm trying to use the Onion Pattern with Generic Repositories + Unit of work + Entity Framework 5 + Ninject with Asp.net MVC4 but I'm having a problem with EF "DetectChanges" feature and I don't understand why I need to set it to "false".
Could you please help me?
My solution is to add the following line to the constructor of FindingBugContext.cs file:
Configuration.AutoDetectChangesEnabled = false;
In the commented lines of the following code, I'll explain more clearly my isssue.
It's a very simple (and maybe stupid) code reproducing the issue I don't understand and I didn't include the code for all interfaces because I think it's pretty simple to figure it out.
This is my service class, "RobotService.cs":
public class RobotService : IRobotService
{
private readonly IRepository<Body> _repoBody;
private readonly IRepository<BodyPart> _repoBodyPart;
private readonly IRepository<Robot> _repoRobot;
private readonly IUnitOfWork _unitOfWork;
public RobotService(
IRepository<Body> repoBody,
IRepository<BodyPart> repoBodyPart,
IRepository<Robot> repoRobot,
IUnitOfWork unitOfWork)
{
_repoBody = repoBody;
_repoBodyPart = repoBodyPart;
_repoRobot = repoRobot;
_unitOfWork = unitOfWork;
}
public Robot Get(int id)
{
Robot robot = new Robot();
robot = _repoRobot.Get(id);
if (robot != null)
{
Body body = _repoBody.Get(robot.BodyId);
/* FROM NOW ON:
* robot.BodyId = 0 --> instead of 1: WHY???
* robot.Name = "Robby1"
*/
if (body != null)
{
BodyPart head = new BodyPart();
head = _repoBodyPart.Get(body.HeadId);
body.Head = head;
/* FROM NOW ON:
* body.BodyId = 0 --> instead of 1: WHY???
* body.HeadId = 0 --> instead of 1: WHY???
* body.LeftArmId = 0 --> instead of 2: WHY???
* body.RightArmId = 0 --> instead of 3: WHY???
* body.BodyName = "Body1" --> doesn't change
*/
BodyPart leftArm = new BodyPart();
leftArm = _repoBodyPart.Get(body.LeftArmId);
body.LeftArm = leftArm;
BodyPart rightArm = new BodyPart();
rightArm = _repoBodyPart.Get(body.RightArmId);
body.RightArm = rightArm;
robot.Body = body;
}
}
return robot;
}
This is my custom code inside NinjectWebCommon.cs file after installation through NuGet package "Ninject.MVC3":
private static void RegisterServices(IKernel kernel)
{
kernel.Bind(typeof(IRepository<>)).To(typeof(RepositoryBase<>)).InRequestScope();
kernel.Bind<IDbContextFactory>().To<DbContextFactory>().InRequestScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind<IRobotService>().To<RobotService>();
}
This is my DbContext class "FindingBugContext.cs":
public class FindingBugContext : DbContext
{
public FindingBugContext()
: base("FindingBugContext")
{
//Configuration.AutoDetectChangesEnabled = false; //This is the solution
}
public virtual void Commit()
{
base.SaveChanges();
}
private IDbSet<Robot> _robots;
private IDbSet<Body> _bodies;
private IDbSet<BodyPart> _bodyParts;
public virtual IDbSet<T> DbSet<T>() where T : class
{
return Set<T>();
}
public IDbSet<Robot> Robots
{
get { return _robots ?? (_robots = DbSet<Robot>()); }
}
public IDbSet<Body> Bodies
{
get { return _bodies ?? (_bodies = DbSet<Body>()); }
}
public IDbSet<BodyPart> BodyParts
{
get { return _bodyParts ?? (_bodyParts = DbSet<BodyPart>()); }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new DropCreateFindingBugWithSeedData());
modelBuilder.Configurations.Add(new RobotConfiguration());
modelBuilder.Configurations.Add(new BodyConfiguration());
modelBuilder.Configurations.Add(new BodyPartConfiguration());
}
public class DropCreateFindingBugWithSeedData : DropCreateDatabaseAlways<FindingBugContext>
{
protected override void Seed(FindingBugContext context)
{
BodyPart head = new BodyPart() { Type = PartType.Head, PartName = "Head" };
BodyPart leftArm = new BodyPart() { Type = PartType.LeftArm, PartName = "LeftArm" };
BodyPart rightArm = new BodyPart() { Type = PartType.RightArm, PartName = "RightArm" };
Body body = new Body() { BodyName = "Body1", Head = head, HeadId = 1, LeftArm = leftArm, LeftArmId = 2, RightArm = rightArm, RightArmId = 3 };
Robot robot = new Robot() { Name = "Robby1", BodyId = 1, Body = body };
context.Robots.Add(robot);
}
}
public class RobotConfiguration : EntityTypeConfiguration<Robot>
{
public RobotConfiguration()
{
ToTable("Robots");
HasKey(r => r.RobotId)
.Property(r => r.RobotId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(r => r.Body)
.WithMany()
.HasForeignKey(r => r.BodyId);
}
}
public class BodyConfiguration : EntityTypeConfiguration<Body>
{
public BodyConfiguration()
{
ToTable("Bodies");
HasKey(b => b.BodyId)
.Property(b => b.BodyId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(b => b.Head)
.WithMany()
.HasForeignKey(b => b.HeadId)
.WillCascadeOnDelete(false);
HasRequired(b => b.LeftArm)
.WithMany()
.HasForeignKey(b => b.LeftArmId)
.WillCascadeOnDelete(false);
HasRequired(b => b.RightArm)
.WithMany()
.HasForeignKey(b => b.RightArmId)
.WillCascadeOnDelete(false);
}
}
public class BodyPartConfiguration : EntityTypeConfiguration<BodyPart>
{
public BodyPartConfiguration()
{
ToTable("BodyParts");
HasKey(b => b.BodyPartId)
.Property(b => b.BodyPartId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
}
These are my Entities:
public class Robot
{
public Robot() { Body = new Body(); }
public int RobotId { get; set; }
public string Name { get; set; }
public int BodyId { get; set; }
public Body Body { get; set; }
}
public class Body
{
public Body()
{
Head = new BodyPart();
LeftArm = new BodyPart();
RightArm = new BodyPart();
}
public int BodyId { get; set; }
public string BodyName { get; set; }
public int HeadId { get; set; }
public BodyPart Head { get; set; }
public int LeftArmId { get; set; }
public BodyPart LeftArm { get; set; }
public int RightArmId { get; set; }
public BodyPart RightArm { get; set; }
}
public class BodyPart
{
public int BodyPartId { get; set; }
public PartType Type { get; set; }
public string PartName { get; set; }
}
public enum PartType
{
Head,
LeftArm,
RightArm
}
This is the Unit of Work code:
public class UnitOfWork : IUnitOfWork
{
private readonly IDbContextFactory _dbContextFactory;
private FindingBugContext _context;
public UnitOfWork(IDbContextFactory dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
protected FindingBugContext FindingBugContext
{
get { return _context ?? (_context = _dbContextFactory.Get()); }
}
public void Commit()
{
FindingBugContext.Commit();
}
}
This is the Generic Repository code:
public class RepositoryBase<T> : IRepository<T> where T : class
{
private FindingBugContext _context;
private readonly IDbSet<T> _dbSet;
public RepositoryBase(IDbContextFactory dbContextFactory)
{
DbContextFactory = dbContextFactory;
_dbSet = FindingBugContext.Set<T>();
}
public IDbContextFactory DbContextFactory
{
get;
private set;
}
public FindingBugContext FindingBugContext
{
get { return _context ?? (_context = DbContextFactory.Get()); }
}
//Read
public T Get(int id)
{
return _dbSet.Find(id);
}
}
This is my ContextFactory code:
public class DbContextFactory : Disposable, IDbContextFactory
{
private FindingBugContext _context;
public FindingBugContext Get()
{
if (_context == null)
{
_context = new FindingBugContext();
return _context;
}
else
{
return _context;
}
}
public void Dispose()
{
if (_context != null)
{
_context.Dispose();
}
}
}