How i can get a single property insie my async code - asp.net-core

I have the following model class-
public partial class Settings
{
public int Id { get; set; }
public string Value { get; set; }
public string Name { get; set; }
}
Inside my asp.net core MVC 3.1, i want to only get the Value of a single item, now i can NOT do this:-
var result = await _context.Settings.SingleOrDefaultAsync(a => a.Name.ToLower() == "noofvisits").Value;
and the only way is to first get the whole object from the database and then get its Value, as follow:-
var result = await _context.Settings.SingleOrDefaultAsync(a => a.Name.ToLower() == "noofvisits");
var val = result.Value;
but to make my code more efficient how i can directly get the Value property from the database asynchronously ?

You should use Raw Sql query.
Try this :
var result = await _context.Settings.FromSql("SELECT Top(1) NAME FROM dbo.Settings ").SingleOrDefaultAsync();

Try the below statement:
var result = await _context.Settings.Where(a => a.Name.ToLower() == "noofvisits").Select(o => o.Value).FirstOrDefaultAsync();

Related

EF Core 5 with Sum

Repost model class:
public class TransactionDayReport
{
public Decimal TotalAmount { get; set; }
public ICollection<TransactionResponse> TransactionResponses { get; set; } = null!;
}
This is my repository method
public async Task<IEnumerable<Transaction>> SearchTransactionAsync(SearchTransaction search)
{
var query = _context.Transactions
.Include(t => t.Branch)
.Include(t => t.Customer)
.AsQueryable();
if (search.CIN != null)
query = query.Where(t => t.Customer.CIN.Contains(search.CIN));
if (search.ValueDate != null)
query = query.Where(t => t.ValueDate.Date == search.ValueDate.Date);
return await query.ToListAsync();
}
Service method:
public async Task<TransactionResponse> SearchTransactions(SearchTransaction search)
{
var transactionList = await _unitOfWork.Transactions.SearchTransactionAsync(search);
var mapped = ObjectMapper.Mapper.Map<TransactionDayReport>(transactionList);
return mapped;
}
I need to send total amount in service class..
am looking similar to this
But I am confused how to get addition sum added to my dto. Please can anyone suggest a solution? Thanks
The only way to also calculate the sum on the DB is to run another query on the DB, unless the DB query can be re-written to return both the sum and the data.
If you're ok with calculating the Sum on the client, it's more straightforward. Here are some options I could think of:
You could configure the mapper to set the sum.
You could set the sum after performing the mapping in SearchTransactions().
You could change the DTO to calculate the Sum (in which case it wouldn't be a POCO anymore, but I think this is a reasonable approach too):
public class TransactionDayReport
{
private decimal? _totalAmount = null;
public decimal TotalAmount { get {
if(_totalAmount is not null) return _totalAmount; // new C# 9 feature :)
_totalAmount = // calculate total amount here
return _totalAmount;
}
public ICollection<TransactionResponse> TransactionResponses { get; set; } = null!;
}

Asp.Net core ODATA compute property after filter

Is there any way to create a computed property on an ODATA HTTP request, after the ODATA filter has been processed?
I have the following model
class Person
{
public string Name { get; set; }
public string Email { get; set; }
public string ImageLink { get; set; } //This is computed
}
And the following IEdmModel
private static IEdmModel GetEdmModel(IApplicationBuilder app)
{
var builder = new ODataConventionModelBuilder(app.ApplicationServices).EnableLowerCamelCase();
var person = builder.EntitySet<Person>("People").EntityType;
person.Property(x => x.Name);
person.Property(x => x.Email);
person.Property(x => x.ImageLink).IsOptional();
return builder.GetEdmModel();
}
The image link is generated dynamically and is a fairly costly process, as the link is signed. Is there a way to add only add this property when it is explicitly included by a $select and to only calculate after the filter is applied?
Setting the Select(SelectExpandType.Allowed) property does not seem to exclude it from the model.
I've managed to manually exclude the property by checking the query string in the controller. But the property is still included in the model, the value is null, and obviously this runs before any filter has applied.
var people = await _personService.FetchAllPeopleAsync(cancellationToken);
if (Request.Query.TryGetValue("$select", out var values) &&
values.Any(v => v.Contains(nameof(Person.ImageLink))))
{
return _photoService.AttachImageLinks(Url, people);
}
return people;

Swagger listing IFormFile parameter as type "object"

I have a controller that requests a model containing an IFormFile as one of it's properties. For the request description, the Swagger UI (I'm using Swashbuckle and OpenApi 3.0 for .NET Core) lists the type of the file property as type object. Is there some way to make the Swagger UI denote the exact type and it's JSON representation to help the client?
The controller requesting the model looks as follows.
[HttpPost]
[Consumes("multipart/form-data")
public async Task<IActionResult> CreateSomethingAndUploadFile ([FromForm]RequestModel model)
{
// do something
}
And the model is defined as below:
public class AssetCreationModel
{
[Required}
public string Filename { get; set; }
[Required]
public IFormFile File { get; set; }
}
We've been exploring this issue today. If you add the following to your startup it will convert IFormFile to the correct type
services.AddSwaggerGen(c => {
c.SchemaRegistryOptions.CustomTypeMappings.Add(typeof(IFormFile), () => new Schema() { Type = "file", Format = "binary"});
});
Also see the following article on file upload in .net core
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.1
This problem was already tackled in the following github issue/thread.
This improvement was already merged into Swashbuckle.AspNetCore master (as per 10/30/2018), but i don't expect that to be available as a package soon.
There are simple solutions if you only have a IFormFile as a parameter.
public async Task UploadFile(IFormFile filePayload){}
For simple case you can take a look at the following answer.
For complicated cases like container cases, you can take a look at the following answer.
internal class FormFileOperationFilter : IOperationFilter
{
private struct ContainerParameterData
{
public readonly ParameterDescriptor Parameter;
public readonly PropertyInfo Property;
public string FullName => $"{Parameter.Name}.{Property.Name}";
public string Name => Property.Name;
public ContainerParameterData(ParameterDescriptor parameter, PropertyInfo property)
{
Parameter = parameter;
Property = property;
}
}
private static readonly ImmutableArray<string> iFormFilePropertyNames =
typeof(IFormFile).GetTypeInfo().DeclaredProperties.Select(p => p.Name).ToImmutableArray();
public void Apply(Operation operation, OperationFilterContext context)
{
var parameters = operation.Parameters;
if (parameters == null)
return;
var #params = context.ApiDescription.ActionDescriptor.Parameters;
if (parameters.Count == #params.Count)
return;
var formFileParams =
(from parameter in #params
where parameter.ParameterType.IsAssignableFrom(typeof(IFormFile))
select parameter).ToArray();
var iFormFileType = typeof(IFormFile).GetTypeInfo();
var containerParams =
#params.Select(p => new KeyValuePair<ParameterDescriptor, PropertyInfo[]>(
p, p.ParameterType.GetProperties()))
.Where(pp => pp.Value.Any(p => iFormFileType.IsAssignableFrom(p.PropertyType)))
.SelectMany(p => p.Value.Select(pp => new ContainerParameterData(p.Key, pp)))
.ToImmutableArray();
if (!(formFileParams.Any() || containerParams.Any()))
return;
var consumes = operation.Consumes;
consumes.Clear();
consumes.Add("application/form-data");
if (!containerParams.Any())
{
var nonIFormFileProperties =
parameters.Where(p =>
!(iFormFilePropertyNames.Contains(p.Name)
&& string.Compare(p.In, "formData", StringComparison.OrdinalIgnoreCase) == 0))
.ToImmutableArray();
parameters.Clear();
foreach (var parameter in nonIFormFileProperties) parameters.Add(parameter);
foreach (var parameter in formFileParams)
{
parameters.Add(new NonBodyParameter
{
Name = parameter.Name,
//Required = , // TODO: find a way to determine
Type = "file"
});
}
}
else
{
var paramsToRemove = new List<IParameter>();
foreach (var parameter in containerParams)
{
var parameterFilter = parameter.Property.Name + ".";
paramsToRemove.AddRange(from p in parameters
where p.Name.StartsWith(parameterFilter)
select p);
}
paramsToRemove.ForEach(x => parameters.Remove(x));
foreach (var parameter in containerParams)
{
if (iFormFileType.IsAssignableFrom(parameter.Property.PropertyType))
{
var originalParameter = parameters.FirstOrDefault(param => param.Name == parameter.Name);
parameters.Remove(originalParameter);
parameters.Add(new NonBodyParameter
{
Name = parameter.Name,
Required = originalParameter.Required,
Type = "file",
In = "formData"
});
}
}
}
}
}
You need to look into how you can add some/an OperationFilter that is suitable for your case.

Querying for RavenDB documents using multiple properties

I need to make a query against a document collection that matches several properties.
(Cross post from the mailing list: https://groups.google.com/forum/?fromgroups=#!topic/ravendb/r5f1zr2jd_o)
Here is the document:
public class SessionToken
{
[JsonProperty("jti")]
public string Id { get; set; }
[JsonProperty("aud")]
public Uri Audience { get; set; }
[JsonProperty("sub")]
public string Subject { get; set; }
[JsonProperty("claims")]
public Dictionary<string, string> Claims { get; set; }
}
And here is the test:
[TestFixture]
public class RavenDbTests
{
private IDocumentStore documentStore;
[SetUp]
public void SetUp()
{
this.documentStore = new EmbeddableDocumentStore() { RunInMemory = true };
this.documentStore.Initialize();
}
[Test]
public async void FirstOrDefault_WhenSessionTokenExists_ShouldReturnSessionToken()
{
var c = new SessionToken()
{
Audience = new Uri("http://localhost"),
Subject = "NUnit",
Claims = new Dictionary<string, string>()
{
{ ClaimTypes.System, "NUnit" }
}
};
using (var session = this.documentStore.OpenAsyncSession())
{
await session.StoreAsync(c);
await session.SaveChangesAsync();
// Check if the token exists in the database without using Where clause
var allTokens = await session.Query<SessionToken>().ToListAsync();
Assert.That(allTokens.Any(x => x.Subject == "NUnit" && x.Audience == new Uri("http://localhost")));
// Try getting token back with Where clause
var token = await session.Query<SessionToken>().Customize(x => x.WaitForNonStaleResults()).Where(x => x.Subject == "NUnit" && x.Audience == new Uri("http://localhost")).ToListAsync();
Assert.IsNotNullOrEmpty(token.First().Id);
}
}
}
The last Assert is the one that is failing.
I must admit Im not sure whether this is a bug or a failure on my part.
As far as I understand, this is supposed to work.
PS. I´ve tried with a standalone document store as well as embedded without running in memory, but with same result.
You are getting stale results. In a unit test, you need to allow time for indexing to occur.
Add .Customize(x=> x.WaitForNonStaleResults()) to your queries and the test should pass.
Also, I think you left the Id property off your question when you cut/paste because it doesn't compile as-is.
UPDATE
Per discussion in comments, the issue was that you were applying the [JsonProperty] attribute to the Id property. Since the Id property represents the document key, and is not serialized as part of the JSON document, you can't apply the [JsonProperty] attribute to it.

Best Practice with MVC4 and EF5 to apply changes

I have a CustomerOrder-view where I would like to change an existing CustomerOrder.
I have a viewmodel that very simpliefied looks something like this:
public class CustomerOrderViewModel
{
public int ID { get; set; }
public Customer Customer { get; set; }
public DateTime Date { get; set; }
public IEnumerable<OrderRow> OrderRows { get; set; }
}
public class OrderRow
{
public int id { get; set; }
public int price { get; set; }
}
public class Customer
{
public string Name { get; set; }
}
I also have a database with mapping tables / fields.
In my GET Action Method I load the Order with the help of Automapper like this:
var customerOrder = using (var ctx = new My_Entities()) {
return ctx.CustomerOrders.
Include("Orderrows").
Include("Customer").
Single(o => o.CustomerOrderID == id);
}
var model= AutoMapper.Mapper.DynamicMap<DataAccessLayer.CustomerOrder, CustomerOrderViewModel>(customerOrder);
In the View I use Knockout to bind to a viewmodel there, where the user can update the CustomerOrder. That includes editing Customer information and adding new orderrows etc.
Then in the post back a map the ViewModel back to the ObjectContext CustomerOrder:
var customerOrderToBeSaved =
AutoMapper.Mapper.DynamicMap<CustomerOrderViewModel, CustomerOrder>(
customerOrderViewModel);
try
{
using (var ctx = new MyEntities())
{
ctx.CustomerOrders.Attach(customerOrderToBeSaved);
ctx.CustomerOrders.ApplyCurrentValues(customerOrderToBeSaved);
...
ctx.SaveChanges();
}
}
I get the error message: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
OK, that I can understand. But how should I go about this? Can I get the existing object and apply Changes to that one, because that is really what I'd like. I've tried to look up the old one and detach it but I haven't got it to wrok.Perhaps I'm doing this in a completely wrong way. Please advice.
You should not attach customerOrderToBeSaved, see MSDN about the argument of ApplyCurrentValues.
The detached object that has property updates to apply to the original object.
So you've got to load the entity from the database into the context and then ApplyCurrentValues with the detached object that has the new values.
You don't have to load the row from the database to update it.
You can do something like this:
var entity = ctx.CustomerOrders.Attach(customerOrderToBeSaved);
ctx.Entry( entity ).State = EntityState.Modified;
ctx.SaveChanges();
This will tell EF to issue an UPDATE SQL statement that overwrites all the columns in the record.
You can select which columns you want to update like this:
var entity = ctx.CustomerOrders.Attach(customerOrderToBeSaved);
var entry = ctx.Entry( entity );
entry.Property( o => o.<ColumnPropertyToUpdate> ).IsModified = true;
entry.Property( o => o.<ColumnPropertyToUpdate> ).IsModified = true;
...
ctx.SaveChanges();
If you do this, EF will only include the columns you've marked as modified in the UPDATE statement.