EF select to POCO from "complex" object won't compile - entity-framework-4

This is my method that is giving the error.
public List<StatusViewModel> GetStatuses(){
using(var ctx = new AppStatusEntities()){
var result = ctx.GetLatestStatuses().Select(r => new StatusViewModel
{
r.ApplicationId,
r.ApplicationName,
r.ApplicationStatus,
r.LastRun
}).ToList();
return result;
}
}
StatusViewModel is a POCO.
public class StatusViewModel{
public Guid ApplicationId {get;set;}
public string ApplicationName {get;set;}
public string ApplicationStatus {get;set;}
public DateTime LastRun {get;set;}
}
The error message is
Cannot initialize type 'StatusViewModel' with a collection initializer because it does not implement 'System.Collections.IEnumerable'
I can only assume it has something to do with the return type of the stored procedure call being a "complex" as specified when I imported the function in the Entity Data Model. But I cannot figure out why that would matter. If I do something like ctx.ApplicationStatus.Select(r => new StatusViewModel {... where ApplicationStatus is a table and not a stored procedure call, then that code will compile without errors.

Try it this way:
var result = ctx.GetLatestStatuses().Select(r => new StatusViewModel
{
ApplicationId = r.ApplicationId,
ApplicationName = r.ApplicationName,
ApplicationStatus = r.ApplicationStatus,
LastRun = r.LastRun
}).ToList();
The difference between your stored procedure call and direct access to ObjectSet is Linq implementation. Your first example with stored procedure uses projection your application using Linq-to-Objects whereas the second example uses projection in SQL using Linq-to-entities.

Related

How to execute SQL query in a new database?

My problem is like this... I'm creating a software where you have different areas of a business and those areas have a property name ConnectionString.
So when the administrator of the system wants to know certain specific information of the area all he does is register a SQL query.
My problem is in the logic of how to do execute the query. I'm creating a new DbContext and pass the connection string but I can't find a function of DbContext to run the query.
Does someone know any function of DbContext to run a query and get the result?
If you require to execute raw query in Entity Framework Core using DbContext object you can access like
For access raw query
var students = context.Students
.FromSql("SELECT * FROM dbo.Students Where id = 1")
.ToList();
If your requirement is access or get data from a stored procedure:
var students = context.Students
.FromSql("EXECUTE dbo.GetTopperStudents")
.ToList();
if passing with parameter then
var name = new SqlParameter("name", "abc");
var students = context.Students
.FromSql("EXECUTE dbo.GetTopperStudents #name",name )
.ToList();
if execute command insert/update/delete then
var commandText = "INSERT Students (name) VALUES (#name)";
var name = new SqlParameter("#name", "Test");
context.Database.ExecuteSqlCommand(commandText, name);
if you are execute query or stored procedure in not your model or you add custom model then you can execute as query like
my studentFees Model is not any model related to database.
public class StudentFees
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public decimal Fees { get; set; }
public DateTime FeesDate { get; set; }
}
and just add your query into your DbContext OnModelCreating() method
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Query<StudentFees>();
}
now if you can access and execute your query or your stored procedure in custom model
var studentId = new System.Data.SqlClient.SqlParameter("#studentId", 1);
var studentsFees = _dbContext.Query<StudentFees>.FromSql("GetStudentFees #studentId", studentId).ToList();
in this way you can access or set your custom model also and execute query or stored procedure.
Let me know require more information.

MVC5 How to define class with only getters

I'm using VS2013 and building a simple MVC5 app with EF6 (learning C#/MVC)
The app is simple. There's a table and an SP in an SQL database that serves up sequential job nos to users via web page. This SP will also be called by a another app so the biz logic is in the SP.
The SP takes a username and returns a JobNo (PK), which is derived as Max(JobNo) + 1
I'm using DB First (as I don't currently understand enough about migrations to production with code first and nearly everything I will write has to work with existing DB's and existing SP's)
I created the model from the DB using ADO.NET and chose the table and a few SP's. This created the following:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace JobNoServer.Models
{
using System;
using System.Collections.Generic;
public partial class JobNo
{
public int JobNo1 { get; set; }
public System.DateTime CreateDateTime { get; set; }
public string UserName { get; set; }
}
}
The problem I've got is that when I call the SP (user clicks "Get new Job No"), I only want to pass the username. The CreateDateTime will be current datetime (set in SP) and JobNo is determined in SP.
I tried removing the setter in class members:
public int JobNo1 { get; }
but then I get the error "must declare a body because it is not marked abstract or extern", but can't figure out how to fix this.
The other issue I have is that the controller created this Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "JobNo1,CreateDateTime,UserName")] JobNo jobNo)
{
if (ModelState.IsValid)
{
db.JobNo.Add(jobNo);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(jobNo);
}
But I can't pass a job no, as it doesn't exist until after Create is called. When I remove the JobNo from the add method, I get a message saying there is no overload that takes zero params. When i look at the definition of Add it's some kind of generic class and the create view is saying job no is mandatory
public class DbSet<TEntity> : DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable, IInternalSetAdapter where TEntity : class
{
Could someone point me in the right direction of how to have a create method that takes just the UserName, calls the SP and gets the return value?
There are lot's of way to do this let's look at a few options:
//add a post method to the JobNo class (makes controller logic dead simple)...
public partial class JobNo
{
public int JobNo1 { get; set; }
public System.DateTime CreateDateTime { get; set; }
public string UserName { get; set; }
public Task Post(){
using (var db = new My Entities){
//any issues found here are purely Data access related!
db.JobNo.Add(UserName);
await db.SaveChangesAsync();
}
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(JobNo vm)
{
if (ModelState.IsValid)
{
await vm.Post();
RedirectToAction("Index");
}
return View(vm);
}
But first you have to solve the problem of the SP wanting more than just 1 parameter right? Did you map the SP using DB first? Is JobNo class from the SP? There seems to be an "impedance mismatch"
By the looks of it you have your db mapped successfully, however if your db relies on Stored procedures to create PK instead of it's default functionality MVC can get a little confused, which is where you're having troubles.
Have you tried mapping the stored procedure to your context. Basically right click in your edmx diagram and 'update from database'. Instead of adding your tables(which you already did), you'll find the SP under the Stored Procedures and Functions section and add it. Done. Here's the MS how to...
If you map your stored procedure you can simply do something like the following
public JobNo myCreate( DateTime createDT, string uName)
{
int jobNo = 0;
using(Context db = new Context())
{
jobNo = db.yourSP(youParams probably uName, createDT);
}
return new JobNo() { JobNo1 = jobNo, CreateDateTime = createDT, UserName = uName};
}
Then you can reference the Create function in your question and just pass it the JobNo object you just created.
If all else fails and you can't seem to map your SP, remember you could always default to exectuting SQL directly against your context, and call your SP that way.
public JobNo myCreate( DateTime createDT, string uName)
{
int jobNo = 0;
using(Context db = new Context())
{
jobNo = db.Database.SqlQuery<int>("YourProcName #param1, #param2",
new SqlParameter("param1", createDT.toString()),
new SqlParameter("param2", uName));
}
return new JobNo() { JobNo1 = jobNo, CreateDateTime = createDT, UserName = uName};
}

ASP.NET Web API Model Binding

I'm using Web API within ASP .NET MVC 4 RC, and I have a method that takes a complex object with nullable DateTime properties. I want the values of the input to be read from the query string, so I have something like this:
public class MyCriteria
{
public int? ID { get; set; }
public DateTime? Date { get; set; }
}
[HttpGet]
public IEnumerable<MyResult> Search([FromUri]MyCriteria criteria)
{
// Do stuff here.
}
This works well if I pass a standard date format in the query string such as 01/15/2012:
http://mysite/Search?ID=1&Date=01/15/2012
However, I want to specify a custom format for the DateTime (maybe MMddyyyy)... for example:
http://mysite/Search?ID=1&Date=01152012
Edit:
I've tried to apply a custom model binder, but I haven't had any luck applying it to only DateTime objects. The ModelBinderProvider I've tried looks something like this:
public class DateTimeModelBinderProvider : ModelBinderProvider
{
public override IModelBinder GetBinder(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(DateTime) || bindingContext.ModelType == typeof(DateTime?))
{
return new DateTimeModelBinder();
}
return null;
}
}
// In the Global.asax
GlobalConfiguration.Configuration.Services.Add(typeof(ModelBinderProvider), new DateTimeModelBinderProvider());
The new model binder provider is created, but GetBinder is only called once (for the complex model parameter, but not for each property within the model). This makes sense, but I would like to find a way to make it to use my DateTimeModelBinder for DateTime properties, while using the default binding for non-DateTime properties. Is there a way to override the default ModelBinder and specify how each property is bound?
Thanks!!!
Consider setting your view-model's Date property to type string
Then either write a utility function to handle the mapping between the viewmodel type and the domain-model type:
public static MyCriteria MapMyCriteriaViewModelToDomain(MyCriteriaViewModel model){
var date = Convert.ToDateTime(model.Date.Substring(0,2) + "/" model.Date.Substring(2,2) + "/" model.Date.Substring(4,2));
return new MyCriteria
{
ID = model.ID,
Date = date
};
}
or use a tool like AutoMapper, like this:
in Global.asax
//if passed as MMDDYYYY:
Mapper.CreateMap<MyCriteriaViewModel, MyCriteria>().
.ForMember(
dest => dest.Date,
opt => opt.MapFrom(src => Convert.ToDateTime(src.Date.Substring(0,2) + "/" src.Date.Substring(2,2) + "/" src.Date.Substring(4,2)))
);
and in the controller:
public ActionResult MyAction(MyCriteriaViewModel model)
{
var myCriteria = Mapper.Map<MyCriteriaViewModel, MyCriteria>(model);
// etc.
}
From this example it might not seem that AutoMapper is providing any added value. It's value comes when you are configuring several or many mappings with objects that generally have more properties than this example. CreateMap will automatically map properties with the same name and type, so it saves lots of typing and it's much DRYer.

EF 4: Referencing Non-Scalar Variables Not Supported

I'm using code first and trying to do a simple query, on a List property to see if it contains a string in the filtering list. However I am running into problems. For simplicity assume the following.
public class Person
{
public List<string> FavoriteColors { get; set; }
}
//Now some code. Create and add to DbContext
var person = new Person{ FavoriteColors = new List<string>{ "Green", "Blue"} };
dbContext.Persons.Add(person);
myDataBaseContext.SaveChanges();
//Build
var filterBy = new List<string>{ "Purple", "Green" };
var matches = dbContext.Persons.AsQueryable();
matches = from p in matches
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
The option I am considering is transforming this to a json serialized string since I can perform a Contains call if FavoriteColors is a string. Alternatively, I can go overboard and create a "Color" entity but thats fairly heavy weight. Unfortunately enums are also not supported.
I think the problem is not the collection, but the reference to matches.
var matches = dbContext.Persons.AsQueryable();
matches = from p in matches
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
If you check out the Known Issues and Considerations for EF4 this is more or less exactly the case mentioned.
Referencing a non-scalar variables,
such as an entity, in a query is not
supported. When such a query executes,
a NotSupportedException exception is
thrown with a message that states
"Unable to create a constant value of
type EntityType.
Also note that it specifically says that referencing a collection of scalar variables is supported (that's new in EF 4 imo).
Having said that the following should work (can't try it out right now):
matches = from p in dbContext.Persons
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
I decided to experiment by creating a "StringEntity" class to overcome this limitation, and used implicit operators to make nice easy transformations to and from strings. See below for solution:
public class MyClass
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid Id { get; set; }
public List<StringEntity> Animals { get; set; }
public MyClass()
{
List<StringEntity> Animals = List<StringEntity>();
}
}
public class StringEntity
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid Id { get; set; }
public string Value { get; set; }
public StringEntity(string value) { Value = value; }
public static implicit operator string(StringEntity se) { return se.Value; }
public static implicit operator StringEntity(string value) { return new StringEntity(value); }
}
public class MyDbContext : DbContext
{
public DbSet<MyClass> MyClasses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyClass>()
.HasMany(x => x.Animals)
.WithMany()
.Map(x =>
{
x.MapLeftKey(l => l.Id, "MyClassId");
x.MapRightKey(r => r.Id, "StringEntityId");
});
}
}
...Everything looked like it was working perfectly with some testing(Albeit heavy), and then I implemented for its original purpose, a Multiselect ListBox in an MVC3 view. For reasons unknown to me, IF the ListBox is assigned the same NAME as an Entity Collection Property, none of your selected items will be loaded.
To demonstrate the following did NOT work:
//Razor View Code
string[] animalOptions = new string[] {"Dog", "Cat", "Goat"};
string[] animalSelections = new string[] {"Dog", "Cat"};
Html.ListBox("Animals", Multiselect(animalOptions, animalSelections));
To get around this limitation, I needed to do four things:
//#1 Unpluralize the ListBox name so that is doesn't match the name Model.Animals
var animalOptions = new string[] {"Dog", "Cat", "Goat"};
#Html.ListBox("Animal", new MultiSelectList(animalOptions, Model.Animals.Select(x => x.Value)))
//#2 Use JQuery to replace the id and name attribute, so that binding can occur on the form post
<script type="text/javascript">
jQuery(function ($) {
$("select#Animal").attr("name", "Animals").attr("id", "Animals");
});
</script>
//#3 Create a model binder class to handle List<StringEntity> objects
public class StringEntityListBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stringArray = controllerContext.HttpContext.Request.Params.GetValues(bindingContext.ModelName);
return stringArray.Select(x => new StringEntity(x)).ToList();
}
}
//#4 Initialize the binder in your Global.asax setup.
ModelBinders.Binders.Add(typeof(List<StringEntity>), new StringEntityListBinder ());
Note, that the Listbox bug did NOT occur when the property was a List of strings, it just didn't like it when it was a List of entities.

Pass a function to LINQ query

I'd like to pass a function to LINQ queries to do operations on existing data. For eg. I have this function:
string doStuff(Product prod)
{
return string.Format("{0} - {1}", prod.f1, prod.s2);
}
When requesting products, i'd like to pass in this function so it returns this string and assign it to a product property NewString.
An example of the POCO object the repository returns:
class Product
{
public string NewString{get; set;};
public string f1 {get; set;};
public string s2 {get; set;};
}
//Service layer
public IList<Product> GetProducts()
{
//I currently have this:
return _repository.GetProducts().ToList();
//OR, is something like this possible?
return _repository.GetProducts().Select(p => p.NewString = doStuff(p));
}
All methods in the repository returns IQuerable<Product>
So basically, I want to generate a new string from existing data from the service layer. How do I accomplish this without looping through the returned list of objects?
If it is calculated based on Product fields and is needed where Product is needed, why not just add this function call to the NewString getter?
//Service layer
public IList<Product> GetProducts()
{
return _repository
.GetProducts()
.Select(p => new Product {
NewString = doStuff(p),
f1 = p.f1,
s2 = p.s2
})
.ToList;
}

Resources