Don't get data to my list from my model (Table) - json

I'm trying to get the data from my database in SQL, I use entity. This is my function in my controller:
public JsonResult getProductCategories()
{
List<Categories> category = new List<Categories>();
using (MasterDetailsEntities1 dc = new MasterDetailsEntities1())
{
category = dc.Categories.OrderBy(a => a.CategoryName).ToList(); -- I make a break here and doesn't pass anything and I have data in my table Categories.
}
return new JsonResult { Data = category, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
What I want to do is pass all the data from my model or table Categories to the list but it does pass me anything, i'm new doing this I don't know if I'm doing the right way.
This is my model:
public partial class Categories
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Categories()
{
this.Products = new HashSet<Products>();
}
public int CategoryID { get; set; }
public string CategoryName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Products> Products { get; set; }
}
}

So, your problem here is not related to JSON result..it's for db not returning data from the table.
Please check your connection string for the MasterDetailsEntities1 context.
Do you get data from any other table using the same context? Please check.
Nothing looks improper here.

Related

Include fields from related tables

I'm using EF with DotNet Core 2.1 in my application. The application deals with data in multiple related and with FK interconnected tables.
I need audit logging data only changes to one table. However, my problem is, the table I need to audit log has quite some FK and for each of these I would like to log the FK itself and a field from the related table.
Let me try illustrate what I'm about - let's suppose this is my model:
public class Blog {
public int Id { get; set; }
public string Name { get; set; }
public string Url { get; set; }
[InverseProperty ("Blog")]
public ICollection<Post> Posts { get; set; }
public Blog() {
Posts = new Collection<Post> ();
}
}
...
[AuditInclude]
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[Required]
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
As said, I'd like audit logging only changes to one entity - let's say it is a Post - here is an audit class:
public class Audit_Post : IAudit {
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public string Blog { get; set; } // <- I need populating this from Blog.Name
[StringLength (64)]
public string AuditUsername { get; set; }
public DateTime AuditDt { get; set; }
public string AuditAction { get; set; }
public Audit_Manufacturer () { }
}
And this is how I set up audit logging in my startup.cs -> ConfigureService():
...
Audit.Core.Configuration.Setup ()
.UseEntityFramework (ef => ef
.AuditTypeExplicitMapper (m => m
.Map<Post, Audit_Post> ((d, al) => {
al.Blog = d.Blog?.Name; // !! This doesn't work
})
.AuditEntityAction<IAudit> ((evt, entry, auditEntity) => {
Object val;
var gotVal = evt.CustomFields.TryGetValue ("AuditUsername", out val);
string username = null;
if (gotVal && val is string)
username = val as string;
else
username = "<anonymous>";
auditEntity.AuditDt = DateTime.UtcNow;
auditEntity.AuditUsername = username;
auditEntity.AuditAction = entry.Action;
})
)
);
question: Is it possible at all to get and audit log data from dependant table (one-to-many) relation?
Beside the mentioned issue, I'm also bumped in an off-topic one, which is - if I forget updating the DB with the migration for initialising the Audit_Posts table and I'm doing operations on Posts table, the data get stored to the later even if audit logs fail to get written (UnitOfWork save exception). Is there a flag to AuditDbContext that would make it run in the same transaction as the original query?
As #thepirat000 pointed out, it is enough to guarantee all related items being present in the DbContext memory. This means:
INSERT Just before doing context.Posts.Add(item) do a query to all related items such as context.Blogs.Find(item.BlogId).
UPDATE When retrieving the Post, do it with .Include(d => d.Blog) + other related items.
DELETE When retrieving the Post, do it with .Include(d => d.Blog) + other related items.
An additional important thing that was causing me troubles is the layout of my Audit table. The issue was I reused the same property name in Audit table with a different type - in the original table the property Blog was a relationship property, whiles in the audit table it was a string. This caused errors in conversion from one to the other model.
[AuditInclude]
public class Post
{
...
[Required]
public int BlogId { get; set; }
public Blog Blog { get; set; }
...
}
Just rename it to something else like:
public class Audit_Post
{
...
public int BlogId { get; set; }
public string BlogName { get; set; }
...
}
...
// and in startup.cs use ...
...
.Map<Post, Audit_Post> ((d, al) => {
al.BlogName = d.Blog?.Name;
})
...
Regarding the 2nd issue - running audit inside transactions. I decided not to use it for now. I'll get covered the described case with tests.
Maybe a suggestion for future development of the package - it would be nice to have mentioned cases covered easily - I mean, transitive properties.
Your code should work fine if you Include the Blog property when retrieving the Post entity, for example:
using (var context = new BlogsContext())
{
var post = context.Posts
.Include(p => p.Blog) // Important, otherwise post.Blog will be NULL
.First();
post.Content += " adding this";
context.SaveChanges();
}
If you can't include the Blog entity because any reason, you could do the query on the mapping, but you will need to use a lower level overload on the Map method, like this:
.Map<Post, PostAudit>((ev, entry, postAudit) =>
{
var entryEf = entry.GetEntry();
var post = entryEf.Entity as Post;
var dbContext = entryEf.Context as BlogsContext;
// Get the blog related
var blog = dbContext.Blogs.FirstOrDefault(b => b.Id == post.BlogId);
postAudit.Blog = blog?.Name;
})
Regarding the other question, currently there is no built-in mechanism to rollback your database changes when the audit saving fails, but maybe you try with the AuditDbContext overrides

jQuery Bootgrid - Ajax Sort Parameter with ASP.NET MVC Actions

I managed to create an ApiController retrieving data from my repositories, and populating a grid in my view through Bootgrid with Ajax. This is an example of request data sent to Api's Action, given by their Docs here (look for POST Body Request tab):
current=1&rowCount=10&sort[sender]=asc&searchPhrase=&id=b0df282a-0d67-40e5-8558-c9e93b7befed
Here is an example URL:
http://localhost/api/SomeController?current=1&rowCount=10&sort%5BName%5D=asc&searchPhrase=&id=b0df282a-0d67-40e5-8558-c9e93b7befed
I created two Helper classes to handle data I must return as response, and sort data (as it's an array):
public class SortData
{
public string Field { get; set; } // FIeld Name
public string Type { get; set; } // ASC or DESC
}
public class BootgridResponseData<T> where T: class
{
public int current { get; set; } // current page
public int rowCount { get; set; } // rows per page
public IEnumerable<T> rows { get; set; } // items
public int total { get; set; } // total rows for whole query
}
Therefore, my action is as follow:
public BootgridResponseData<SomeViewModel> Get(int current, int rowCount, List<SortData> sort, string searchPhrase, string id)
{
// get items and return a bootgrid response data with them...
}
The method is invoked and all parameters come with data properly, except sort, which is always null.
What kind of parameter should I expect for this? I also tried to put object but it comes null anyway.
After learning a bit more, I saw Bootgrid has a requestHandler setting which allows you to manipulate data sent to server.
I did it in my javascript like this:
var grid = $("#my-grid").bootgrid({
ajax: true,
rowCount: 10,
ajaxSettings: {
method: "POST",
cache: true
},
requestHandler: function (request) {
// Scan the original sort object from Bootgrid...
// and populate an array of "SortData"...
request.sortItems = [];
if (request.sort == null)
return request;
for (var property in request.sort) {
if (request.sort.hasOwnProperty(property)) {
request.sortItems.push({ Field: property, Type: request.sort[property] });
}
}
return request;
},
url: "/api/FooApi"
});
Then I created my post action in API like this:
public class FooApiController : ApiController
{
[HttpPost]
public BootgridResponseData<FooModel> Get(BootgridRequestData model)
{
// This would come from some data store, using the request params...
// I use PagedList to make pagination easier...
IPagedList<FooModel> itemsPaged = store.GetPagedFoo();
// Then return the response with data...
return new BootgridResponseData<FooModel>()
{
current = model.current,
rowCount = model.rowCount,
rows = itemsPaged,
total = itemsPaged.TotalItemCount
};
}
}
The BootgridResponseData has already been shown in my question. I just added a BootgridRequestData which the following structure:
public class BootgridRequestData
{
public int current { get; set; }
public int rowCount { get; set; }
public string searchPhrase { get; set; }
public IEnumerable<SortData> sortItems { get; set; }
}
Then I could even use my original SortData helper class:
public class SortData
{
public string Field { get; set; } // FIeld Name
public string Type { get; set; } // ASC or DESC
}
I've struggled with this as well. You are overthinking it. It's nice to create simple models to handle the post call from jquery-bootgrid, but you can also just use simple parameters in the post method. As for the sort, it looks like a Key-Value pair, but that doesn't serialize properly.
I ended up trying a Dictionary object and it works.
Here is my signature:
[HttpPost]
public async Task<ActionResult> GetActiveDogs(int? current, int? rowCount,
Dictionary<string, string> sort, string searchPhrase = null)
I had the same problem passing the sort options to my webservice. The Dictionary object did not solve my problem either.
To solve it, I created a class holding string properties for each field I wanted to pass through the bootgrid sort options. See code excerpt
class TestSort
{
public string field1 { get; set; }
public string field2 { get; set; }
...
}
I use this class as the sort options parameter in my webservice. All fields in this class that are referred to by the bootgrid options are set to "ASC" or "DESC". The others remain null.
I added an 'orderBy' property to this class that returns an orderby clause for the fields that are not null.
Approch1.
consider you have table with columns "col1, col2, col3, ...".
you can use:
public ActionType Get(int current, int rowCount, Sort sort, string searchPhrase) {
//sort.col1 == 'asc' (consider sorted by col1 in ascending order)
}
public class Sort
{
public string col1 { get; set; }
public string col2 { get; set; }
public string col3 { get; set; }
//... other columns
}
Approach 2.
You can use remove you parameters and parse request data manually. i used post here instead of get.
[HttpPost]
public object Post(){
string jsonContent = Request.Content.ReadAsStringAsync().Result;
Dictionary<string, string> keyvalues = new Dictionary<string, string>();
string[] keyvalue_strings = jsonContent.Split('&');
string sort_column = "";
string sort_direction = "";
for (var i = 0; i< keyvalue_strings.Length; i++)
{
var a = keyvalue_strings[i].Split('=');
a[0] = a[0].Replace("%5B", "[").Replace("%5D", "]");
keyvalues.Add(a[0], (a[1]));
if (a[0].Contains("sort"))
{
sort_column = a[0].Replace("sort[", "").Replace("]", "");
sort_direction = a[1];
}
}
//now you have keyvalues, sort_column, sort_direction.
//...
}

Entity null - Linq to SQL

I need some help...
I have my entity that i have create manually.
public class Project()
{
public Project Data {get;set;}
public string ProjectID { get; set; }
public string AreaID { get; set; }
public string Country { get; set; }
}
Where property "Project" is the the table created by SQLmetal.
I have also created my class, with SQLmetal, wish there have there own entity.
Now i trying to parse between them in the constructor like:
public Project()
{
ProjectID = Data.ProjectID;
AreaID = Data.AreaID;
Country = Data.Country;
}
But when I use
projects.Select(p => new Project { Data = p });
the Data property in the constructor is null.
Any idea why? and how will I solve this the better way?
Yes, because the initializer
var x = new Project { Data = p };
is equivalent to
var x = new Project();
x.Data = p;
The Data property is set AFTER the constructor.
You can solve it by creating a constructor that takes Data as a parameter
public Project(Data data)
{
this.Data = Data;
ProjectID = Data.ProjectID;
AreaID = Data.AreaID;
Country = Data.Country;
}
and call the constructor
projects.Select(p => new Project(p));

JSON + LazyLoad

Guys, I'm havin a problem with this...
My User class has a property UserType userType like below:
public class User
{
public virtual int Id { get; set; }
public virtual string User { get; set; }
public virtual string Name { get; set; }
public virtual UserType userType { get; set; }
}
I can't return a JSON, like this...
[HttpGet]
public JsonResult JSONUsers(string q)
{
IEnumerable<User> model = dataServ.Users.GetUsers( q );
return this.Json( new { Result = model }, JsonRequestBehavior.AllowGet );
}
I'm getting an error:
A circular reference was detected
while serializing an object of type
'System.Reflection.RuntimeModule'.
The reason I'm getting this error is the Lazy-Load (at least that's what I understood), and to poorly solve it, I did:
public JsonResult JSON(string q)
{
List<User> model = new List<User>();
IEnumerable<User> users= dataServ.Users.Getusers( q );
foreach (var item in users)
{
User user = new User
{
Id = item.Id,
Name = item.Name
};
model.Add( user );
};
return this.Json( new { Result = model }, JsonRequestBehavior.AllowGet );
}
I don't think this is a good solution. In this case I only need de "Id" and "Name" properties, but what if I need all properties? Will I have to copy one by one?
Can Anybody tell me if there is a better solution?
Thanks,
Thiago
Ayende wrote a great series of blog posts about this problem.
But to summarize: USE VIEW MODELS => and by the way that's the solution to more than half of the questions on StackOverflow about ASP.NET MVC that I am answering.

Entity Framework 4.1, MVC3 JsonResult and Circular References

I'm trying to learn Entity Framework Code First development with ASP.NET MVC3.
Let's say I have a simple data Model for an Auction and Bids and I'd like to query all the Auctions and their Bids.
I have turned off LazyLoadingEnabled and ProxyCreationEnabled.
Here is the code I have:
public class MiCoreDb2Context : DbContext
{
public MiCoreDb2Context()
: base()
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Auction> Auctions { get; set; }
public DbSet<Bid> Bids { get; set; }
}
public class Auction
{
public int AuctionId { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
}
public class Bid
{
public long BidId { get; set; }
public int AuctionId { get; set; }
[ForeignKeyAttribute("AuctionId")]
public virtual Auction Auction { get; set; }
}
public JsonResult Thing()
{
List<Auction> auctions;
using (var db = new MiCoreDb2Context())
{
var auctions = (from a in db.Auctions.Include("Bids") select a).ToList();
}
return Json(auctions, JsonRequestBehavior.AllowGet);
}
When I load the page, a circular reference occurs. How will I get around this?
When I load the page, a circular reference occurs. How will I get around this?
By using view models (and by the way that's the answer to any question you might have concerning ASP.NET MVC :-)). Ayende Rahien has an excellent series of blog posts on this topic.
Conclusion: absolutely always pass/take view models to/from a view. Absolutely never pass/take models (EF, domain, ...) to/from a view. Once this fundamental rule is being respected you will find out that everything works.
I solved this problem by doing a projection in the Linq to Entities query. This will create anonymous types which can be serialized to json without any circular reference issues.
var result =
from Item in dbContext.SomeEntityCollection
where SomePredicate
select new { Property1 = Item.Property1, Property2 = Item.Property2 };
Return Json(result, JsonRequestBehavior.AllowGet);
BOb