How can i use Custom query Single field in EFCore? - entity-framework

In EF6, there is context.Database.sqlquery that I can use. Why does EF Core cancel this?
I try to find the answer but the context.user.FromInterpolated doesn't allow select Single field. (Or am I using the wrong method of this?)
Here is my code. Please give me some advice that I can solve this problem.
[HttpPost]
[Obsolete]
public async Task<IActionResult> SendEmail()
{
Email email = new Email();
var ID = HttpContext.Request.Form["ID"].ToString();
var Title = HttpContext.Request.Form["Title"].ToString();
var Body = HttpContext.Request.Form["Body"].ToString();
var Emails = context.user.FromSqlInterpolated($"select email from user where UserId in({ID})");
foreach (var item in Emails)
{
if (!string.IsNullOrEmpty(item.Email))
{
email.Send(item.Email, Title, Body);
}
}
await HttpResponseWritingExtensions.WriteAsync(this.Response, "success");
return RedirectToAction(nameof(Index));
}

You can do something like this:
public class StringReturn
{
public string Value { get; set; }
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
{
modelBuilder.Entity<StringReturn>().HasNoKey();
}
using (var db = new NorthwindContext())
{
var result = db.Set<IntReturn>()
.FromSqlRaw("exec dbo.Scalar")
.AsEnumerable()
.First().Value;
Console.WriteLine(result);
}
See my blog post here for more info: https://erikej.github.io/efcore/2020/05/26/ef-core-fromsql-scalar.html

Ok first of all you have sql injection in your code. So if user pass in field id smth like 1 ) or ( 1=1 . It will allow it and that is basic not very harmfull in your case but
What you should really do is
var ids= HttpContext.Request.Form["ID"].ToString().Split(",", StringSplitOptions.RemoveEmptyEntries).ToList();
if(ids.Any()){
var Emails = await context.user.Where(u=>ids.Contains(u.UserId)).Select(u=>u.email).ToListAsync();
}

Related

Entity Framework 6 dbContext not saving changes

In our MVC 5 project our database context is instantiate in the AccountController like this
private CustomersContext _customersContext;
public CustomersContext CustContext
{
get
{
return _customersContext ?? new CustomersContext();
}
private set
{
_customersContext = value;
}
}
Each customer is referred by a number of sources. The routine below changes the UserId of the referral source to a new user.
var referralList = CustContext.Referrals.Where(d => d.UserId == membershipUser.Id);
foreach (Referral referral in referralList)
{
referral.UserId = newUser.Id;
}
Stepping trough the code I can see referral.UserId being updated. However
var result = await CustContext.SaveChangesAsync();
returns 0. The database is not updated.
CustomersContext looks like this
{
public partial class CustomersContext : IdentityDbContext<ApplicationUser>//, ICustomersContext
{
public CustomersContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static CustomersContext Create()
{
return new CustomersContext();
}
public virtual DbSet<ReferralSource> ReferralSources { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasMany(e => e.Referrals)
.WithRequired(e => e.User)
.HasForeignKey(e => e.UserId)
.WillCascadeOnDelete(false);
I don't see any sql emitted in SQL Profiler. Why doesn't the database context save changes?
Before calling var result = await CustContext.SaveChangesAsync(); you need to set the state of the entities that you want to be modified. Somthing like:
var referralList = CustContext.Referrals.Where(d => d.UserId == membershipUser.Id);
foreach (Referral referral in referralList)
{
referral.UserId = newUser.Id;
CustContext.Entry(referral).State = System.Data.Entity.EntityState.Modified;
}
var result = await CustContext.SaveChangesAsync();
The answer provided by #Issac did not solve my problem, but it did put me on the road to a solution. The error
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
suggested there were multiple instances of dbContext. I removed the context from the CTOR and instantiated the context within a using statement
using (CustomersContext customersContext = new CustomersContext())
{
var referralList = customersContext.Referrals.Where(d => d.UserId == membershipUser.Id);
foreach (Referral referral in referralList)
{
referral.UserId = newUser.Id;
}
var result = await customersContext.SaveChangesAsync();
}
and now all is tickety-boo

How to write a generic WebAPI Put method against Entity Framework that works with child lists?

I am tinkering with WebAPI to create a generic implementation for entity framework. I am able to implement most of the methods just fine, but am finding PUT to be tricky in non-trivial cases. The implementation most commonly found online works for simple entities:
[HttpPut]
[ActionName("Endpoint")]
public virtual T Put(T entity)
{
var db = GetDbContext();
var entry = db.Entry(entity);
entry.State = EntityState.Modified;
var set = db.Set<T>();
set.Attach(entity);
db.SaveChanges();
return entity;
}
...but does not delete or update child lists:
public class Invoice
{
...
public virtual InvoiceLineItem {get; set;} //Attach method doesn't address these
}
In an MVC Controller, you could simply use "UpdateModel" and it would add/update/delete children as needed, however that method is not available on ApiController. I understand that some code would be necessary to get the original item from the database, and that it would need to use Include to get the child lists, but can't quite figure out the best way to replicate UpdateModel's functionality:
[HttpPut]
[ActionName("Endpoint")]
public virtual T Put(T entity)
{
var db = GetDbContext();
var original = GetOriginalFor(entity);
//TODO: Something similar to UpdateModel(original), such as UpdateModel(original, entity);
db.SaveChanges();
return original;
}
How can I implement UpdateModel OR somehow implement Put in such a way that it will handle child lists?
The routine dont validate entity, but fill the pre-existent entity.
protected virtual void UpdateModel<T>(T original, bool overrideForEmptyList = true)
{
var json = ControllerContext.Request.Content.ReadAsStringAsync().Result;
UpdateModel<T>(json, original, overrideForEmptyList);
}
private void UpdateModel<T>(string json, T original, bool overrideForEmptyList = true)
{
var newValues = JsonConvert.DeserializeObject<Pessoa>(json);
foreach (var property in original.GetType().GetProperties())
{
var isEnumerable = property.PropertyType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (isEnumerable && property.PropertyType != typeof(string))
{
var propertyOriginalValue = property.GetValue(original, null);
if (propertyOriginalValue != null)
{
var propertyNewValue = property.GetValue(newValues, null);
if (propertyNewValue != null && (overrideForEmptyList || ((IEnumerable<object>)propertyNewValue).Any()))
{
property.SetValue(original, null);
}
}
}
}
JsonConvert.PopulateObject(json, original);
}
public void Post()
{
var sample = Pessoa.FindById(12);
UpdateModel(sample);
}

Entity Framework + ODATA: side-stepping the pagination

The project I'm working on has the Entity Framework on top of an OData layer. The Odata layer has it's server side pagination turned to a value of 75. My reading on the subject leads me to believe that this pagination value is used across the board, rather than a per table basis. The table that I'm currently looking to extract all the data from is, of course, more than 75 rows. Using the entity framework, my code is simply thus:
public IQueryable<ProductColor> GetProductColors()
{
return db.ProductColors;
}
where db is the entity context. This is returning the first 75 records. I read something where I could append a parameter inlinecount set to allpages giving me the following code:
public IQueryable<ProductColor> GetProductColors()
{
return db.ProductColors.AddQueryOption("inlinecount","allpages");
}
However, this too returns 75 rows!
Can anyone shed light on how to truly get all the records regardless of the OData server-side pagination stuff?
important: I cannot remove the pagination or turn it off! It's extremely valuable in other scenarios where performance is a concern.
Update:
Through some more searching I've found an MSDN that describes how to do this task.
I'd love to be able to turn it into a full Generic method but, this was as close as I could get to a generic without using reflection:
public IQueryable<T> TakeAll<T>(QueryOperationResponse<T> qor)
{
var collection = new List<T>();
DataServiceQueryContinuation<T> next = null;
QueryOperationResponse<T> response = qor;
do
{
if (next != null)
{
response = db.Execute<T>(next) as QueryOperationResponse<T>;
}
foreach (var elem in response)
{
collection.Add(elem);
}
} while ((next = response.GetContinuation()) != null);
return collection.AsQueryable();
}
calling it like:
public IQueryable<ProductColor> GetProductColors()
{
QueryOperationResponse<ProductColor> response = db.ProductColors.Execute() as QueryOperationResponse<ProductColor>;
var productColors = this.TakeAll<ProductColor>(response);
return productColors.AsQueryable();
}
If unable turn off paging you'll receive 75 row by call, always. You can get all rows in following ways:
Add another IQueryable<ProductColor> AllProductColors and modify
public static void InitializeService(DataServiceConfiguration config)
{
config.UseVerboseErrors = true;
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.SetEntitySetPageSize("ProductColors", 75); - Note only paged queries are present
config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
You should call ProductColors as many as needed, for example
var cat = new NetflixCatalog(new Uri("http://odata.netflix.com/v1/Catalog/"));
var x = from t in cat.Titles
where t.ReleaseYear == 2009
select t;
var response = (QueryOperationResponse<Title>)((DataServiceQuery<Title>)x).Execute();
while (true)
{
foreach (Title title in response)
{
Console.WriteLine(title.Name);
}
var continuation = response.GetContinuation();
if (continuation == null)
{
break;
}
response = cat.Execute(continuation);
}
I use Rx with following code
public sealed class DataSequence<TEntry> : IObservable<TEntry>
{
private readonly DataServiceContext context;
private readonly Logger logger = LogManager.GetCurrentClassLogger();
private readonly IQueryable<TEntry> query;
public DataSequence(IQueryable<TEntry> query, DataServiceContext context)
{
this.query = query;
this.context = context;
}
public IDisposable Subscribe(IObserver<TEntry> observer)
{
QueryOperationResponse<TEntry> response;
try
{
response = (QueryOperationResponse<TEntry>)((DataServiceQuery<TEntry>)query).Execute();
if (response == null)
{
return Disposable.Empty;
}
}
catch (Exception ex)
{
logger.Error(ex);
return Disposable.Empty;
}
var initialState = new State
{
CanContinue = true,
Response = response
};
IObservable<TEntry> sequence = Observable.Generate(
initialState,
state => state.CanContinue,
MoveToNextState,
GetCurrentValue,
Scheduler.ThreadPool).Merge();
return new CompositeDisposable(initialState, sequence.Subscribe(observer));
}
private static IObservable<TEntry> GetCurrentValue(State state)
{
if (state.Response == null)
{
return Observable.Empty<TEntry>();
}
return state.Response.ToObservable();
}
private State MoveToNextState(State state)
{
DataServiceQueryContinuation<TEntry> continuation = state.Response.GetContinuation();
if (continuation == null)
{
state.CanContinue = false;
return state;
}
QueryOperationResponse<TEntry> response;
try
{
response = context.Execute(continuation);
}
catch (Exception)
{
state.CanContinue = false;
return state;
}
state.Response = response;
return state;
}
private sealed class State : IDisposable
{
public bool CanContinue { get; set; }
public QueryOperationResponse<TEntry> Response { get; set; }
public void Dispose()
{
CanContinue = false;
}
}
}
so for get any data thru OData, create a sequence and Rx does the rest
var sequence = new DataSequence<Product>(context.Products, context);
sequence.OnErrorResumeNext(Observable.Empty<Product>())
.ObserveOnDispatcher().SubscribeOn(Scheduler.NewThread).Subscribe(AddProduct, logger.Error);
The page size is set by the service author and can be set per entity set (but a service may choose to apply the same page size to all entity sets). There's no way to avoid it from the client (which is by design since it's a security feature).
The inlinecount option asks the server to include the total count of the results (just the number), it doesn't disable the paging.
From the client the only way to read all the data is to issue the request which will return the first page and it may contain a next link which you request to read the next page and so on until the last response doesn't have the next link.
If you're using the WCF Data Services client library it has support for continuations (the next link) and a simple sample can be found in this blog post (for example): http://blogs.msdn.com/b/phaniraj/archive/2010/04/25/server-driven-paging-with-wcf-data-services.aspx

How to filter bad words of textbox in ASP.NET MVC?

I have a requirement in which i wanna filter the textbox value, that is should remove the bad words entered by the user. Once the user enters the bad words and click on submit button, action is invoked. Somewhere in the model(any place) i should be able to remove the bad words and rebind the filtered value back to the model.
How can i do this?
If you can update the solution to MVC 3 the solution is trivial. Just implement the word check in a controller and then apply the RemoteAttribute on the property that should be validated against bad words. You will get an unobtrusive ajax check and server side check with just one method and one attribute. Example:
public class YourModel
{
[Remote("BadWords", "Validation")]
public string Content { get; set; }
}
public class ValidationController
{
public JsonResult BadWords(string content)
{
var badWords = new[] { "java", "oracle", "webforms" };
if (CheckText(content, badWords))
{
return Json("Sorry, you can't use java, oracle or webforms!", JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
private bool CheckText(string content, string[] badWords)
{
foreach (var badWord in badWords)
{
var regex = new Regex("(^|[\\?\\.,\\s])" + badWord + "([\\?\\.,\\s]|$)");
if (regex.IsMatch(content)) return true;
}
return false;
}
}

Stuffing an anonymous type in ViewBag causing model binder issues

can someone tell me what I'm doing wrong? :-)
I have this simple query:
var sample = from training in _db.Trainings
where training.InstructorID == 10
select new { Something = training.Instructor.UserName };
And I pass this to ViewBag.
ViewBag.Sample = sample;
Then I want to access it in my view like this:
#foreach (var item in ViewBag.Sample) {
#item.Something
}
And I get error message 'object' does not contain a definition for 'Something'. If I put there just #item, I get result { Something = SomeUserName }
Thanks for help.
This cannot be done. ViewBag is dynamic and the problem is that the anonymous type is generated as internal. I would recommend you using a view model:
public class Instructor
{
public string Name { get; set; }
}
and then:
public ActionResult Index()
{
var mdoel = from training in _db.Trainings
where training.InstructorID == 10
select new Instructor {
Name = training.Instructor.UserName
};
return View(model);
}
and in the view:
#model IEnumerable<Instructor>
#foreach (var item in ViewBag.Sample) {
#item.Something
}
If you want to send in ViewData For example and don't want to send in model
you could use the same could as in the upper answer
and in the Controller
enter code here
ViewData[Instractor] = from training in _db.Trainings
where training.InstructorID == 10
select new Instructor {
Name = training.Instructor.UserName
};
and in the view you need to cast this to
`IEnumerable<Instructor>`
but to do this you should use
#model IEnumerable<Instructor>
Then you could do something like this
IEnumerable<instructors> Instructors =(IEnumerable<Instructor>)ViewData[Instractor];
then go with foreach
#foreach (var item in Instructors ) {
#item.Something
}