prevent duplicate users in online users list signalr - asp.net-mvc

I am working on online users list. My code is:
public class User
{
public string id;
public string name;
public string dpExtension;
}
public class OnlineUsers : Hub
{
private Entities db = new Entities();
public static ConcurrentDictionary<string, User> users = new ConcurrentDictionary<string, User>();
public override System.Threading.Tasks.Task OnConnected()
{
User u = new User();
u.id = "visitor";
u.name = "Visitor";
u.dpExtension = "";
if (Context.User.Identity.IsAuthenticated)
{
u.id = Context.User.Identity.GetUserId();
var da = db.AspNetUsers.Find(u.id);
u.name = da.Email;
u.dpExtension = da.dpExtension;
}
User abc;
var data = users.TryGetValue(Context.ConnectionId, out abc);
if (!data)
{
users.TryAdd(Context.ConnectionId, u);
}
Clients.All.showConnected(users);
return base.OnConnected();
}
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
User abc;
users.TryRemove(Context.ConnectionId, out abc);
Clients.All.showConnected(users);
return base.OnDisconnected(stopCalled);
}
}
Now if the user has opened one browser tab, it is shown once in list and that's fine. But if user opens two tabs it is shown in list twice. How can I show one user only once in list?

You need to map ConnectionIds to one User. You can read a lot in this article - http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections .
I've added a few things in your code, you could see what was going on in this article:
public class User
{
public string dpExtension;
public string id;
public string name;
}
public class Map
{
public string UserId { get; set; }
public User User { get; set; }
public List<string> Connections { get; set; }
}
public class OnlineUsers : Hub
{
public static readonly List<Map> maps = new List<Map>();
private readonly Entities db = new Entities();
public override Task OnConnected()
{
var user = new User { id = "visitor", name = "Visitor", dpExtension = "" };
if (Context.User.Identity.IsAuthenticated)
{
user.id = Context.User.Identity.GetUserId();
var da = db.AspNetUsers.Find(user.id);
user.name = da.Email;
user.dpExtension = da.dpExtension;
}
Map data = maps.FirstOrDefault(t => t.UserId == user.id);
if (data == null)
{
maps.Add(new Map() {
UserId = user.id,
User = user,
Connections = new List<string>() { Context.ConnectionId }
});
}
else
{
data.Connections.Add(Context.ConnectionId);
}
Clients.All.showConnected(maps.Select(m => m.User));
return base.OnConnected();
}
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
var map = maps.FirstOrDefault(t => t.Connections.Contains(Context.ConnectionId));
if (map != null)
{
map.Connections.Remove(Context.ConnectionId);
if (map.Connections.Count <= 0)
{
maps.Remove(map);
}
}
Clients.All.showConnected(maps.Select(m => m.User));
return base.OnDisconnected(stopCalled);
}
}

Related

How to add entity referencing two other entities to database to database in ASP.NET CORE

please I have these entities to be sent to database
MatchingChildtoApplicant
{
[Key]
public int MatchId { get; set; }
public int ChildId {get; set;}
public Child Children { get; set; }
public int ApplyId {get; set;}
public ICollection<Application> Applications { get; set; }
public string MatchingBy { get; set; }
}
this is the post request to send to the database
[HttpPost]
public async Task<ActionResult<MatchingChildtoApplicant>> AddNewMatching( int applyid, int chd, [FromForm]MatchingChildtoApplicant matchingChildtoApplicant)
{
//getting the application to match
var gettheapplicantion = await _unitOfWork.ApplicationRepository.GetApplicantByIdAsync(applyid);
if(gettheapplicantion == null){
return NotFound("The Application to match was not found");
}
var appid = gettheapplicantion.ApplyId;
//getting the child to match with applicant
var childtomatch = await _unitOfWork.ChildRepository.GetChildByIdAsync(chd);
if(childtomatch == null){
return NotFound("The child to match was not found");
}
var childtoid = childtomatch.Cld;
//getting the login user doing the matching
var userdoingmatch = await _userManager.FindByEmailFromClaimsPrinciple(User);
if (userdoingmatch == null)
{
return NotFound("The user doing the matching details is missing");
}
var nameofuser = userdoingmatch.DisplayName;
//declaring new instance of MatchingChildtoApplicant
var newmatching = new MatchingChildtoApplicant
{
ApplyId = appid,
ChildId = childtoid,
MatchingBy = nameofuser,
Comment = matchingChildtoApplicant.Comment,
TheApplicationAcepted = matchingChildtoApplicant.TheApplicationAcepted,
MatchedDate = matchingChildtoApplicant.MatchedDate,
};
This is where I get the error sending to the database
// var result = await _context.MatchingChildtoApplicant.AddAsync(matchingChildtoApplicant);
// var result = await _context.MatchingChildtoApplicant.AddAsync(matchingChildtoApplicant);
// gettheapplicantion.MatchingChildtoApplicants.Children.Add(matchingChildtoApplicant);
await _context.SaveChangesAsync();
return new MatchingChildtoApplicant
{
ApplyId = appid,
ChildId = childtoid,
MatchingBy = nameofuser,
Comment = matchingChildtoApplicant.Comment,
TheApplicationAcepted = matchingChildtoApplicant.TheApplicationAcepted,
MatchedDate = matchingChildtoApplicant.MatchedDate,
};
The error I get is this
The type arguments for method 'ValueTransformerConfigurationExtensions.Add(List, Expression<Func<TValue, TValue>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. [API]

How to create DbModificationClause with CASE WHEN .... THEN

all
I was create IDbCommandTreeInterceptor and got the problem: EF provider wrong sql generation. As a result, I want to get this SQL
UPDATE [dbo].[Devices] SET [DeletedDate] = CASE
WHEN [DeletedDate] IS NULL THEN GETUTCDATE()
ELSE [DeletedDate] END
Code for testing. Interseptor class for fake delettion.
public class SoftDeleteInterseptor : IDbCommandTreeInterceptor
{
private const string DELETED_DATE_COLUMN = "DeletedDate";
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace)
{
return;
}
var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree;
if (deleteCommand != null)
{
interceptionContext.Result = HandleDeleteCommand(deleteCommand);
return;
}
}
private DbCommandTree HandleDeleteCommand(DbDeleteCommandTree deleteCommand)
{
if (!IsPropertyExists(deleteCommand, DELETED_DATE_COLUMN))
{
return deleteCommand;
}
var deletedProperty = DbExpressionBuilder.Property(
DbExpressionBuilder.Variable(deleteCommand.Target.VariableType, deleteCommand.Target.VariableName),
DELETED_DATE_COLUMN
);
var caseValue = DbExpressionBuilder.Case(
new DbExpression[] { deletedProperty.IsNull() },
new DbExpression[] { EdmFunctions.CurrentUtcDateTime() },
deletedProperty);
var setClauses = new List<DbModificationClause> { DbExpressionBuilder.SetClause(deletedProperty, caseValue) };
return new DbUpdateCommandTree(
deleteCommand.MetadataWorkspace,
deleteCommand.DataSpace,
deleteCommand.Target,
deleteCommand.Predicate,
setClauses.AsReadOnly(), null);
}
private bool IsPropertyExists(DbModificationCommandTree command, string property)
{
var table = (EntityType)command.Target.VariableType.EdmType;
return table.Properties.Any(p => p.Name == property);
}
}
Create configuration class for register DbInterseptor.
public class CustomDbConfiguration : DbConfiguration
{
public CustomDbConfiguration()
{
AddInterceptor(new SoftDeleteInterseptor());
}
}
public partial class CustomDbContext : DbContext
{
static IDCompleteDbContext()
{
DbConfiguration.SetConfiguration(new CustomDbConfiguration());
}
public virtual DbSet<CommandEntity> CommandEntities { get; set; }
}
public class CommandEntity
{
public int Id {get; set;}
public DateTime? DeletedDate {get; set;}
public string Name {get; set;}
}
When delete entity, entity did not deleted
var context = new CustomDbContext();
var entity = context.CommandEntities.First();
context.CommandEntities.Remove(entity);
context.SubmitChanges();
Did not work. EF provider generate wrong SQL: UPDATE [dbo].[Devices] SET [DeletedDate] = [DeletedDate] IS NULL#0[DeletedDate] WHERE ([Id] = #1) #0: '01.05.2018 7:45:22' (Type = DateTime2) #1: '20' (Type = Int32)

BreezeSharp Attach Property key not found

I'm implementing an application with Breezesharp. I ran into a issue when insert the entity in the EntityManager. The error is:
There are no KeyProperties yet defined on EntityType: 'TransportReceipt:#Business.DomainModels'
I already faced this error with my first entity type "Customer" and implement a mismatching approach as suggested here. In that case I made the get operation against my WebApi with success. But now I'm creating the TransportReceipt entity inside my application.
Mapping mismatch fix
public static class ExtendMap
{
private static bool? executed;
public static void Execute(MetadataStore metadataStore) {
if (ExtendMap.executed == true)
{
return;
}
var customerBuilder = new EntityTypeBuilder<Customer>(metadataStore);
customerBuilder.DataProperty(t => t.id).IsPartOfKey().IsAutoIncrementing();
var transportReceiptBuilder = new EntityTypeBuilder<TransportReceipt>(metadataStore);
transportReceiptBuilder.DataProperty(t => t.id).IsPartOfKey().IsAutoIncrementing();
var transportReceiptAttachmentBuilder = new EntityTypeBuilder<TransportReceiptAttachment>(metadataStore);
transportReceiptAttachmentBuilder.DataProperty(t => t.id).IsPartOfKey().IsAutoIncrementing();
var uploadedFileBuilder = new EntityTypeBuilder<UploadedFile>(metadataStore);
uploadedFileBuilder.DataProperty(t => t.id).IsPartOfKey().IsAutoIncrementing();
ExtendMap.executed = true;
}
}
My base dataservice core code
public abstract class SimpleBaseDataService
{
public static string Metadata { get; protected set; }
public static MetadataStore MetadataStore { get; protected set; }
public string EntityName { get; protected set; }
public string EntityResourceName { get; protected set; }
public EntityManager EntityManager { get; set; }
public string DefaultTargetMethod { get; protected set; }
static SimpleBaseDataService()
{
try
{
var metadata = GetMetadata();
metadata.Wait();
Metadata = metadata.Result;
MetadataStore = BuildMetadataStore();
}
catch (Exception ex)
{
var b = 0;
}
}
public SimpleBaseDataService(Type entityType, string resourceName, string targetMethod = null)
{
var modelType = typeof(Customer);
Configuration.Instance.ProbeAssemblies(ConstantsFactory.BusinessAssembly);
try
{
this.EntityName = entityType.FullName;
this.EntityResourceName = resourceName;
this.DefaultTargetMethod = (string.IsNullOrWhiteSpace(targetMethod) ? "GetAllMobile" : targetMethod);
var dataService = new DataService($"{ConstantsFactory.Get.BreezeHostUrl}{this.EntityResourceName}", new CustomHttpClient());
dataService.HasServerMetadata = false;
this.EntityManager = new EntityManager(dataService, SimpleBaseDataService.MetadataStore);
this.EntityManager.MetadataStore.AllowedMetadataMismatchTypes = MetadataMismatchTypes.AllAllowable;
// Attach an anonymous handler to the MetadataMismatch event
this.EntityManager.MetadataStore.MetadataMismatch += (s, e) =>
{
// Log the mismatch
var message = string.Format("{0} : Type = {1}, Property = {2}, Allow = {3}",
e.MetadataMismatchType, e.StructuralTypeName, e.PropertyName, e.Allow);
// Disallow missing navigation properties on the TodoItem entity type
if (e.MetadataMismatchType == MetadataMismatchTypes.MissingCLRNavigationProperty &&
e.StructuralTypeName.StartsWith("TodoItem"))
{
e.Allow = false;
}
};
}
catch (Exception ex)
{
var b = 0;
}
}
}
This is who I'm trying to add the new entity
//DataService snippet
public void AttachEntity(T entity)
{
this.EntityManager.AttachEntity(entity, EntityState.Added);
}
//Business
this.TransportReceipt = new TransportReceipt { id = Guid.NewGuid(), date = DateTime.Now, customerId = Customer.id/*, customer = this.Customer*/ };
this.Attachments = new List<TransportReceiptAttachment>();
this.TransportReceipt.attachments = this.Attachments;
TransportReceiptDataService.AttachEntity(this.TransportReceipt);
When I try to add add the entity to the EntityManager, I can see the custom mapping for all my entity classes.
So my question is what I'm doing wrong.
Ok. That was weird.
I changed the mapping for a new fake int property and works. I'll test the entire save flow soon and I'll share the result here.
Update
I moved on and start removing Breezesharp. The Breezesharp project is no up-to-date and doesn't have good integration with Xamarin. I'll appreciate any comment with your experience.

uCommerce - add dynamic property to order line

I have hit a problem building a uCommerce site based on top of the demo razor store available http://thesitedoctor.co.uk/portfolio/avenue-clothingcom/
The demo uses servicestack and the ucommerceapi for its basket functions.
I am trying to add a dynamic property to the basket (on an order line) at the point where the user clicks buy. I traced through the productpage.js file and amended the code to add a new property ('message'):
function (data) {
var variant = data.Variant;
$.uCommerce.addToBasket(
{
sku: variant.Sku,
variantSku: variant.VariantSku,
quantity: qty,
message: $('#personalisedMessage').val()
},
function () {
updateCartTotals(addToCartButton);
}
);
});
using firebug, i checked the data that is being posted
addToExistingLine: true
message: "this is a message"
quantity:"1"
sku: "Product (options: none)"
variantSku:""
Posting this does not cause an error, but I cannot tell if it has worked either - I cannot find it in the database, assuming that it would be stored in OrderProperty table. In this scenario, I am 'buying' a product with no variations.
Any help is greatly appreciated with this.
Out of the box you can't add order/line item properties via the API like that. The API payload that you've added to is specified although valid JSON won't get interpreted/used by the API.
Instead what you'll need to do is add your own method to the API. To do this you'll need to implement a service from IUCommerceApiService and then you can do what you need. I've created an example (untested) below and will get it added to the demo store as I think it's a useful bit of functionality to have.
public class AddOrderLineProperty
{
public int? OrderLineId { get; set; }
public string Sku { get; set; }
public string VariantSku { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
public class AddOrderLinePropertyResponse : IHasResponseStatus
{
public AddOrderLinePropertyResponse() { }
public AddOrderLinePropertyResponse(UCommerce.EntitiesV2.OrderLine line)
{
if (line == null)
{
UpdatedLine = new LineItem();
return;
}
var currency = SiteContext.Current.CatalogContext.CurrentCatalog.PriceGroup.Currency;
var lineTotal = new Money(line.Total.Value, currency);
UpdatedLine = new LineItem()
{
OrderLineId = line.OrderLineId,
Quantity = line.Quantity,
Sku = line.Sku,
VariantSku = line.VariantSku,
Price = line.Price,
ProductName = line.ProductName,
Total = line.Total,
FormattedTotal = lineTotal.ToString(),
UnitDiscount = line.UnitDiscount,
VAT = line.VAT,
VATRate = line.VATRate
};
}
public ResponseStatus ResponseStatus { get; set; }
public LineItem UpdatedLine { get; set; }
}
public class AddOrderLinePropertyService : ServiceBase<AddOrderLineProperty>, IUCommerceApiService
{
protected override object Run(AddOrderLineProperty request)
{
var orderLineId = request.OrderLineId;
var sku = request.Sku;
var variantSku = request.VariantSku;
var orderLine = findOrderLine(orderLineId, sku, variantSku);
addPropertyToOrderLine(orderLine, request.Key, request.Value);
TransactionLibrary.ExecuteBasketPipeline();
var newLine = findOrderLine(orderLineId, sku, variantSku);
return new AddOrderLinePropertyResponse(newLine);
}
private void addPropertyToOrderLine(OrderLine orderLine, string key, string value)
{
if (orderLine == null)
return;
orderLine[key] = value;
orderLine.Save();
}
private static OrderLine findOrderLine(int? orderLineId, string sku, string variantSku)
{
return orderLineId.HasValue
? getOrderLineByOrderLineId(orderLineId)
: getOrderLineBySku(sku, variantSku);
}
private static OrderLine getOrderLineBySku(string sku, string variantSku)
{
return String.IsNullOrWhiteSpace(variantSku)
? getOrderLines().FirstOrDefault(l => (l.Sku == sku))
: getOrderLines().FirstOrDefault(l => (l.Sku == sku && l.VariantSku == variantSku));
}
private static OrderLine getOrderLineByOrderLineId(int? orderLineId)
{
return getOrderLines().FirstOrDefault(l => l.OrderLineId == orderLineId);
}
private static ICollection<OrderLine> getOrderLines()
{
return TransactionLibrary.GetBasket().PurchaseOrder.OrderLines;
}
}
You'll need to add the new method to uCommerce.jQuery.js as well something like this:
addOrderLineProperty: function (options, onSuccess, onError) {
var defaults = {
orderLineId: 0
};
var extendedOptions = $.extend(defaults, options);
callServiceStack({ AddOrderLineProperty: extendedOptions }, onSuccess, onError);
}
Let me know if you have any issues using it.
Tim

.Net Reactive Extensions, how to implement simple chat that will not lose messages

I'm implementing a simple chat in .NET using Rx on the basis of this example:
https://blogs.claritycon.com/blog/2011/04/roll-your-own-mvc-3-long-polling-chat-site/
There's a method that, using LongPolling waits for new messages to come:
public static void CheckForMessagesAsync(Action<List<MessageInfo>> onMessages)
{
var queued = ThreadPool.QueueUserWorkItem(new WaitCallback(parm =>
{
var msgs = new List<MessageInfo>();
var wait = new AutoResetEvent(false);
using (var subscriber = _messages.Subscribe(msg =>
{
msgs.Add(msg);
wait.Set();
}))
{
// Wait for the max seconds for a new msg
wait.WaitOne(TimeSpan.FromSeconds(MaxWaitSeconds));
}
((Action<List<MessageInfo>>)parm)(msgs);
}), onMessages);
if (!queued)
onMessages(new List<MessageInfo>());
}
Using this method I lose messages appearing between disconnecting and disposing the observer and re-connecting.
How to correctly implement this mechanism to not lose those messages?
I found a solution. I do not know whether it is the most beautiful in the world, but it works.
I created private property, so each user can have multiple sessions:
private ConcurrentDictionary<long, ChatServerUserSessions> _chatServerUserSessionInfoDictionary = new ConcurrentDictionary<long, ChatServerUserSessions>();
and session class:
public class ChatServerUserSessions
{
public long UserId { get; set; }
public string UserName { get; set; }
public ConcurrentDictionary<string, ChatServerUserSessionInfo> Sessions { get; set; }
public object Lock { get; set; }
}
and for each session I created class:
public class ChatServerUserSessionInfo : IObservable<ChatServerRoomActivityInfoBase>, IDisposable
{
public string SessionId { get; set; }
public List<long> SubscribedRoomIds { get; set; }
public DateTime SubscriptionTicketInvalidationDate { get; set; }
public Queue<ChatServerRoomActivityInfoBase> MessagesQueue { get; set; }
private IDisposable subscription;
private List<IObserver<ChatServerRoomActivityInfoBase>> observers;
private ChatServerUserSessions parentUserSessions;
public ChatServerUserSessionInfo(string sessionId, DateTime subscriptionTicketInvalidationDate, Subject<ChatServerRoomActivityInfoBase> chatServerRoomActivity, ChatServerUserSessions parentUserSessions)
{
this.SessionId = sessionId;
this.SubscribedRoomIds = new List<long>();
this.SubscriptionTicketInvalidationDate = subscriptionTicketInvalidationDate;
this.MessagesQueue = new Queue<ChatServerRoomActivityInfoBase>();
this.parentUserSessions = parentUserSessions;
subscription = chatServerRoomActivity.Subscribe(activity =>
{
lock (parentUserSessions.Lock)
{
if (this.SubscribedRoomIds.Contains(activity.RoomId))
{
this.MessagesQueue.Enqueue(activity);
foreach (var observer in observers)
{
observer.OnNext(activity);
}
}
}
});
observers = new List<IObserver<ChatServerRoomActivityInfoBase>>();
}
~ChatServerUserSessionInfo()
{
Dispose();
}
public void Dispose()
{
if (subscription != null)
{
subscription.Dispose();
subscription = null;
}
this.observers = null;
GC.SuppressFinalize(this);
}
public IDisposable Subscribe(IObserver<ChatServerRoomActivityInfoBase> observer)
{
lock (parentUserSessions.Lock)
{
this.observers.Add(observer);
return (IDisposable)new Subscription(this, observer);
}
}
private void Unsubscribe(IObserver<ChatServerRoomActivityInfoBase> observer)
{
lock (parentUserSessions.Lock)
{
if (this.observers == null)
{
return;
}
this.observers.Remove(observer);
}
}
private class Subscription : IDisposable
{
private ChatServerUserSessionInfo subject;
private IObserver<ChatServerRoomActivityInfoBase> observer;
public Subscription(ChatServerUserSessionInfo subject, IObserver<ChatServerRoomActivityInfoBase> observer)
{
this.subject = subject;
this.observer = observer;
}
public void Dispose()
{
IObserver<ChatServerRoomActivityInfoBase> observer = Interlocked.Exchange<IObserver<ChatServerRoomActivityInfoBase>>(ref this.observer, (IObserver<ChatServerRoomActivityInfoBase>)null);
if (observer == null)
{
return;
}
this.subject.Unsubscribe(observer);
this.subject = (ChatServerUserSessionInfo)null;
}
}
}
Each user session has own MessageQueue and is subscribed to global chat room activity subject. ChatRoomActivityMessages are persisted for each session individualy. Here is method for retrieving messages:
public void CheckForChatRoomsActivityAsync(long userId, string userName, Action<List<ChatServerRoomActivityInfoBase>> onChatRoomActivity)
{
var sessionId = GetCurrentSessionId();
var chatServerUserSessions = GetChatServerUserSessions(userId, userName);
lock (chatServerUserSessions.Lock)
{
var userSession = GetChatServerUserSessionInfo(sessionId, chatServerUserSessions);
ProlongSubscriptions(userSession);
if (userSession.MessagesQueue.Count > 0)
{
var activities = new List<ChatServerRoomActivityInfoBase>();
while (userSession.MessagesQueue.Count > 0)
{
activities.Add(userSession.MessagesQueue.Dequeue());
}
onChatRoomActivity(activities);
}
else
{
var queued = ThreadPool.QueueUserWorkItem(new WaitCallback(parm =>
{
var activities = new List<ChatServerRoomActivityInfoBase>();
var wait = new AutoResetEvent(false);
using (var subscriber = userSession.Subscribe(activity =>
{
lock (chatServerUserSessions.Lock)
{
activities.Add(activity);
userSession.MessagesQueue.Dequeue();
wait.Set();
}
}))
{
wait.WaitOne(TimeSpan.FromSeconds(CheckForActivityMaxWaitSeconds));
}
((Action<List<ChatServerRoomActivityInfoBase>>)parm)(activities);
}), onChatRoomActivity);
if (!queued)
{
onChatRoomActivity(new List<ChatServerRoomActivityInfoBase>());
}
}
}
}

Resources