Why is my Viewmodel that I am returning to my POST method in MVC returning a null ViewModel - asp.net-core

I really thought I understood what I was doing, but here goes. I decided I need more properties in my View so I created a ViewModel called ViewDataset and replaced the original Dataset model in the View. On my HttpGet TestDataset the View Model is populated correctly with data from the Viewmodel:
My original dataset that was working is below:
public class Dataset
{
public int Dataset_ID { get; set; }
public int Category_ID { get; set; }
public string Provider { get; set; }
public string Name { get; set; }
public string Institution { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public bool Domestic { get; set; }
public bool International { get; set; }
public bool Includes_PHI { get; set; }
public bool PHI_Limited { get; set; }
public bool Includes_PIL { get; set; }
public bool PIL_Limited { get; set; }
public bool Citation_Requirements { get; set; }
public string Citation_Comments { get; set; }
public Nullable<System.DateTime> Availability_Beg_DT { get; set; }
public Nullable<System.DateTime> Availability_End_DT { get; set; }
public bool Subscription_Renewal { get; set; }
public Nullable<System.DateTime> Subscription_Renewal_DT { get; set; }
public bool Retention_Expiry { get; set; }
public Nullable<System.DateTime> Retention_Expiry_DT { get; set; }
public bool Data_Destruction { get; set; }
public Nullable<System.DateTime> Data_Destruction_DT { get; set; }
public string Data_Destruction_Instructions { get; set; }
public Nullable<int> Contract_ID { get; set; }
public bool Draft_Status { get; set; }
public bool Admin_Only { get; set; }
public string Dataset_Alias { get; set; }
public bool Loc_Amazon { get; set; }
public bool Loc_IT_Research { get; set; }
public bool Loc_Research_Proj { get; set; }
public bool Loc_Secure_Data { get; set; }
public bool Loc_Mercury { get; set; }
public bool Loc_Research_CC { get; set; }
public bool Loc_Research_VM { get; set; }
public bool Loc_External { get; set; }
public bool Access_Url { get; set; }
public bool Access_Download_App { get; set; }
public bool Access_Lab_Terminal { get; set; }
public bool Access_Email_Req { get; set; }
public bool Access_File_Download { get; set; }
public bool Access_Other { get; set; }
public string Location_Notes { get; set; }
public string Access_Notes { get; set; }
public Nullable<System.DateTime> Date_Added { get; set; }
public Nullable<System.DateTime> Date_Modified { get; set; }
public string Added_By_User { get; set; }
public string Updated_By_User { get; set; }
public bool External_Collaborators
{
get; set;
}
}
Here is my new ViewDataset (viewmodel)
public class ViewDataset
{
public Dataset dataset;
public List<SelectListItem> categories_list;
}
Here is my is a sample of my view ... couple of lines, but the view is populated correctly with data from the ViewDataset model
#model ResearchDataInventoryWeb.Models.ViewDataset
<td>
#Html.TextBoxFor(model => model.dataset.Institution, new { placeholder = "<Institution>", #class = "input-box" })
</td>
<td>
#Html.TextBoxFor(model => model.dataset.Name, new { placeholder = "<Dataset Name>", #class = "input-box" })
</td>
#Html.CheckBoxFor(model => model.dataset.Domestic) <span class="display-checkbox">Domestic</span>
#Html.CheckBoxFor(model => model.dataset.International) <span class="display-checkbox">International</span>
#Html.CheckBoxFor(model => model.dataset.Includes_PHI) <span class="display-checkbox">Includes PHI </span>
#Html.CheckBoxFor(model => model.dataset.Includes_PIL) <span class="display-checkbox">Includes PII </span><br />
#Html.CheckBoxFor(model => model.dataset.External_Collaborators) <span class="display-checkbox">External Collaborators Allowed (Sharable)</span>
#Html.CheckBoxFor(model => model.dataset.Citation_Requirements) <span class="display-checkbox">Citation Requirements</span>
<input type="submit" value="TestPost" />
Here is my HttpPost TestDataset : The model Viewdataset (dataset, categories_list) properties that I am passing back from the view are NULL, am I missing something?
[HttpPost]
public ActionResult TestDataset(ViewDataset viewdataset)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:4251/");
//HTTP POST
viewdataset.dataset.Category_ID = 2;
viewdataset.dataset.Dataset_ID = 1;
viewdataset.dataset.Location = "Fingers Crossed";
viewdataset.dataset.Institution = "Not Null";
var dataset = viewdataset.dataset;
// var postTask = client.PostAsJsonAsync<Dataset>("api/datasets/1", dataset);
var postTask = client.PostAsJsonAsync("api/datasets/1", dataset);
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
}
ModelState.AddModelError(string.Empty, "Server Error. Please contact administrator.");
return View(viewdataset);
}
Here if my HttpGet TestDataset in case you need to see it
public async Task<ActionResult> TestDataset()
{
//Hosted web API REST Service base url
string Baseurl = "http://localhost:4251/";
List<Dataset> dataset = new List<Dataset>();
List<Category> categories = new List<Category>();
var parm = "1";
var returned = new Dataset();
var ViewDataset = new ViewDataset();
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("api/Datasets/" + parm);
HttpResponseMessage Res2 = await client.GetAsync("api/Categories");
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var EmpResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
returned = JsonConvert.DeserializeObject<Dataset>(EmpResponse);
}
if (Res2.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var CatResp = Res2.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
categories = JsonConvert.DeserializeObject<List<Category>>(CatResp);
}
// Create the Categories Select List
var categoryList = new List<SelectListItem>();
foreach (Category c in categories)
{
categoryList.Add(new SelectListItem
{
Value = c.Category_ID.ToString(),
Text = c.Description,
Selected = (c.Category_ID == returned.Category_ID ? true : false)
});
}
ViewDataset.dataset = returned;
ViewDataset.categories_list = categoryList;
//returned.External_Collaborators = returned.External_Collaborators == null || false ? false : true;
//returning the employee list to view
return View(ViewDataset);
}
}

Try to change
public class ViewDataset
{
public Dataset dataset;
public List<SelectListItem> categories_list;
}
to
public class ViewDataset
{
public Dataset dataset { get; set; }
public List<SelectListItem> categories_list { get; set; }
}

Related

Eager loading use Include got error in .Net

I'd like to display the 'ApplicationRole' Name with the 'ApplicationUser' data together using Eager loading.
here is my ApplicationUser
public class ApplicationUser : IdentityUser
{
public string? Name { get; set; }
public string? Department { get; set; }
public bool IsDeleted { get; set; }
public string RoleId { get; set; }
public virtual ApplicationRole Role { get; set; } = new ApplicationRole();
}
public class ApplicationUserDTO
{
public string Id { get; set; }
public int UserId { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
// Foreign Key
public string RoleId { get; set; }
public virtual ApplicationRoleDTO Role { get; set; } = new ApplicationRoleDTO();
}
here is ApplicationRole
[NotMapped]
public class ApplicationRole : IdentityRole
{
public string Id { get; set; }
public string Name { get; set; }
//[InverseProperty(nameof(ApplicationUser.Role))]
public virtual ICollection<ApplicationUser> Users { get; set; } = new List<ApplicationUser>();
}
public class ApplicationRoleDTO
{
public string Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ApplicationUserDTO> Users { get; set; } = new List<ApplicationUserDTO>();
}
and here is the UserRepository
public async Task<IEnumerable<ApplicationUser>> GetAllUsersAsync()
{
//return await _dbContext.User.Where(u => u.IsDeleted == false).ToListAsync();
return await _dbContext.User.Include(u => u.Role).Where(u => u.IsDeleted == false).ToListAsync();
}
public async Task<ApplicationUser> GetUserAsync(string id)
{
return await _dbContext.User.Where(u => u.Id == id).Where(u => u.IsDeleted == false).FirstOrDefaultAsync();
}
But when I use the 'Include' keyword it show this error
When I try to use the string "Role", it also show error
public async Task<IEnumerable<ApplicationUser>> GetAllUsersAsync()
{
return await _dbContext.User.Include("Role").Where(u => u.IsDeleted == false).ToListAsync();
}
Here is the error show

Querying DTOs based on EF using Odata

I have an ASP.NET Core Web API setup with a SQL Server database and an EF data model.
Versions:
EF: Microsoft.EntityFrameworkCore 5.0.0-preview.7.20365.15
OData: Microsoft.AspNetCore.OData 7.4.1
.Net Core 3.1
The question is: I can't use filter, select in expand (nested expand). Example URLs which OData does not add filters to the where condition of SQL query which is seen on SQL Server Profiler:
https://localhost:44327/odata/clientcontract?$expand=ContactsInfo($filter=value eq '100003265')
https://localhost:44327/odata/clientcontract?$expand=Documents($filter=documentnnumber eq '100003265')
These are my database-first entity models:
public partial class ClientRef
{
public ClientRef()
{
Addresses = new HashSet<Address>();
Assets = new HashSet<Asset>();
ClientContactInfoComps = new HashSet<ClientContactInfoComp>();
ClientRelationCompClient1Navigations = new HashSet<ClientRelationComp>();
ClientRelationCompClient2Navigations = new HashSet<ClientRelationComp>();
Clients = new HashSet<Client>();
CommentComps = new HashSet<CommentComp>();
Companies = new HashSet<Company>();
Documents = new HashSet<Document>();
PhysicalPeople = new HashSet<PhysicalPerson>();
}
[Column("Inn")]
public int Id { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Asset> Assets { get; set; }
public virtual ICollection<ClientContactInfoComp> ClientContactInfoComps { get; set; }
public virtual ICollection<ClientRelationComp> ClientRelationCompClient1Navigations { get; set; }
public virtual ICollection<ClientRelationComp> ClientRelationCompClient2Navigations { get; set; }
public virtual ICollection<Client> Clients { get; set; }
public virtual ICollection<CommentComp> CommentComps { get; set; }
public virtual ICollection<Company> Companies { get; set; }
public virtual ICollection<Document> Documents { get; set; }
public virtual ICollection<PhysicalPerson> PhysicalPeople { get; set; }
}
public partial class Document
{
public int Id { get; set; }
public string DocumentNumber { get; set; }
public int DocumentType { get; set; }
public int Inn { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
public DateTime? DocumentExpireDate { get; set; }
public virtual ClientRef InnNavigation { get; set; }
}
public partial class ClientContactInfoComp
{
public int Id { get; set; }
public int Inn { get; set; }
public int ContactInfoId { get; set; }
public int? Point { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidTo { get; set; }
[ForeignKey("ContactInfoId")]
public ContactInfo ContactInfo { get; set; }
public virtual ClientRef InnNavigation { get; set; }
}
public partial class ContactInfo
{
public ContactInfo()
{
CallHistories = new HashSet<CallHistory>();
ClientContactInfoComps = new HashSet<ClientContactInfoComp>();
CommentComps = new HashSet<CommentComp>();
}
public int Id { get; set; }
public int? Type { get; set; }
public string Value { get; set; }
public virtual ICollection<CallHistory> CallHistories { get; set; }
public virtual ICollection<ClientContactInfoComp> ClientContactInfoComps { get; set; }
public virtual ICollection<CommentComp> CommentComps { get; set; }
}
public partial class CommentComp
{
public int Id { get; set; }
public int Inn { get; set; }
public int? CommentId { get; set; }
public int? ContactId { get; set; }
public virtual Comment Comment { get; set; }
public virtual ContactInfo Contact { get; set; }
public virtual ClientRef InnNavigation { get; set; }
}
public partial class Comment
{
public Comment()
{
CommentComps = new HashSet<CommentComp>();
}
public int Id { get; set; }
public string Text { get; set; }
public DateTime? CreateTimestamp { get; set; }
public string Creator { get; set; }
public virtual ICollection<CommentComp> CommentComps { get; set; }
}
These are my DTOs:
[DataContract]
public class ClientContract
{
public ClientContract()
{
ContactsInfo = new List<ContactInfoContract>();
Documents = new List<DocumentContract>();
Relations = new List<RelationContract>();
ClientComment = new CommentContract();
}
[DataMember(Name = "INN")]
[Key]
public int INN { get; set; }
[DataMember(Name = "validfrom")]
public DateTime ValidFrom { get; set; }
[DataMember(Name = "validto")]
public DateTime ValidTo { get; set; }
[DataMember(Name = "clienttype")]
public ClientType ClientType { get; set; }
[DataMember(Name = "companyname")]
public string CompanyName { get; set; }
[DataMember(Name = "firstname")]
public string FirstName { get; set; }
[DataMember(Name = "lastname")]
public string LastName { get; set; }
[DataMember(Name = "fathername")]
public string FatherName { get; set; }
[DataMember(Name = "pinnumber")]
public string PinNumber { get; set; }
[DataMember(Name = "birthdate")]
public DateTime? BirthDate { get; set; }
[DataMember(Name = "positioncustom")]
public string PositionCustom { get; set; }
[DataMember(Name = "position")]
public int Position { get; set; }
[DataMember(Name = "monthlyincome")]
public decimal MonthlyIncome { get; set; }
[DataMember(Name = "clientcomment")]
public CommentContract ClientComment { get; set; }
[DataMember(Name = "contactsinfo")]
public List<ContactInfoContract> ContactsInfo { get; set; }
[DataMember(Name = "documents")]
[ForeignKey("Documents")]
public List<DocumentContract> Documents { get; set; }
[DataMember(Name = "relations")]
public List<RelationContract> Relations { get; set; }
}
public class DocumentContract
{
[Key]
public int Id { get; set; }
[DataMember(Name = "documentNumber")]
public string documentNumber { get; set; }
[DataMember(Name = "documentType")]
public int documentType { get; set; }
[DataMember(Name = "documentexpiredate")]
public DateTime? documentExpireDate { get; set; }
}
[DataContract]
public class ContactInfoContract
{
public ContactInfoContract()
{
ContactComment = new CommentContract();
}
[Key]
public int Id { get; set; }
[DataMember(Name = "type")]
public int Type { get; set; }
[DataMember(Name = "value")]
public string Value { get; set; }
[DataMember(Name = "contactComment")]
public CommentContract ContactComment { get; set; }
}
public class CommentContract
{
[DataMember(Name = "Text")]
public string Text { get; set; }
[DataMember(Name = "creator")]
public string Creator { get; set; }
}
In DTOs there is not relation model. For instance: in EF, there is a ClientContactInfoComp model, which connects ClientRefs and ContactInfo models, but in DTO ClientContract is directly referenced with ContactInfoContract.
Model Builder in Startup.cs
private IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<ClientContract>("ClientContract").EntityType.HasKey(x => x.INN).Name = "ClientRef";
builder.EntitySet<DocumentContract>("DocumentContract");
builder.EntitySet<ContactInfoContract>("ContactInfoContracts").EntityType.HasKey(x => x.Id);
return builder.GetEdmModel();
}
public class ClientContractController : ControllerBase
{
[EnableQuery(MaxExpansionDepth = 10)]
public IQueryable<ClientContract> Get()
{
var clientRefs = _context.ClientRefs
.Include(x => x.Clients.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.Include(x => x.PhysicalPeople.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.Include(x => x.Companies.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.Include(x => x.Documents.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.Include(x => x.ClientContactInfoComps.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now))
.ThenInclude(x => x.ContactInfo)
.Include(x => x.Assets.Where(x => x.ValidFrom < DateTime.Now && x.ValidTo > DateTime.Now));
List<ClientContract> contracts = new List<ClientContract>();
foreach (var clientRef in clientRefs)
{
ClientContract clientContract = new ClientContract() { INN = clientRef.Id };
foreach(var c in clientRef.Clients)
{
clientContract.ClientType = (ClientType) c.ClientType;
}
foreach (var pp in clientRef.PhysicalPeople)
{
clientContract.FirstName = pp.FirstName;
clientContract.LastName = pp.LastName;
}
foreach (var comp in clientRef.Companies)
{
clientContract.CompanyName = comp.CompanyName;
}
foreach (var doc in clientRef.Documents)
{
clientContract.Documents.Add(new DocumentContract()
{
documentNumber = doc.DocumentNumber,
documentExpireDate = doc.DocumentExpireDate,
documentType = doc.DocumentType
});
}
foreach (var comp in clientRef.ClientContactInfoComps)
{
clientContract.ContactsInfo.Add(new ContactInfoContract
{
Type = comp.ContactInfo.Type.Value,
Value = comp.ContactInfo.Value
});
}
contracts.Add(clientContract);
}
return contracts.AsQueryable();
}
}
Yes I getting result from following links:
https://localhost:44327/odata/clientcontract
https://localhost:44327/odata/clientcontract?$filter=Id eq 4
https://localhost:44327/odata/clientcontract?$expand=documents,contactsinfo
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();//.AddNewtonsoftJson(); ;
services.AddDbContext<DbContext>(options =>
options.UseSqlServer("connectionstring"));
services.AddOData();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
//endpoints.EnableDependencyInjection();
endpoints.Expand().Select().Filter().OrderBy().Count().MaxTop(10);
endpoints.MapODataRoute("odata", "odata", GetEdmModel());
});
}
private IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<ClientContract>("ClientContract").EntityType.HasKey(x => x.INN).Name = "ClientRef";
builder.EntitySet<DocumentContract>("DocumentContract");
builder.EntitySet<ContactInfoContract>("ContactInfoContracts").EntityType.HasKey(x => x.Id);
return builder.GetEdmModel();
}
}
You should use the property name and not the attribute Name when use OData.
OData client library relies on it's own attribute OriginalNameAttribute to gain knowledge about class/member names as server emits them. The details you can see from here.
It works when I remove [DataContract] attribute.
public class ClientContract
{
public ClientContract()
{
ContactsInfo = new List<ContactInfoContract>();
}
[Key]
public int INN { get; set; }
public string CompanyName { get; set; }
public List<ContactInfoContract> ContactsInfo { get; set; }
}
public class ContactInfoContract
{
[Key]
public int Id { get; set; }
[DataMember(Name = "type")]
public int Type { get; set; }
[DataMember(Name = "value")]
public string Value { get; set; }
}

How to avoid saving a value of property of form object when saving changes to db

In a crud asp.net core 2.2 web app, I need to avoid saving a property of form object to db. How do I do that?
I've tried using [Editable(false)] data annotation on the ListBin property to prevent saving property value to db.
[Table("supply_lists")]
public partial class SupplyLists
{
[Column("id")]
public int Id { get; set; }
[Column("category_id")]
public int CategoryId { get; set; }
[Required]
[Column("coursecode")]
[StringLength(200)]
public string Coursecode { get; set; }
[Required]
[Column("title")]
[StringLength(200)]
public string Title { get; set; }
[Required]
[Column("filename")]
[StringLength(200)]
public string Filename { get; set; }
[Column("isactive")]
public bool Isactive { get; set; }
[Column("date", TypeName = "smalldatetime")]
public DateTime Date { get; set; }
[Column("list_bin")]
public byte[] ListBin { get; set; }
[ForeignKey("CategoryId")]
[InverseProperty("SupplyLists")]
public virtual SupplyListCategory Category { get; set; }
}
[ModelMetadataType(typeof(MetaDataTypeModel))]
public partial class SupplyLists
{
}
public class MetaDataTypeModel
{
[Editable(false)]
public byte[] ListBin { get; set; }
[Display(Name = "Is Active")]
public bool Isactive { get; set; }
[Display(Name ="Course Code")]
public string Coursecode { get; set; }
[Display(Name = "Category")]
public int CategoryId { get; set; }
[DataType(DataType.Date)]
public DateTime Date { get; set; }
}
public class EditModel : PageModel
{
private readonly SupplyListCore22.Models.SupplyListsContext _context;
private readonly IHostingEnvironment _env;
public EditModel(SupplyListCore22.Models.SupplyListsContext context, IHostingEnvironment env)
{
_context = context;
_env = env;
}
[BindProperty]
public SupplyLists SupplyLists { get; set; }
[BindProperty]
public FileUpload FileUpload { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
SupplyLists = await _context.SupplyLists
.Include(s => s.Category).FirstOrDefaultAsync(m => m.Id == id);
if (SupplyLists == null)
{
return NotFound();
}
ViewData["CategoryId"] = new SelectList(_context.SupplyListCategory, "Id", "Category");
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
//if (!ModelState.IsValid)
//{
// return Page();
//}
_context.Attach(SupplyLists).State = EntityState.Modified;
await _context.SaveChangesAsync();
if (FileUpload.UploadSupplyList != null)
{
var fileUploadData = await utilities.utilities.ProcessFormFile(FileUpload.UploadSupplyList, ModelState);
if (ModelState.ErrorCount > 0)
{
ViewData["CategoryId"] = new SelectList(_context.SupplyListCategory, "Id", "Category");
return Page();
}
var sl = _context.SupplyLists.Find(SupplyLists.Id);
sl.ListBin = fileUploadData;
await _context.SaveChangesAsync();
}
return RedirectToPage("./Index");
}
It set the ListBin to null in db which is not what I wanted when saving changes (I wanted to preserve the old value of ListBin in db).

Losing data on .Where(expression) to list()

Func<View_Album_Search, bool> expressionAlbum = Al => Al.name.Contains(text) || Al.soundName.Contains(text) || Al.artist.Contains(text);
var query = view_Album_SearchRepository.Where(expressionAlbum);
var b = query.Count(); (*Count = 1440*)
(*Lose data *)
var a = query.ToList(); (*Count = 154*)
/***************************************************\
public IEnumerable<TEntity> Where(Func<TEntity, bool> predicate)
{
return _context.Set<TEntity>().Where(predicate);
}
MODEL
public partial class View_Album_Search
{
public string name { get; set; }
public decimal id_album { get; set; }
public string soundName { get; set; }
public string artist { get; set; }
public byte feature { get; set; }
public Nullable<System.DateTime> album_date { get; set; }
public Nullable<decimal> hits { get; set; }
public string artist_twitter { get; set; }
public Nullable<int> feature_order { get; set; }
public string video_url { get; set; }
}
I dont understand, if I do it on get all works fine.
Aditional information
On debug mode when he do .toList() he execute the exrpessionAlbum again
Func<View_Album_Search, bool> expressionAlbum = Al => Al.name.ToLower().Contains(text.ToLower()) || Al.artist.ToLower().Contains(text.ToLower());

How to filter audio podcasts from iTunes Search API?

By searching for podcasts with the iTunes API (http://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html) the result contains both audio and video podcasts. Is there any way to retrieve only audio podcasts from the API?
Thanks in advance :-)
From the documentation it doesn’t seem as if filtering audio and video podcasts is possible; however, you could loop through the resulting items and check whether each item is audio or video for filtering. You can do that be finding additional information from the RSS feed or by making another call to iTunes using the subscribePodcast url (see example).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web.Script.Serialization;
using System.Xml.Linq;
using System.IO;
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
//Searching for Windows Weekly
string url = "https://itunes.apple.com/search?term=Windows%20Weekly&media=podcast&attibute=audio";
string json = FetchHTML(url);
JavaScriptSerializer s = new JavaScriptSerializer();
var result = s.Deserialize(json);
var audioOnly = new List();
foreach (var item in result.Results)
{
if (!IsVideo(item.TrackId))
{
audioOnly.Add(item);
}
}
foreach (var au in audioOnly)
{
Console.WriteLine(au.TrackName);
}
Console.ReadLine();
}
static bool IsVideo(string id)
{
string req = "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/com.apple.jingle.app.finance.DirectAction/subscribePodcast?id=" + id + "&wasWarnedAboutPodcasts=true";
string xml = FetchHTML(req,true);
bool isVideo = false;
var re = XElement.Load(new StringReader(xml)).Elements("dict").Elements("dict");
bool next = false;
foreach (var e in re.Elements())
{
if (next)
{
//This is the is video value
isVideo = e.Name.LocalName.Trim().ToLower() == "true";
next = false;
break;
}
if (e.Value == "is-video")
{
next = true;
}
}
return isVideo;
}
static string FetchHTML(string url,bool doItAsITunes = false)
{
string htmlCode = "";
using (WebClient client = new WebClient())
{
if (doItAsITunes)
{
client.Headers.Add("user-agent", "iTunes/9.1.1");
}
htmlCode = client.DownloadString(url);
}
return htmlCode;
}
}
public class SearchResult
{
public SearchResult()
{
Results = new List();
}
public int ResultCount { set; get; }
public List Results { set; get; }
}
public class Item
{
public Item()
{
GenreIDs = new List();
Genres = new List();
}
public string WrapperType { set; get; }
public string Kind { set; get; }
public string ArtistID { set; get; }
public string CollectionID { set; get; }
public string TrackId { set; get; }
public string ArtistName { set; get; }
public string CollectionName { set; get; }
public string TrackName { set; get; }
public string CollectionCensoredName { set; get; }
public string TrackCensoredName { set; get; }
public string ArtistViewUrl { set; get; }
public string FeedUrl { set; get; }
public string TrackViewUrl { set; get; }
public string PreviewUrl { set; get; }
public string ArtworkUrl60 { set; get; }
public string ArtworkUrl100 { set; get; }
public float CollectionPrice { set; get; }
public float TrackPrice { set; get; }
public string CollectionExplicitness { set; get; }
public string TrackExplicitness { set; get; }
public string DiscCount { set; get; }
public string DiscNumber { set; get; }
public string TrackCount { set; get; }
public string TrackNumber { set; get; }
public string TrackTimeMillis { set; get; }
public string Country { set; get; }
public string Currency { set; get; }
public string PrimaryGenreName { set; get; }
public List GenreIDs { set; get; }
public List Genres { set; get; }
}
}
Yes. In regular serach you get everything:
https://itunes.apple.com/search?term=jack+johnson
But you can add some params to request for example (for your case)
&entity=song
So the request will be:
https://itunes.apple.com/search?term=jack+johnson&entity=song
For more look at Searching seaction in this docs