Checking Against Multiple Entries in a Table - ASP.NET Core 3.1 and EFCore - asp.net-core

I'm trying to figure out the logic when checking against existing fields in a table with a single variable. Here is my entity class:
public class Metadata
{
public int Id { get; set; }
public string MachineName { get; set; }
public string MachineId { get; set; }
public string UserId { get; set; }
}
Lets say for example the user has 3 entries in the table and we check the current machine name with the machine names in the table. If the current machine name does not match the machine names in the table, I want to save a new entry in the table.
At the moment, my code jumps to the else and returns out of the method if the first machine name entry in the table is the same as the current machine name, which is not good as it might not be the case for the 2nd or 3rd entry. Also if the current machine name matches the 2nd entry, it will be ignored and saved since the first entry did not match.
Here is my method:
private void ValidateMetadata(string userId)
{
// get list of entities if they exist based upon the userId
var metaList = _metadata.where(x => x.UserId == userId).ToList();
if(metaList != null)
{
foreach (var m in metaList)
{
// check each machine name with current machine name
if (m.MachineName != GetMachineName())
{
// create new entry
var metadata = new { MachineName = GetMachineName(), MachineId = GetMachineId(), UserId = userId };
// save to db
_metadata.Save(metadata);
}
else
{
return;
}
}
}
else
{
// create new entry if there are none in the table
var metadata = new { MachineName = GetMachineName(), MachineId = GetMachineId(), UserId = userId };
// save to db
_metadata.Save(metadata);
}
}

Use Any
var hasHit = _metadata.Any(x => x.UserId == userId);
Example
var test = new List<string> {"one", "two"};
var result = test.Any(x => x.Equals("one"));
result will have the value true

Related

How i can get a single property insie my async code

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();

Can I dynamically reference multiple databases in a LINQPad script?

I have a database with a table describing multiple entities, with one column being the name of another database holding data for that entity. All entity databases are on the same SQL Server as the one listing them and all have an identical schema.
I know that I can use Ctrl-drag to add the additional databases to my script but what I actually want is to do this dynamically from the database name. Something like this.
var entities = ParentDatabase.EntityList
.Where(e => ??)
.Select(e => new { e.Id, e.DatabaseName });
var results = new List<ResultCarrier>();
foreach (var entity in entities)
{
results.AddRange(
GetDataContextFor(entity.DatabaseName).SomeTable
.Select(t => new ResultCarrier()
{
EntityId = e.Id,
Column1 = t.Column1,
Column2 = t.Column2,
...
}));
}
// further process combined results
Is this possible?
I see that the type of one of these databases is LINQPad.User.DatabaseNameTypes.TypedDataContext and wondered whether, as each database has the same schema, there might be a base class that I could use in some way to achieve this.
TypedDataContext is your base class, and you can just create a new instance of this and pass it the sql connection string.
You can find your current connection string using
this.Connection.ConnectionString.Dump();
For example, I use Integrated Security and I have a little routine that goes through all the database in my server and dumps out a table, so I use the following routine.
var databases = ExecuteQuery<String>("SELECT name FROM sys.databases").ToList();
foreach(var r in databases)
{
switch (r)
{
case "master" :
case "tempdb" :
case "model" :
case "msdb" :
break;
default:
try
{
string newConnectionString = String.Format("Data Source={0};Integrated Security=SSPI;Initial Catalog={1};app=LINQPad", this.Connection.DataSource, r);
var dc = new TypedDataContext(newConnectionString);
dc.Table.Dump(r);
}
catch (Exception ex)
{
ex.Message.Dump(r);
}
break;
}
}
#sgmoore's answer got me on the right track. I hadn't come across ExecuteQuery in LINQPad and I was able to use it to achieve what I wanted. Below is the code I ended up with. I will now extend it to further retrieve data from a service and join it to databaseLocations to give the final result I'm after.
void Main()
{
var organisations = ExecuteQuery<OrganisationCarrier>(#"
SELECT do.GroupId [Id], o.sOrganisationName [Name], o.sConnectDatabase [Database]
FROM dbo.Organisation o
INNER JOIN dynamix.Organisations do ON o.liOrgID = do.OrganisationID
INNER JOIN dynamix.OrganisationFeatures oft ON do.OrganisationKey = oft.OrganisationKey
INNER JOIN dynamix.Features ft ON oft.FeatureKey = ft.FeatureKey
WHERE ft.FeatureName = 'LightningLocations'").ToList();
var databaseLocations = new List<LocationExtract>();
foreach (var organisation in organisations)
{
this.Connection.ConnectionString = $"Data Source={this.Connection.DataSource};Integrated Security=SSPI;Initial Catalog={organisation.Database};app=LINQPad";
databaseLocations.AddRange(ExecuteQuery<LocationCarrier>(#"
SELECT dml.DmxLocationId [Id], ml.sLocationName [Name], ml.bDeleted [IsDeleted]
FROM dynamix.MapLocations dml
INNER JOIN dbo.MapLocations ml ON dml.FmLocationId = ml.liLocationID")
.Select(l => new LocationExtract(organisation.Id, l.Id, l.Name, l.IsDeleted)));
}
databaseLocations.Dump();
}
class OrganisationCarrier
{
public long Id { get; set; }
public string Name { get; set; }
public string Database { get; set; }
}
class LocationCarrier
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
class LocationExtract
{
public long OrganisationId { get; }
public long LocationId { get; }
public string Name { get; }
public bool IsDeleted { get; }
public LocationExtract(long organisationId, long locationId, string name, bool isDeleted)
{
OrganisationId = organisationId;
LocationId = locationId;
Name = name;
IsDeleted = isDeleted;
}
}

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.

How to add object to list (or other collection) that is inside another object?

I have this:
public class Room
{
public string RoomID { get; set; }
public string RoomName { get; set; }
public List<User> UsersInRoom { get; set; }
//public IDuplexClient RoomCallbackChannel { get; set; }
}
As you can see there is an List that contains users in room.
But to actually make it work I need to add users, so I did this:
User usr;
Room roomi;
if (userNameTest == null)
{
lock (_clients)
{
usr = new User { UserID = sessionID, CallbackChannel = client, UserName = userName };
roomi = new Room();
roomi.RoomID = sessionID;
roomi.RoomName = room;
roomi.UsersInRoom.Add(usr);
//roomi.UsersInRoom.Add(usr);
_rooms.Add(roomi);
//_clients.Add(usr);
}
}
and at line:
roomi.UsersInRoom.Add(usr);
I get NullReferenceException.
What's going on ?
You haven't created a list - so roomi.UsersInRoom is null.
You could fix this by changing the line to:
roomi.UsersInRoom = new List<User>();
roomi.UsersInRoom.Add(usr);
Note that if you're using C# 3 or higher, you can use object and collection initializers to make all of this code simpler. Additionally, you could potentially make UsersInRoom a read-only property, setting the value to a new list within a Room constructor (or a variable initializer).
Unfortunately I don't have time to show all of this right now, but something like this for the object/collection initializers:
_rooms.Add(new Room {
RoomId = sessionId,
RoomName = room,
UsersInRoom = new List<User> {
new User {
UserID = sessionID,
CallbackChannel = client,
UserName = userName
}
}
});
If you change it so that UsersInRoom is initialized by Room itself, this changes to:
_rooms.Add(new Room {
RoomId = sessionId,
RoomName = room,
UsersInRoom = {
new User {
UserID = sessionID,
CallbackChannel = client,
UserName = userName
}
}
});
List<T>, is a reference type. The default value for a reference type is null.
You need to initialise the List before you start adding items to it.
roomi.UsersInRoom=new List<User>();
Have you made sure the List is initialized? List is an object so you have to 'create' it, like so:
List<User> nameOfList = new List<User>();