Why my Generic List is always null? - c#

I have class like this:
public class Lugar
{
[Key]
public int LugarId { get; set; }
public List<Review> Reviews { get; set; }
public int SumReviews { get; set; }
public double AverageReviews { get {
if (Reviews == null)
return 0;
else if (Reviews.Count == 0)
return 0;
else
return (double)SumReviews/(Reviews.Count); } }
}
And in my controller I have this:
[HttpPost, Authorize]
public ActionResult WriteReview(int id, FormCollection formCollection)
{
Lugar lugar = db.Lugares.Find(id);
Review review=new Review();
review.User = User.Identity.Name;
review.Rating=Convert.ToInt32(formCollection["Rating"]);
review.Texto = formCollection["Review"];
if (lugar != null)
{
if( lugar.Reviews==null)
lugar.Reviews=new List<Review>();
lugar.Reviews.Add(review);
lugar.SumReviews += review.Rating;
db.SaveChanges();
}
else
return RedirectToAction("Index");
return RedirectToAction("Index");
}
}
The problem is in the line:
if( lugar.Reviews==null)
lugar.Reviews=new List();
Everytime I execute I am getting ( lugar.Reviews==null) as true.....
Even if I already added a Review for that place, the if statement returns true.....

Try using the 'virtual' keyword where you declare your List and see if you have any more luck.

You might want to introduce a constructor in your Lugar class and instantiate the list there.
Something like this:
public void Lugar()
{
Reviews = new List<Review>();
}
Hope this helps. of not please let me know.
P.S. another thing that is not related to your specific question, but certainly an improvement is to use view model rather than FormCollection.
It will simplify your life in a major way. For example of how to use it please take a look at this successfully answered question: What is ViewModel in MVC?

You have 2 options here, lazy loading (which is enabled by putting virtual on navigation properties) This will pull down your second entitiy when you access the property in C#
or eager loading by using a .Include(/*lambda or string property name*/) statement in your query.
Personally i perfer eager loading as you have more control over when entities are loaded

Related

View expecting IEnumerable

Well im kinda new in Asp.net Mvc and im learning alone from scratch, i have a aplicattion that controls expends and earnings and what i am trying to do now is, basing on a list of earnings and expends give me the balance from a user, im having a lot of problems trying to control this and i dont know if i am doing it the right way
Here is my model:
public class Balance
{
public int BalanceId { get; set; }
public List<Expense> Despesas { get; set; }
public List<Earning> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
Soo what i did was, first trying to control when the user inserts a Earning or a row like, verifying if the User already exists on the database in the control method Create on the expenses and in the earning, if it doesnt exist he add the aplicationUserId and the expensive or the earning.
I want that the balance appears in every page, soo i added this to my Layout.cshtml
<li>#Html.Action("GetBalance", "Home")</li>
it calls the controller GetBalance:
public PartialViewResult GetBalance()
{
var userId = User.Identity.GetUserId();
var balance = db.Balance.Where(d => d.ApplicationUserId == userId);
return PartialView("_GetBalance",balance);
}
Send to the view _GetBalance the balance model:
#model <MSDiary.Models.Balance>
<p>Saldo: #GetBalance()</p>
#functions
{
HtmlString GetBalance()
{
decimal saldo = 0;
if (Model.Expense.Count != 0 || Model.Earning.Count != 0)
{
foreach (var item in Model.Despesas)
{
balance += item.EarningValue;
}
foreach (var item in Model.Rendimentos)
{
balance -= item.ExpenseValor;
}
}
return new HtmlString(balance.ToString());
}
}
What i want to know is, if there is a easyer way to do this, or what i can do to do what i want, i cant get it why my view expects something different can someone explain me what i am doing wrong?
Ps: Sorry for the long post and English, but i want to learn more :)
Firstly, the model #model <MSDiary.Models.Balance> needs to be changed to:
#model IEnumerable<MSDiary.Models.Balance>
Also, the method GetBalance should ideally be placed in a class not in GetBalance partial view. You could achieve this two ways, either through extension methods or have a Balance View Model that has the calculated balance as a property which is then passed down to your view.
As an example via an extension method:
public static class BalanceExtensions
{
public static string GetBalance(this Balance balance)
{
string displayBalance = "0:00";
// Your logic here
return displayBalance;
}
}
And then in your Partial View you can use the new HTML Helper:
#Html.GetBalance();
As an additional note I would change List to IEnumerable for expenses and earnings as it appears you are only exposing the data and not manipulating the data.
Your model would then look like:
public class Balance
{
public int BalanceId { get; set; }
public IEnumerable<Expense> Despesas { get; set; }
public IEnumerable<Earning> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
#Filipe Costa A few things here.
You should probably name your view the same thing as your method. The underscore preceding the name is fairly standard so I would suggest using that same name for the method. If the name of the method and view are the same you can simply pass in the model and not have to do the name + model signature of PartialView method. It's simpler.
Aside from that your code is fine but your .cshtml partial view should have this for the first line. That will accept the list you're passing.
#model IEnumerable<MSDiary.Models.Balance>
<h1>#Model.BalanceId</h1>
#*Do other stuff!*#

Pass linq query result to viewmodel

In my MVC web application, I have linq query that feth the record from database, I want to display that record on view using viewmodel. I have tried with following code.
[HttpGet]
public ActionResult CreatePDF()
{
RentalAgreementEntities db = new RentalAgreementEntities();
String strSession1 = "39726-10275-6027589725",strStatus = "Y",strUserType = "L";
var q = (from um in db.User_Master
join ut in db.UserType_Master on um.US_SerialNo.ToString() equals ut.UT_UserNo
join pu in db.PropertyUser_Master on ut.UT_SerialNo.ToString() equals pu.PU_UserNo
join pr in db.Property_Master on pu.PU_PropertyNo equals pr.PR_SerialNo.ToString()
where pr.PR_MakerID == strSession1
&& ut.UT_Status == strStatus
&& ut.UT_UserType == strUserType
select new
{
um.US_FirstName,
um.US_LastName
}
).AsEnumerable().Select(um => new User_Master {
US_FirstName = um.US_FirstName.ToString(),
US_LastName=um.US_LastName
}).ToList();
var myviewmodel=new viewmodelPDF()
{
lsusermaster=q.ToList();
}
return View("pdfgenerationvw",myviewmodel);
}
I also created viemodel to manage all model's for to display on a view (Here, Just one model access code).
public class viewmodelPDF
{
public List<User_Master> lsusermaster { get; set; }
}
My model class, for which I am going to fetch record from database.
public partial class User_Master
{
public string US_FirstName { get; set; }
public string US_LastName { get; set; }
public int US_SerialNo { get; set; }
}
//Other Models
Now my problem is that, In my action code , when I am trying to assign query result to the lsusermaster of viewmodel then it gives compiler error as belows.
I don't know, why this compile error is thrown, How can I assign query result to viemodel property?
Try this:
var myviewmodel=new viewmodelPDF()
{
lsusermaster=q.ToList()
};
When you are using an object initializer in C#, you can't use ; between the properties, you use it at the end of the initializer
So just remove the ; (or use a ,, as suggested), and move it to the end of the initializer block
var myviewmodel=new viewmodelPDF()
{
lsusermaster=q.ToList()
};
Using a , works even if there are no more properties... it "looks" bad, but it makes easier to add new properties should you ever need them... if the code is final, I'd not use it, but that's personal preference

Find a generic DbSet in a DbContext dynamically

I know this question has already been asked but I couldn't find an answer that satisfied me. What I am trying to do is to retrieve a particular DbSet<T> based on its type's name.
I have the following :
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyDllAssemblyName")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyCallingAssemblyName")]
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
public dynamic GetByName_SwitchTest(string name) {
switch (name) {
case "A": return A;
case "B": return B;
}
}
public dynamic GetByName_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return model.GetValue(this);
return null;
}
}
I have no trouble getting the type itself whether it is via a simple switch or reflection. I need however to return the type as a dynamic since I do not know what DbSet type it will be.
Then somewhere else in the same assembly, I use it this way :
// MyDbContext MyDbContextInstance..
var model = MyDbContextInstance.GetByName_SwitchTest("A");
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException
At this point model contains an instance of a InternalDbSet<ModelA> type. From there, any use I do with the model object I get a RunTimeBinderException :
'Microsoft.Data.Entity.Internal.InternalDbSet' does not contain a definition for 'FirstOrDefault'
Investigating on the web, I found a blog post explaining that (dixit his blog) :
the reason the call to FirstOrDefault() fails is that the type
information of model is not available at runtime. The reason it's not
available is because anonymous types are not public. When the method
is returning an instance of that anonymous type, it's returning a
System.Object which references an instance of an anonymous type - a
type whose info isn't available to the main program.
And then he points that a solution :
The solution is actually quite simple. All we have to do is open up
AssemplyInfo.cs of the ClassLibrary1 project and add the following
line to it: [assembly:InternalsVisibleTo("assembly-name")]
I did try this solution on my code but it doesn't work. For info I have an asp.net 5 solution with two assemblies running on dnx dotnet46. An app and a dll containing all my models and DbContext. All the concerned calls I do are located on the dll though.
Does this solution have any chance to work ?
Am I missing something ?
Any pointers would be greatly appreciated ?
Thanks in advance
[EDIT]
I have tried to return IQueryable<dynamic> rather than dynamic and I could do the basic query model.FirstOrDefault(); but above all I'd like to be able to filter on a field too :
var record = model.FirstOrDefault(item => item.MyProperty == true);
So how did I do it when I am not aware of <T> during compile time.
First need to get the type as DbContext.Set method returns a non-generic DbSet instance for access to entities of the given type in the context and the underlying store.
public virtual DbSet Set(Type entityType)
Note here argument is the type of entity for which a set should be returned.And set for the given entity type is the return value.
var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == <Pass your table name>);
now once I have this type
if(type != null)
{
DbSet context = context.Set(type);
}
Or a one liner would be
DbSet mySet = context.Set(Type.GetType("<Your Entity Name>"));
*Disclaimer: This response doesn't give a stricto sensu answer to my question. It is rather a different approach to resolve my own problem. I am aware this is a specific example for a given situation that will not work for everyone. I am posting this approach in the hope it helps someone but will not mark it as the answer as I am still hoping for a real solution.
To start with, let's accept the fact that the only useful information we can get out of the current code is whether a record exists or not.. Any attempt of a dynamic queries after that would give the RuntimeBinderException.
Then let's continue with another fact; DbContext.Add(object) and DbContext.Update(object) are not template based so we can use them to save our models ( Instead of db.A.Add() or db.A.Update() )
In my own situation, no more is required to work out a procedure
Define models a little differently
To start with, I need a field that is retrievable across all my models which should obviously be a way to identify a unique record.
// IModel give me a reliable common field to all my models ( Fits my DB design maybe not yours though )
interface IModel { Guid Id { get; set; } }
// ModelA inherit IModel so that I always have access to an 'Id'
class ModelA : IModel {
public Guid Id { get; set; }
public int OtherField { get; set; }
}
// ModelB inherit IModel so that I always have access to an 'Id'
class ModelB : IModel {
public Guid Id { get; set; }
public string WhateverOtherField { get; set; }
}
Re-purpose the dynamic queries a bit to do something we know works
I haven't found a way to do smart query dynamically, so instead I know I can reliably identify a record and know if it exists or not.
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
// In my case, this method help me to know the next action I need to do
// The switch/case option is not pretty but might have better performance
// than Reflection. Anyhow, this is one's choice.
public bool HasRecord_SwitchTest(string name) {
switch (name) {
case "A": return A.AsNoTracking().Any(o => o.Id == id);
case "B": return B.AsNoTracking().Any(o => o.Id == id);
}
return false;
}
// In my case, this method help me to know the next action I need to do
public bool HasRecord_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return (bool)model.GetValue(this).AsNoTracking().Any(o => o.Id == id);
return false;
}
// Update and save immediately - simplified for example
public async Task<bool> UpdateDynamic(object content)
{
EntityEntry entry = Update(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
// Insert and save immediately - simplified for example
public async Task<bool> InsertDynamic(object content)
{
EntityEntry entry = Add(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
}
A little bit of plumbing to give a sense to my situation
Next, what I needed to do with that dynamic queries was a way to replicate data from a server down to my client. ( I have omitted a big chunk of the architecture to simplify this example )
class ReplicationItem
{
public ReplicationAction Action { get; set; } // = Create, Update, Delete
public string ModelName { get; set; } // Model name
public Guid Id { get; set; } // Unique identified across whole platform
}
Connecting the bits.
Now, here's the routine that connects the bits
public async void ProcessReplicationItem(ReplicationItem replicationItem)
{
using (var db = new MyDbContext())
{
// Custom method that attempts to get remote value by Model Name and Id
// This is where I get the strongly typed object
var remoteRecord = await TryGetAsync(replicationItem.ModelName, replicationItem.Id);
bool hasRemoteRecord = remoteRecord.Content != null;
// Get to know if a local copy of this record exists.
bool hasLocalRecord = db.HasRecord_ReflectionTest(replicationItem.ModelName, replicationItem.Id);
// Ensure response is valid whether it is a successful get or error is meaningful ( ie. NotFound )
if (remoteRecord.Success || remoteRecord.ResponseCode == System.Net.HttpStatusCode.NotFound)
{
switch (replicationItem.Action)
{
case ReplicationAction.Create:
{
if (hasRemoteRecord)
{
if (hasLocalRecord)
await db.UpdateDynamic(remoteRecord.Content);
else
await db.InsertDynamic(remoteRecord.Content);
}
// else - Do nothing
break;
}
case ReplicationAction.Update:
[etc...]
}
}
}
}
// Get record from server and with 'response.Content.ReadAsAsync' type it
// already to the appropriately
public static async Task<Response> TryGetAsync(ReplicationItem item)
{
if (string.IsNullOrWhiteSpace(item.ModelName))
{
throw new ArgumentException("Missing a model name", nameof(item));
}
if (item.Id == Guid.Empty)
{
throw new ArgumentException("Missing a primary key", nameof(item));
}
// This black box, just extrapolate a uri based on model name and id
// typically "api/ModelA/{the-guid}"
string uri = GetPathFromMessage(item);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:12345");
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return new Response()
{
Content = await response.Content.ReadAsAsync(Type.GetType(item.ModelName)),
Success = true,
ResponseCode = response.StatusCode
};
}
else
{
return new Response()
{
Success = false,
ResponseCode = response.StatusCode
};
}
}
}
public class Response
{
public object Content { get; set; }
public bool Success { get; set; }
public HttpStatusCode ResponseCode { get; set; }
}
ps: I am still interested in a real answer, so please keep posting for other answer if you have a real one to share.
You could use this to get the DBSet for a specific type:
public object GetByType(DbContextcontext, Type type) {
var methode = _context.GetType().GetMethod("Set", types: Type.EmptyTypes);
if (methode == null) {
return null;
}
return methode.MakeGenericMethod(type).Invoke(_context, null);
}

ASP.NET Boilerplate - "The ObjectContext instance has been disposed" exception

I'm trying to use ASP.NET Boilerplate to handle my project and I have one serious problem.
I have 2 Models : Photo and Comment:
public class Comment : Entity<int>
{
[DataType(DataType.MultilineText)]
public string Text { get; set; }
public string Author { get; set; }
public int ItemID { get; set; }
public virtual Item Item { get; set; }
}
public class Item : Entity<int>
{
public string Title { get; set; }
public string Description { get; set; }
public ItemSourceType SourceType { get; set; }
public byte[] PhotoBytes { get; set; }
public string Url { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
Additionally I have created default OOB repository based on RepositoryBase<Item> and same for Comment.
The problem exists when I'm trying to get Item like this:
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Item item = _repoItems.Get(id.Value);
if (item == null)
{
return HttpNotFound();
}
return View(item);
}
When I'm debugging this code I can see that item has this exception in Comments property.
Am I missing something from ASP.NET Boilerplate or what?
Thanks for helping!
//Edit:
Full exception message:
{"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."}
I just stumbled across the same problem.
Seems to be that the UnitOfWork implementation creates and disposes a new DbContext for each "UnitOfWork"
So to fix that particular problem, try to inject "IUnitOfWorkManager"
and call
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
using(var uow = _unitOfWorkManager.Begin())
{
try
{
Item item = _repoItems.Get(id.Value);
if (item == null)
{
return HttpNotFound();
}
return View(item);
}
finally
{
uow.Complete()
};
}
}
if that works, consider calling "Begin()" in the constructor of your ApiController and "Complete()" in its "Dispose()" override.
Hope that helps!
No. The problem is most likely in your repository, the code of which you unfortunately have not included. If I had to guess, I'd say you're using something like the following in that repository method:
using (var context = new ApplicationContext())
{
// fetch something
}
Your Comments property is virtual, which means by default, Entity Framework will lazy load it, only actually issuing the query for that to the database once you try to access the property. However, by that point, you context has been disposed because the repository method has already finished its work, and your context was only available inside the using block.
There's a number of ways to fix this. You could eagerly load comments inside the the using block:
return context.Items.Include("Comments").Find(id);
However, that really just glosses over the problem. The best thing you can do is just not use using. Ideally, your context should be instantiated once and only once for each request. The easiest way to do that is to use a dependency injection container and add a constructor to your repository that accepts the context:
public class MyAwesomeRepository
{
private readonly ApplicationContext context;
public MyAwesomeRepository(ApplicationContext context)
{
this.context = context;
}
}
The configuration for your DI container will vary depending on which you choose to go with, but generally, you want to make sure that you bind your context class to request scope.
I had the similar issue with ASP.NET Boilerplate and figured out that this framework does all this DI magic properly only if you name your interfaces and classes in accordance with their naming conventions. You can somehow do it manually though, but you have to drill down into ABP architecture much deeper than you want.
A link posted by #ChrisPratt (see comments in his answer) says:
Naming conventions are very important here. For example you can change name of PersonAppService to MyPersonAppService or another name which contains 'PersonAppService' postfix since the IPersonAppService has this postfix. But you can not name your service as PeopleService. If you do it, it's not registered for IPersonAppService automatically (It's registered to DI framework but with self-registration, not with interface), so, you should manually register it if you want.
In my case, I had a service called ProductService implementing interface IProductAppService. It failed for me with ObjectDisposedException exception until I renamed my service to ProductAppService.
I don't think that OP still have this issue, but hopefully it will save few hours for the folks struggling with ABP like me. :-)

IValidatableObject only in some actions

I have a model that implement IValidatlableObject, and so custom error checking through Validate method.
When I create an object all is fine, but when I try to edit that object, I wan't to do that custom validation.
How can I know from wich action I'm calling the Validate method in order to no do the validation?
UPDATED:
This is mi model:
public class Ingredient : IValidatableObject
{
public int Id { get; set; }
[Required(ErrorMessage = "Required!!")]
public string Name { get; set; }
public virtual List<Product> Products { get; set; }
public Ingredient()
{
Products = new List<Product>();
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
using (var uow = new UnitOfWork())
{
var ingredient = uow.IngredientRepository.Get(i => i.Name ==Name).FirstOrDefault();
if (ingredient != null)
yield return new ValidationResult("Duplicate!!!.", new[] { "Name" });
}
}
}
}
So When I create an Ingredient I want to validate ALL (Attributes + IValidatable)
but when I edit an Ingrendient I only want to validate attributes (so I mean skip IValidatable)
Any method to know, inside the IValidatable method, from where I'm calling Validate ?
Thanks!!!
Check primary key of model - whether it is not null :)
The more "MVCish" correct way here is you actually have two classes, one for the Create method one for the edit. You can call off to a base class for any shared validation, anything then not shared wouldn't be checked here.
If you don't want to validate an object, don't call Model.IsValid (or Validate(), if you're doing it explicitly. Can't answer more than that without knowing more details about your problem.

Categories