I got a small problem here. I got a course class and a User. I want to show all the Users inside a Course through the API.
the error i get,
'Object reference not set to an instance of an object.'
And this is my controller method,
var objList = _courseRepo.GetUsers(CourseId);
if (objList == null)
{
return NotFound();
}
var objToShow = new List<ViewCourseDetailsDTO>();
foreach (var obj in objList)
{
objToShow.Add(_mapper.Map<ViewCourseDetailsDTO>(obj));
}
return Ok(objToShow);
The Error i got is inside the Foreach-loop. It says that i need to create an object...
This is how my DTO classes looks like,
public class ViewCourseDetailsDTO
{
public int CourseId { get; set; }
public string CourseTitle { get; set; };
public ICollection<UserDTO>? Users { get; set; } = new List<UserDTO>();
}
And this one,
public class UserDTO
{
public string ID { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
}
Do you think i have to break out the UserDTO somehow? Is it Therefore u think ?
if you want to see my CourseRepository than its here,
public ICollection<Course> GetUsers(int courseId)
{
return _db.Course.Where(c => c.CourseId == courseId).Include(a => a.Users).ToList();
}
Would be really grateful if you could help me out here.
Wohooo I found it, damnit!
On my controller, i forgot to put in mapper here,
public CourseController(ICourseRepository courseRepo, IMapper mapper)
{
_courseRepo = courseRepo;
_mapper = mapper;
}
I had injected it correct at the top but forgot to put it inside there ^
Related
I want to return a list of links to a web page when it loads. Right now I have a model called SsoLink.cs bound to the page. I would like to return a list, so I have created another model called SsoLinks.cs that has a List. In my helper function, I keep getting "object not set to an instance of an object".
SsoLink.cs
public class SsoLink
{
public enum TypesOfLinks
{
[Display(Name="Please Select a Type")]
Types,
Collaboration,
[Display(Name="Backups & Storage")]
Backups_Storage,
Development,
[Display(Name="Cloud Services")]
Cloud_Services,
[Display(Name="Human Resources")]
Human_Resources,
Analytics
}
public string Id { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Owner { get; set; }
public string OwnerEmail { get; set; }
public string LinkDescription { get; set; }
public TypesOfLinks LinkType { get; set; }
}
SsoLinks.cs
public class SsoLinks
{
public List<SsoLink> Links {get; set;}
}
GetLinksHelper.cs
public partial class SsoLinkHelper
{
public static SsoLinks GetLinks()
{
var ssoList = new SsoLinks();
try
{
//search the index for all sso entries
var searchResponse = _client.Search<SsoLink>(s => s
.Index(_ssoLinkIndex)
.Size(500)
.Query(q => q
.MatchAll()
)
);
if (searchResponse.Documents.Count == 0)
{
return ssoList;
}
ssoList.Links.AddRange(searchResponse.Hits.Select(hit => new SsoLink() {Id = hit.Source.Id, Name = hit.Source.Name, Url = hit.Source.Url, Owner = hit.Source.Owner}));
return ssoList;
}
catch (Exception e)
{
Log.Error(e, "Web.Helpers.SsoLinkHelper.GetLinks");
return ssoList;
}
}
}
While debugging, It is failing at SsoLinks.Links.AddRange(etc). How can I add a new SsoLink to the ssoList for every item found in my query?
Edit: Here is a screenshot of the error while debugging.
The null reference exception looks like it comes from ssoList.Links being null when calling AddRange on it, so it needs to be initialized to a new instance of List<SsoLink> before calling AddRange().
Russ's answer led me down the right path, I ended up just needing to change my view to:
#model List<SharedModels.Models.SsoLink>
rather than
#model SharedModels.Models.SsoLink
and do away with the SsoLinks model.
I have the following scenario
Entity framework classes classes:
public class Block
{
public Guid Id { get; set; }
public ICollection<BlockLocation> BlockLocations { get; set; }
public BlockType Type { get; set; }
}
public class BlockLocation
{
public Guid Id { get; set; }
public Guid BlockId { get; set; }
public Block Block { get; set; }
}
And my Domain Entities look like
public class Block
{
public Block(BlockType type = BlockType.None) : this()
{
Type = type;
}
private Block() { }
public Guid Id { get; set; }
public List<BlockLocation> BlockLocations { get; set; }
public BlockType Type { get; set; }
}
public class LiveBlock : Block
{
public LiveBlock() : base(BlockType.Live) { }
}
public class UnsequencedBlock : Block
{
public UnsequencedBlock() : base(BlockType.Unsequenced) { }
}
public class BlockLocation
{
public Guid Id { get; set; }
public Guid BlockId { get; set; }
public Block Block { get; set; }
}
public enum BlockType
{
None = 0,
Live,
Unsequenced
}
And what I want to do is map from Entity Framework to a Domain entity to the child type and also preserve the reference so that I don't get a stack overflow
My mappings are
cfg.CreateMap<Data.Block, Domain.LiveBlock>();
cfg.CreateMap<Data.Block, Domain.UnsequencedBlock>();
cfg.CreateMap<Data.Block, Domain.Block>().PreserveReferences().ConstructUsing((block, context) =>
{
if (block.Type == BlockType.Live)
{
// This loops until stack overflow
return context.Mapper.Map<Domain.LiveBlock>(block);
}
if (block.Type == BlockType.Unsequenced)
{
return context.Mapper.Map<Domain.LiveBlock>(block);
}
return context.Mapper.Map<Domain.Block>(block);
});
cfg.CreateMap<Data.BlockLocation, Domain.BlockLocation>();
And I'm trying to do the following:
// This is the EF entity
var block = new Data.Block
{
Id = Guid.NewGuid(),
Type = BlockType.Live,
BlockLocations = new List<Data.BlockLocation>
{
new BlockLocation {Id = Guid.NewGuid()},
new BlockLocation {Id = Guid.NewGuid()}
}
};
block.BlockLocations[0].Block = block;
block.BlockLocations[1].Block = block;
// Trying to create a Domain entity
var domainBlock = Mapper.Map<Data.Block, Domain.Block>(block);
The result that I want to achieve is for domainBlock to be of type LiveBlock and have a list of BlockLocations which in turn have the same LiveBlock entity as their Block property
What I get is a loop in ConstructUsing, until I get stack overflow.
Now, my questions are:
Can this be achieved with AutoMapper?
If yes, can it be done with ContructUsing? I've also tried ConvertUsing, but I get the same result.
Some other approach maybe?
I know that a way of doing to would be to Ignore the BlockLocations property from Domain.Block and map them separately, but I would like to have Automapper to that automatically.
Thank you for your help.
Got it working with Lucian's help
I changed the mapper to the following
cfg.CreateMap<Data.Block, Domain.LiveBlock>().PreserveReferences();
cfg.CreateMap<Data.Block, Domain.UnsequencedBlock>().PreserveReferences();
cfg.CreateMap<Data.Block, Domain.Block>().PreserveReferences().ConstructUsing((block, context) =>
{
if (block.Type == BlockType.Live)
{
var b = new LiveBlock();
return context.Mapper.Map(block, b, context);
}
if (block.Type == BlockType.Unsequenced)
{
var unsequencedBlock = new UnsequencedBlock();
return context.Mapper.Map(block, unsequencedBlock, context);
}
return context.Mapper.Map<Domain.Block>(block);
});
cfg.CreateMap<Data.BlockLocation, Domain.BlockLocation>().PreserveReferences();
The secred was usint the Map method that takes the context as a parameter
context.Mapper.Map(block, unsequencedBlock, context);
I am having trouble saving children entities via Entity Framework / ASP Identity. It seems to be adding duplicates of everything that is added.
I have tried using a detached graph of the DrivingLicenceModel by TeamMember.DrivingLicence = null in the TeamMemberModel and then working with a detached graph by looking if there is new or old DrivingLicenceCategories but because DrivingLicence links back to TeamMember it causes TeamMember.DrivingLicenceId to be null as it cannot link back to TeamMember.
I have tried Manually adding the EntityState to the DrivingLicence and DrivingLicenceCategories but when I do that it complains that it cannot save two entities with the same primary key.
I assume this is because they way I am copying the entities but I after a lot of looking I am drawing a blank.
If there anyway to copy from TeamMemberRequestModel to TeamMemberModel and then save without the children trying to create clone copies of themselves?
Models
public class TeamMemberModel : IdentityUser
{
public virtual DrivingLicenceModel DrivingLicence { get; set; }
public void ShallowCopy(TeamMemberRequestModel src)
{
this.DateOfBirth = src.DateOfBirth;
if (src.DrivingLicence != null)
{
if (this.DrivingLicence == null)
{
this.DrivingLicence = new DrivingLicenceModel(src.DrivingLicence);
}
else
{
this.DrivingLicence.ShallowCopy(src.DrivingLicence);
}
}
}
public TeamMemberModel() { }
}
public class DrivingLicenceModel
{
[Key]
public int Id { get; set; }
[ForeignKey("TeamMember")]
public string TeamMemberId { get; set; }
[JsonIgnore]
public TeamMemberModel TeamMember { get; set; }
public virtual List<DrivingLicenceCategoryModel> DrivingLicenceCategories { get; set; }
public DrivingLicenceModel() { }
public DrivingLicenceModel(DrivingLicenceModel src)
{
this.ShallowCopy(src);
}
public void ShallowCopy(DrivingLicenceModel src)
{
this.Id = src.Id;
this.IsFullLicence = src.IsFullLicence;
this.IssueDate = src.IssueDate;
this.ExpiryDate = src.ExpiryDate;
this.IssuingAuthority = src.IssuingAuthority;
this.LicenceNumber = src.LicenceNumber;
this.DrivingLicenceCategories = src.DrivingLicenceCategories;
this.DrivingLicencePoints = src.DrivingLicencePoints;
}
}
public class DrivingLicenceCategoryModel
{
[Key]
public int Id { get; set; }
[ForeignKey("DrivingLicence")]
public int DrivingLicenceId { get; set; }
[JsonIgnore]
public DrivingLicenceModel DrivingLicence { get; set; }
}
public class TeamMemberRequestModel
{
public string Id { get; set; }
public virtual DrivingLicenceModel DrivingLicence { get; set; }
}
Context
public class TIERDBContext : IdentityDbContext<TeamMemberModel, RoleModel, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public TIERDBContext() : base("SARDBConnection") { }
public DbSet<DrivingLicenceModel> DrivingLicences { get; set; }
public DbSet<DrivingLicenceCategoryModel> DrivingLicenceCategories { get; set; }
}
Controller
public async Task<IHttpActionResult> Put(string id, TeamMemberRequestModel teamMember)
{
TeamMemberModel CurrentTeamMember = await this.TIERUserManager.FindByIdAsync(id);
CurrentTeamMember.ShallowCopy(teamMember);
await this.TIERUserManager.UpdateAsync(CurrentTeamMember);
}
you have to create clone property into context class
.
In the context clases you could to use clone method that retiran the entity you send by parameters this duplicarse any entity you pass. Sorry for my english
hope you help
After far to many hours working over this. I have come to an answer. The best way to deal with this is to simply deal with it is to add or attach all entities down the tree.
The controller now attaches all children unless they have an ID of 0, therefore new and uses add instead. Then I use this very useful extension I found here http://yassershaikh.com/c-exceptby-extension-method/ to compare lists to see added and deleted entities in the list. While I don't need the added part as the entity will already be marked to an add state as I use add() it does not harm and I want to use it later with add and delete state changing.
Controller
public async Task<IHttpActionResult> Put(string id, TeamMemberRequestModel teamMember)
{
TIERDBContext IdentityContext = (TIERDBContext)this.TIERUserManager.UserStore().Context;
foreach (DrivingLicenceCategoryModel DrivingLicenceCategory in teamMember.DrivingLicence.DrivingLicenceCategories)
{
if (DrivingLicenceCategory.Id == 0)
{
IdentityContext.DrivingLicenceCategories.Add(DrivingLicenceCategory);
}
else
{
IdentityContext.DrivingLicenceCategories.Attach(DrivingLicenceCategory);
}
}
foreach (DrivingLicencePointModel DrivingLicencePoint in teamMember.DrivingLicence.DrivingLicencePoints)
{
if (DrivingLicencePoint.Id == 0)
{
IdentityContext.DrivingLicencePoints.Add(DrivingLicencePoint);
}
else
{
IdentityContext.DrivingLicencePoints.Attach(DrivingLicencePoint);
}
}
this.DetectAddedOrRemoveAndSetEntityState(CurrentTeamMember.DrivingLicence.DrivingLicenceCategories.AsQueryable(),teamMember.DrivingLicence.DrivingLicenceCategories, IdentityContext);
this.DetectAddedOrRemoveAndSetEntityState(CurrentTeamMember.DrivingLicence.DrivingLicencePoints.AsQueryable(),teamMember.DrivingLicence.DrivingLicencePoints, IdentityContext);
CurrentTeamMember.ShallowCopy(teamMember);
await this.TIERUserManager.UpdateAsync(CurrentTeamMember);
}
I then use a generic that uses ExceptBy to work out what is added and delete from the old team member model to the new team member model.
protected void DetectAddedOrRemoveAndSetEntityState<T>(IQueryable<T> old, List<T> current, TIERDBContext context) where T : class, IHasIntID
{
List<T> OldList = old.ToList();
List<T> Added = current.ExceptBy(OldList, x => x.Id).ToList();
List<T> Deleted = OldList.ExceptBy(current, x => x.Id).ToList();
Added.ForEach(x => context.Entry(x).State = EntityState.Added);
Deleted.ForEach(x => context.Entry(x).State = EntityState.Deleted);
}
It works but it is far from great. It takes two DB queries, getting the original and updating. I just cannot think of any better way to do this.
I am building WebApi2 project to expose some RESTful service. Let's say I have following model objects.
public class Person
{
public string Name { get; set; }
public DateTime? Birthdate { get; set; }
public int Status { get; set; }
public List<Account> Accounts { get; set; }
}
public class Account
{
public decimal Amount { get; set; }
public string Code { get; set; }
public DateTime Expiry { get; set; }
}
In my service I have to go to 2 different systems to retrieve data for Person and the account info of the Person. Obviously the service implementation looks like
[HttpGet]
[Route("Person/{id:int}")]
public IHttpActionResult Get(string id)
{
var person = new Person();
person = GetPersonFromSystemA(id);
if (person.Status == 2)
{
person.Accounts = GetPersonAccountsFromSystemB(id);
}
return this.Ok(person);
}
I cannot use EF at all in this case, so OData is very tricky.
I have some requirement that I need to provide the filtering capability to the service client. The client can decide which fields of the objects to return, it also means that if the client does not like to include Accounts info of the person I should skip the second call to system B to avoid entire child object.
I did some quick search but I could not find some similar solution yet. I know I can implement my own filtering syntax, and have all custom codes to use the filtering (by having lots of if/else).
I am looking for some ideas of more elegant solution.
Entity Framework is not required for building an OData Service. If you do not use OData, you will probably have to implement your own IQueryable which is what OData does out of the box.
Some sample code.
Model classes with some added properties
public class Person
{
[Key]
public String Id { get; set; }
[Required]
public string Name { get; set; }
public DateTime? Birthdate { get; set; }
public int Status { get; set; }
public List<Account> Accounts { get; set; }
}
public class Account
{
[Key]
public String Id { get; set; }
[Required]
public decimal Amount { get; set; }
public string Code { get; set; }
public DateTime Expiry { get; set; }
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapODataServiceRoute("odata", null, GetEdmModel(), new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
config.EnsureInitialized();
}
private static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "YourNamespace";
builder.ContainerName = "DefaultContainer";
builder.EntitySet<Person>("People");
builder.EntitySet<Account>("Accounts");
var edmModel = builder.GetEdmModel();
return edmModel;
}
}
Controller method
[EnableQuery]
public class PeopleController : ODataController
{
public IHttpActionResult Get()
{
return Ok(SomeDataSource.Instance.People.AsQueryable());
}
}
You will need to include the Microsoft.AspNet.OData Nuget package.
Refer to the following for more guidance. It uses an in memory data source, but the concept is the same regardless.
http://www.odata.org/blog/how-to-use-web-api-odata-to-build-an-odata-v4-service-without-entity-framework/
When building a web api you would often want to filter your response and get only certain fields. You could do it in many ways, one of which as suggested above. Another way, you could approach it is using data shaping in your web api.
If you had a controller action as such:
public IHttpActionResult Get(string fields="all")
{
try
{
var results = _tripRepository.Get();
if (results == null)
return NotFound();
// Getting the fields is an expensive operation, so the default is all,
// in which case we will just return the results
if (!string.Equals(fields, "all", StringComparison.OrdinalIgnoreCase))
{
var shapedResults = results.Select(x => GetShapedObject(x, fields));
return Ok(shapedResults);
}
return Ok(results);
}
catch (Exception)
{
return InternalServerError();
}
}
And then your GetShapedData method can do the filtering as such:
public object GetShapedObject<TParameter>(TParameter entity, string fields)
{
if (string.IsNullOrEmpty(fields))
return entity;
Regex regex = new Regex(#"[^,()]+(\([^()]*\))?");
var requestedFields = regex.Matches(fields).Cast<Match>().Select(m => m.Value).Distinct();
ExpandoObject expando = new ExpandoObject();
foreach (var field in requestedFields)
{
if (field.Contains("("))
{
var navField = field.Substring(0, field.IndexOf('('));
IList navFieldValue = entity.GetType()
?.GetProperty(navField, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public)
?.GetValue(entity, null) as IList;
var regexMatch = Regex.Matches(field, #"\((.+?)\)");
if (regexMatch?.Count > 0)
{
var propertiesString = regexMatch[0].Value?.Replace("(", string.Empty).Replace(")", string.Empty);
if (!string.IsNullOrEmpty(propertiesString))
{
string[] navigationObjectProperties = propertiesString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<object> list = new List<object>();
foreach (var item in navFieldValue)
{
list.Add(GetShapedObject(item, navigationObjectProperties));
}
((IDictionary<string, object>)expando).Add(navField, list);
}
}
}
else
{
var value = entity.GetType()
?.GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public)
?.GetValue(entity, null);
((IDictionary<string, object>)expando).Add(field, value);
}
}
return expando;
}
Check my blog for a detailed post: https://jinishbhardwaj.wordpress.com/2016/12/03/web-api-supporting-data-shaping/
I have a class
public class Offer
{
public Int32 OfferId { get; set; }
public string OfferTitle { get; set; }
public string OfferDescription { get; set; }
}
and another class
public class OfferLocationViewModel
{
public Offer Offer { get; set; }
public Int32 InTotalBranch { get; set; }
public Int32 BusinessTotalLocation { get; set; }
}
Now in my controller I have the following
public ActionResult PresentOffers(Guid id)
{
DateTime todaysDate=Utility.getCurrentDateTime();
var rOffers=(from k in dc.GetPresentOffers(id,todaysDate)
select new OfferLocationViewModel()
{
Offer. //I dont get anything here..
}).ToList();
return PartialView();
}
Now the problem is in my controller, I can not access any property of the 'Offer' class !!
I thought, since i am creating a new OfferLocationViewModel() and this has a property of type 'Offer', I will be able to access the properties..But I can not.
Can anyone give me some idea about how to do that?
In a class initializer like new OfferLocationViewModel { ... } you can only set the immediate properties, i.e. 'Offer = new Offer()'.
You can't access the contained type's properties through the initializer.
Though you can initialize the view model's Offer to a new Offer with the given properties like this:
var rOffers = (from k in dc.GetPresentOffers(id,todaysDate)
select new OfferLocationViewModel {
Offer = new Offer {
OfferId = ...,
OfferTitle = ...,
OfferDescription = ...
}
}).ToList();