Php to C# aspnet mvc - c#

i have transform a php/js code to js/c#, but i stuck for update the new value.
The php code is :
`if (isset($_POST['update'])) {
foreach($_POST['positions'] as $position) {
$index = $position[0];
$newPosition = $position[1];
$conn->query("UPDATE country SET position = '$newPosition' WHERE id='$index'");
}
exit('success');
}`
My "empty" c# code
[HttpPost]
public ActionResult Index (userTable index)
{
picturesEntities MyDb = new picturesEntities();
homeViewModel HVM = new homeViewModel();
HVM.userTables = MyDb.userTables.ToList();
if (Request["update"] != null)
{
foreach (Request["positions"])
{
MyDb.SaveChanges();
}
return View(HVM);
}
}
If someone could help me for it that would be great, i'm stuck on it for days and i didn't find a workning solution yet.
Thanks to everyone who read my message.

Most ASP.NET will bind a custom class which will be compatible to your request.
public class UserPositionsRequest
{
public bool Update { get; set; }
// For orderly, this actually be a list of a custom class
public List<int[]> Positions { get; set; }
}
This by any means is not a complete and working solution, the following code was never been tested and can be consider as pseudo-like code.
Also, the .Id and .Position should be the same sensitivity as in Db.
// Binding our UserPositionsRequest class
public void Index(UserPositionsRequest request) {
// Checking if we should update, if you will change the request to boolean type: "true"
// ..on the client side, then you could actually change the condition to be: if (request.Update)
if (request.Update == 1) {
// Creating database connection using (I assume) EntityFramework
using (var picturesEntities = new picturesEntities()) {
// Building a dictionary for fast lookup. Key, Value as the 0, 1 arg respectfully
var usersDataToUpdate = request.Positions.ToDictionary(p => p[0], p => p[1]);
// Finding the entries that needs to be updated
var usersEntitiesToUpdate = picturesEntities.userTables.Where(cntry => usersDataToUpdate.ContainsKey(cntry.Id));
// Iterating over the entities
foreach (var userEntity in usersEntitiesToUpdate) {
// Updating their position.
userEntity.Position = usersDataToUpdate[userEntity.Id];
}
picturesEntities.SaveChanges();
}
}
// Probably you wanted to return something here, but it's probably an ajax and you can skip that.
}

Related

Fluent Validation changing CustomAsync to MustAsync

Could some one please help me to resolved this? i'm trying to change CustomAsync to MustAsync, but i couldn't make things to work. Below is my custom method
RuleFor(o => o).MustAsync(o => {
return CheckIdNumberAlreadyExist(o)
});
private static async Task<ValidationFailure> CheckIdNumberAlreadyExist(SaveProxyCommand command)
{
if (command.Id > 0)
return null;
using (IDbConnection connection = new SqlConnection(ConnectionSettings.LicensingConnectionString))
{
var param = new DynamicParameters();
param.Add("#idnumber", command.IdNumber);
var vehicle = await connection.QueryFirstOrDefaultAsync<dynamic>("new_checkDuplicateProxyIdNumber", param, commandType: CommandType.StoredProcedure);
return vehicle != null
? new ValidationFailure("IdNumber", "Id Number Already Exist")
: null;
}
}
To make it work with the latest version of the FluentValidation, I had to use the codes like below.
RuleFor(ws => ws).MustAsync((x, cancellation) => UserHasAccess(x)).WithMessage("User doesn't have access to perform this action");
Please notice the lambda expression here MustAsync((x, cancellation) => UserHasAccess(x)), without this I was always getting an error as cannot convert from 'method group' to 'Func<Worksheet, CancellationToken, Task<bool>>
Below is my custom UserHasAccess function.
private async Task <bool> UserHasAccess(Worksheet worksheet) {
var permissionObject = await _dataProviderService.GetItemAsync(worksheet.FileItemId);
if (permissionObject is null) return false;
if (EditAccess(permissionObject.Permission)) return true;
return false;
}
I'm assuming you're using a version of FluentValidation prior to version 6, as you're not passing in a Continuation Token, so I've based my answer on version 5.6.2.
Your example code does not compile, for starters, as you're missing a semi-colon in your actual rule. You are also evaluating two different properties on the SaveProxyCommand parameter.
I've built a very small POC based on some assumptions:
Given 2 classes:
public class SaveProxyCommand {
public int Id { get; set; }
}
public class ValidationFailure {
public string PropertyName { get; }
public string Message { get; }
public ValidationFailure(string propertyName, string message){
Message = message;
PropertyName = propertyName;
}
}
And a validator:
public class SaveProxyCommandValidator : AbstractValidator<SaveProxyCommand>{
public SaveProxyCommandValidator()
{
RuleFor(o => o).MustAsync(CheckIdNumberAlreadyExists)
.WithName("Id")
.WithState(o => new ValidationFailure(nameof(o.IdNumber), "Id Number Already Exist"));
}
private static async Task<bool> CheckIdNumberAlreadyExists(SaveProxyCommand command) {
if (command.Id > 0)
return true;
var existingIdNumbers = new[] {
1, 2, 3, 4
};
// This is a fudge, but you'd make your db call here
var isNewNumber = !(await Task.FromResult(existingIdNumbers.Contains(command.IdNumber)));
return isNewNumber;
}
}
I didn't include the call to the database, as that's not part of your problem. There are a couple of things of note here:
You're not setting the .WithName annotation method, but when you're setting up a validation rule for an object you have to do this, as FluentValidation expects you to specify specific properties to be validated by default, if you pass in an entire object it just doesn't know how to report errors back.
Must/MustAsync need to return a bool/Task<bool> instead of a custom object. To get around this, you can specify a custom state to be returned when failing validation.
You can then get access to this like this:
var sut = new SaveProxyCommand { Id = 0, IdNumber = 3 };
var validator = new SaveProxyCommandValidator();
var result = validator.ValidateAsync(sut).GetAwaiter().GetResult();
var ValidationFailures = result.Errors?.Select(s => s.CustomState).Cast<ValidationFailure>();
The above does not take into account empty collections, it's just an example of how to dig into the object graph to retrieve custom state.
As a suggestion, fluentvalidation works best if you set up individual rules per property, instead of validating the entire object. My take on this would be something like this:
public class SaveProxyCommandValidator : AbstractValidator<SaveProxyCommand>{
public SaveProxyCommandValidator()
{
RuleFor(o => o.IdNumber).MustAsync(CheckIdNumberAlreadyExists)
.Unless(o => o.Id > 0)
.WithState(o => new ValidationFailure(nameof(o.IdNumber), "Id Number Already Exist"));
}
private static async Task<bool> CheckIdNumberAlreadyExists(int numberToEvaluate) {
var existingIdNumbers = new[] {
1, 2, 3, 4
};
// This is a fudge, but you'd make your db call here
var isNewNumber = !(await Task.FromResult(existingIdNumbers.Contains(numberToEvaluate)));
return isNewNumber;
}
}
This read more like a narrative, it uses the .Unless construct to only run the rule if Id is not more than 0, and does not require the evaluation of the entire object.

c# how to decide whether to add to sub-list or start new one

I have an sql query that provides me my data where I sometimes have lines that should be clustered (the data is aligned with an order by). The data is grouped by the field CAPName. Going through those rows line by line, I need to decide whether a new list should be initiated (content of CAPName differs to previous itteration), or whether the (already) initated list (from the previous iteration) should be added, too.
My pain lays with the location of the declaration of the relatedCapabilitySystem list.
I wanted to declare it within the if statement (Because, as I stated I need to decide whether the list from the previous iteration should be added too, or whether it should start a new list), but I can't as the compiler throws an exception, as the RLCapSys.Add(rCs); is non-existing in this content (which is only theoretically true). I understand why the compiler throws this exception. But if I declare the list on a "higher" level, than I always have a new list, which I don't want in the case that the item should be added to the list defined in the iteration(s) (1 or more) before
So what I want to achieve is, generate the list RLCapSys and add to it, in case the previous iteration contains the same CAPName (for clustering), otherwise create a new list.
SqlCommand cmdDetail = new SqlCommand(SQL_SubSytemsToCapability, DBConDetail);
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
List<relatedCapility> RLCaps = new List<relatedCapility>();
string lastCapShown = null;
while (rdrDetail.Read())
{
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
if (lastCapShown != rdrDetail["CAPName"].ToString())
{
//List<relatedCapabilitySystem> RLCapSys2 = new List<relatedCapabilitySystem>();
relatedCapility rC = new relatedCapility
{
Capability = rdrDetail["CAPName"].ToString(),
systemsRelated = RLCapSys,
};
RLCaps.Add(rC);
}
relatedCapabilitySystem rCs = new relatedCapabilitySystem
{
system = rdrDetail["name"].ToString(),
start = rdrDetail["SysStart"].ToString(),
end = rdrDetail["SysEnd"].ToString(),
};
RLCapSys.Add(rCs);
// method to compare the last related Capability shown create a new related Capabilty entry or add to the existing releated Capabilty related system list
lastCapShown = rdrDetail["CAPName"].ToString();
}
DBConDetail.Close();
and for reason of completness (but I think it is not needed here):
internal class CapabilitiesC
{
public List<Capability>Capabilities{ get;set;}
}
public class Capability
{
public string name { get; internal set; }
public string tower { get; internal set; }
public string color { get; internal set; }
public List<relatedCapility> related { get; set; }
}
public class relatedCapility
{
public string Capability { get; set; }
public List<relatedCapabilitySystem> systemsRelated { get; set; }
}
public class relatedCapabilitySystem
{
public string system { get; set; }
public string start { get; set; }
public string end { get; set; }
}
The purpose of your code is to take the input data and group it by capability. However, that is not immediately obvious. You can change your code to use LINQ so it becomes easier to understand and in the process solving your problem.
First you need a type to represent a record in your database. For lack of better name I will use Record:
class Record
{
public string System { get; set; }
public string Start { get; set; }
public string End { get; set; }
public string Capabilty { get; set; }
}
You can then create an iterator block to return all the records from the database (using an OR mapper like Entity Framework avoids most of this code and you can even shift some of the work from your computer to the database server):
IEnumerable<Record> GetRecords()
{
// Code to create connection and command (preferably in a using statement)
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
while (rdrDetail.Read())
{
yield return new Record {
System = rdrDetail["name"].ToString(),
Start = rdrDetail["SysStart"].ToString(),
End = rdrDetail["SysEnd"].ToString(),
Capability = rdrDetail["CAPName"].ToString()
};
}
// Close connection (proper using statement will do this)
}
Finally, you can use LINQ to perform the grouping:
var RLCaps = GetRecords()
.GroupBy(
record => record.Capability,
(capability, records) => new relatedCapility
{
Capability = capability ,
systemsRelated = records
.Select(record => new relatedCapabilitySystem
{
system = record.System,
start = record.Start,
end = record.End
})
.ToList()
})
.ToList();
Why not just assign it as NULL. The pattern would be
List<> myList = null;
if(condition)
{
myList = new List<>();
}
else
{
myList = previousList;
}
myList.Add();
previousList = myList;
I've got it working now. Thx everyone for your help. #martin, thx for your solution, you have put quite some effort into this, but that would have required for me to completely re-write my code. I am sure your approach would work and will be my next approach should I have a similar problem again.
It was a combination of the other answers that helped me figure it out. Let me show you what I ended up with:
SqlCommand cmdDetail = new SqlCommand(SQL_SubSytemsToCapability, DBConDetail);
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
List<relatedCapility> RLCaps = new List<relatedCapility>();
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
string lastCapShown = null;
while (rdrDetail.Read())
{
if (lastCapShown != rdrDetail["CAPName"].ToString())
{
RLCapSys = relatedCapabilitySystemList();
relatedCapility rC = new relatedCapility
{
Capability = rdrDetail["CAPName"].ToString(),
systemsRelated = RLCapSys,
};
RLCaps.Add(rC);
}
relatedCapabilitySystem rCs = new relatedCapabilitySystem
{
system = rdrDetail["name"].ToString(),
start = rdrDetail["SysStart"].ToString(),
end = rdrDetail["SysEnd"].ToString(),
};
RLCapSys.Add(rCs);
// method to compare the last related Capability shown create a new related Capabilty entry or add to the existing releated Capabilty related system list
lastCapShown = rdrDetail["CAPName"].ToString();
}
DBConDetail.Close();
So that's the section already shown bevor including my changes. Plus I added this:
private List<relatedCapabilitySystem> relatedCapabilitySystemList()
{
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
return RLCapSys;
}
Now I have new list reference everytime the CapName changes that is then added to the "higher" list. Before I had the issue of the very same list repeatedly assigned rather than a fresh one started. So thx again for your effort.

Entity Framework 6 SaveChanges() override not consistently detecting changes

I've extended a class in order to add a last modified timestamp to a record if there are material changes being made to it. This was done with code similar to this.
Here's my problem. SaveChanges() is firing for both changes, but the second one isn't getting into the loop: no objects are detected as needing changes.
However, the record does get updated by EF through the base.SaveChanges() call.
Here's the extension to MasterTable:
namespace AuditTestEF
{
public interface IHasAuditing
{
DateTime LastModifiedOn { get; set; }
int LastModifiedBy { get; set; }
}
public partial class MasterTable : IHasAuditing
{
}
public class AuditTestEntitiesWithAuditing : AuditTestEntities
{
public int TestingUserIs = 1;
public override int SaveChanges()
{
foreach (ObjectStateEntry entry in (this as IObjectContextAdapter)
.ObjectContext
.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
{
// This loop is entered the first time, but not the second
if (entry.IsRelationship) continue;
var lastModified = entry.Entity as IHasAuditing;
if (lastModified == null) continue;
lastModified.LastModifiedOn = DateTime.UtcNow;
lastModified.LastModifiedBy = TestingUserIs;
}
return base.SaveChanges();
}
}
}
And here's the test harness:
[TestMethod]
public void TestMethod1()
{
MasterTable mtOriginal;
using (var audit = new AuditTestEntitiesWithAuditing())
{
var message = "Hello";
audit.TestingUserIs = 1;
mtOriginal = new MasterTable {TextField = message};
audit.MasterTable.Add(mtOriginal);
audit.SaveChanges();
// This test passes, TestingUser is set in the override
Assert.IsTrue(mtOriginal.LastModifiedBy == audit.TestingUserIs);
}
using (var audit = new AuditTestEntitiesWithAuditing())
{
var mt = audit.MasterTable.Find(mtOriginal.MasterTableId);
mt.TextField = "Goodbye";
audit.TestingUserIs = 4;
audit.SaveChanges();
// This test fails, the record is written with "Goodbye" but
// GetObjectStateEntries(EntityState.Added | EntityState.Modified) has no entries.
Assert.IsTrue(mt.LastModifiedBy == audit.TestingUserIs);
}
}
There's no other code. There's no weird turning off/on the entity tracking or anything. WYSIWYG.
What am I missing? How is the clearly modified object being missed by the check for Modified?
Annnd... answered my own question, of course, after talking to the duck.
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
This fixes everything. Thank you for your attention, I hope this helps someone else.

Capturing more helpful exception / diagnostic when using LINQ

Much as I love lambda expressions / LINQ in C# I seem to come up with this same kind of issue from time to time, and would like some pointers on how best to handle the following type of scenario.
The following code is purely for example and will hopefully demonstrate what I am trying to achieve:
public class Program
{
public class Demo
{
public string Name { get; set; }
public string Val { get; set; }
}
public class Test
{
public string Name { get; set; }
public int Val { get; set; }
}
public static void Main()
{
List<Demo> list = new List<Demo> {
new Demo { Name = "First", Val = "1" },
new Demo { Name = "Second", Val = "2" },
new Demo { Name = "Third" }
};
// Obviously this will blow up as my last item in 'list' has a null 'Val' property
try {
List<Test> results = list.Select(l => new Test { Name = l.Name, Val = int.Parse(l.Val) }).ToList();
}
catch(Exception x)
{
// I want some generic way of capturing a better fault here in my log - e.g. more details about the actual item in my collection would be useful!
Log.Error("Something broke", x, this);
}
}
}
Link to codepen example
The code above would likely log an exception such as:
Run-time exception (line 48): Value cannot be null. Parameter name:
String
Stack Trace:
[System.ArgumentNullException: Value cannot be null. Parameter name:
String] at Program.b__5(Demo l): line 48 at Program.Main():
line 48
So I know I can capture x.Message, stack trace etc, but my point is that when working with large / complex data sets (especially when data comes from another system in production environment etc.) it can be difficult to track down the actual piece of data / item in the collection which is causing the exception.
Is there a neat / generic (no pun intended) way of handling this type of scenario?
Think of it this way. If your weren't using Linq, you would have code similar to:
public List<Test> DemoToTestProjection(List<Demo> demos)
{
var projectedTests = new List<Test>();
for each (var demo in demos)
{
projectedTests.Add(new Test
{
Name = l.Name,
Val = int.Parse(l.Val)
});
}
return projectedTests;
}
public void DoSomething()
{
var demos = new List<Demo>({...});
try
{
var result = DemoToTestProjection(demos);
}
catch(Exception ex)
{
// How would you expect to get information about the specific
// `Demo` you were having an issue with here?
}
}
The answer is that you would need to put the try/catch around the location you wanted the detailed logging for.
So if you want that level of logging, you could do it by creating a different method to do the projection, and add your logging there:
var = list.Select(l => MapToTest(l))
.ToList();
public Test MapToTest(Demo demo)
{
try
{
Test test = new Test {...};
}
catch(Exception ex)
{
LogStuffAboutDemo(demo);
}
}
Or (uglier), inline:
var = list.Select(l =>
{
try {...}
catch{ LogStuffAboutDemo(l); }
})
.ToList();

Error when using WCF dataservice

I'm following this guide and I am getting an error. Can anyone help me?
The code for my datamodel is below
namespace Datalayer {
public class DataModel {
public DataModel()
{
using (btWholesaleDataContext db = new btWholesaleDataContext()) {
//! requires auth
var MACRequestList = from r in db.btRequests
select new Models.BT.Request {
ID = r.ID,
Date = r.DateTime,
StatusCode = 3,
Status = r.Status
};
MACRequests = MACRequestList.AsQueryable();
}
}
public IQueryable<Models.BT.Request> MACRequests { get; private set; }
}
}
The web service gives the error
Cannot access a disposed
object.Object name: 'DataContext
accessed after Dispose.'
When I access MACRequests
I have only posted the code I think is broken. If you want to see more just let me know.
Your data context is being disposed at the end of your constructor, at the end of the using { } block. However when you use the IQueryable MACRequests property, it needs that underlying context, which has since been disposed.
One possible way to handle this is to make your class IDisposable and dispose the context that way:
public class DataModel : IDisposable {
private btWholesaleDataContext wholesaleDataContext;
public DataModel()
{
wholesaleDataContext = new btWholesaleDataContext();
//! requires auth
var MACRequestList = ... ;
MACRequests = MACRequestList.AsQueryable();
}
public IQueryable<Models.BT.Request> MACRequests { get; private set; }
public void Dispose() {
if(wholesaleDataContext != null)
wholesaleDataContext.Dispose();
}
}
Then you have to make sure that DataModel is properly disposed by whatever uses it.
Another alternative is to make MACRequests the actual list of items instead of the IQueryable:
public class DataModel {
public DataModel()
{
using (btWholesaleDataContext db = new btWholesaleDataContext()) {
//! requires auth
var MACRequestList = ... ;
MACRequests = MACRequestList.ToList(); // ToList reads the records now, instead of later.
}
}
public List<Models.BT.Request> MACRequests { get; private set; }
}
I think its because you are using an IQueryable<>. Its lazily queries the service.
Use List<> instead so that it queries immediately
Or make "btWholesaleDataContext db" into a member variable
Queries to MACRequests are deferred - once you're out of the using block and your DataContext is disposed you're not going to be able to make the query you want.
You're creating the data context in a using block in the constructor of your DataModel...so by the time you access the MACRequests, the data context has been disposed.
Consider the following:
public class DataModel : IDisposable {
btWholesaleDataContext db = new btWholesaleDataContext();
public void Dispose()
{
btWholesaleDataContext.Dipose();
}
public IQueryable<Models.BT.Request> MACRequests {
get {
return from r in db.btRequests
select new Models.BT.Request {
ID = r.ID,
Date = r.DateTime,
StatusCode = 3,
Status = r.Status
};
}
}
}
Note that this usage will work:
using (var dm = new DataModel())
{
dm.MACRequests.ToArray();
}
but this will fail for the same reason as the original:
IQueryable<Models.BT.Request> requests = null;
using (var dm = new DataModel())
{
requests = dm.MACRequests;
}
// this will fail because the context is disposed by the time we force enumeration of the query
requests.ToArray();
...Alternatively, since WCF data services can't filter on projections, and thus all you can really do with the query
from r in db.btRequests
select new Models.BT.Request {
ID = r.ID,
Date = r.DateTime,
StatusCode = 3,
Status = r.Status
};
is execute it...
just consider changing your original code to return an array or a list instead of leaving it as a queryable.

Categories

Resources