Entity Framework one-to-many relation, updating the one side - c#

I have a set of endpoints which may or may not belong to an organization and an organization can have many or zeros endpoints in it. I want to remove a specific endpoint from an organization. I have written this method but I can't figure out why it is not working:
public void DeleteEndpointFromOrganization(Guid id, int organiationId)
{
using (var db = new Context())
{
var organization = GetOrganizationById(organiationId);
var endpointSystem = organization.EndpointSystems.FirstOrDefault(e => e.Id == id);
if (endpointSystem != null)
{
organization.EndpointSystems.Remove(endpointSystem);
}
db.Organizations.AddOrUpdate(organization);
db.SaveChanges();
}
}

Related

How to ensure that the WCF service will not communicate to the database in parallel

I have a WCF service, which should return objects from database, but each entity should be returned only once. I would like to avoid scenario, where many clients are using service, and they can get same Request entity.
public Request GetChangeRequest()
{
using (var context = new Data.Core.Context())
{
var request = context.Requests
.Where(r => r.IsAvaible)
.FirstOrDefault();
if (request != null)
{
request.IsAvaible = false;
context.SaveChanges();
}
return request;
}
}
I am really wondering if there is a reason to give additional security like locking database. To do this I have managed something like this:
public Request GetChangeRequest()
{
using (var context = new Data.Core.Context())
{
context.OnLock<Request>(context.GetTableName<Request>(), () =>
{
var request = context.Requests
.Where(r => r.IsAvaible)
.FirstOrDefault();
if (request != null)
{
request.IsAvaible = false;
context.SaveChanges();
}
return request;
});
}
}
public static class DBContextExtensions
{
public static string GetTableName<T>(this DbContext context) where T : class
{
var type = typeof(T);
var entityName = (context as System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext.CreateObjectSet<T>().EntitySet.Name;
var tableAttribute = type.GetCustomAttributes(false).OfType<System.ComponentModel.DataAnnotations.Schema.TableAttribute>().FirstOrDefault();
return tableAttribute == null ? entityName : tableAttribute.Name;
}
public static T OnLock<T>(this DbContext context, string tableName, Func<T> action)
{
T res;
using (DbContextTransaction scope = context.Database.BeginTransaction())
{
context.Database.ExecuteSqlCommand($"SELECT TOP 1 Id FROM {tableName} WITH (TABLOCKX, HOLDLOCK)");
res = action.Invoke();
scope.Commit();
}
return res;
}
}
I couldn't reproduce scenerio, when two request entity are returned to two different clients. Does that mean, that WCF service performs requests sequentially?
Instead of implementing a locking mechanism by yourself, one possible solution would be running the service as a singleton and not allowing parallel requests.
You can achieve this by setting your WCF Service properties InstanceContextMode and ConcurrencyMode to Single.
For more information about Sessions, Instancing, and Concurrency see here.
You should be able to use concurrency checking to make sure that only one entity is returned, without blocking the WCF to one query at a time.
You need a special field in you entity class with an attribute [Timestamp] and then catch the DbUpdateConcurrencyException when saving, which will let you know that someone else has already returned that record, so you should get another one.
public class Request
{
...
[Timestamp]
public byte[] RowVersion { get; set; }
}
public Request GetChangeRequest()
{
using (var context = new Data.Core.Context())
{
while (true)
{
try
{
var request = context.Requests
.Where(r => r.IsAvaible)
.FirstOrDefault();
request.IsAvaible = false;
context.SaveChanges();
return request;
}
catch (DbUpdateConcurrencyException)
{
}
}
}
}
See here for more details

Entity Framework 6 - update fails (disconnected scenario)

I'm trying to update an instance with Entity Framework 6. I suppose it's a disconnected scenario. And the update fails - no errors but the properties I change do not save in DB.
Method in controller
var managers = _iManagerRepository.Managers.ToList();
var manager = managers.FirstOrDefault(m => m.Id == currentUserId);
if (manager != null)
{
manager.ContactDetail.FirstName = withTokenDto.managerInfoModel.FirstName;
manager.ContactDetail.SecondName = withTokenDto.managerInfoModel.SecondName;
manager.ContactDetail.LastName = withTokenDto.managerInfoModel.LastName;
_iManagerRepository.UpdateManager(manager);
return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK));
}
Method in repository:
public void UpdateManager(Manager manager)
{
using (LightCRMEntities context = new LightCRMEntities())
{
context.Managers.Attach(manager);
context.Entry<Manager>(manager).State = EntityState.Modified;
context.SaveChanges();
}
}

How query by global secondary index with DynamoDBContext

i have this class with this attributes:
ContactId -> HashKey
Email -> Global Seconday Index Hash Key
CreatedAt -> Property
Actually, i have this method, but throw me an exception because "Email" property is not a HashKey. How can i get an item using a secondary index and DynamoDBContext class? I found some examples, but all of them uses a low-level api.
public Contact Get(string email)
{
Contact entity = null;
using (var context = new DynamoDBContext(new AmazonDynamoDBClient()))
{
entity = context.Load(new Contact() { Email = email });
}
return entity;
}
EDIT: Actually, this code work for me. It assume that "email" is unique:
public Contact Get(string email)
{
Contact entity = null;
using (var context = new DynamoDBContext(new AmazonDynamoDBClient()))
{
entity = context.Query<Contact>(email, new DynamoDBOperationConfig {IndexName = "Email-index"}).FirstOrDefault();
}
return entity;
}
If using .NET core or .NET 5 and higher with async API calls, the following should work. Note the QueryAsync method and GetRemainingAsync().Result which returns the result of the completed query task.
public Contact Get(string email)
{
Contact entity = null;
using (var context = new DynamoDBContext(new AmazonDynamoDBClient()))
{
entity = context.QueryAsync<Contact>(email,
new DynamoDBOperationConfig {IndexName = "Email-index"})
.GetRemainingAsync().Result.FirstOrDefault();
}
return entity;
}

How to implement a many to many relationship in ASP.NET using SQL Server

Basically I am trying to insert a userId value and a RoleId value in an intermediate table in SQL Server 2010. The problem is that the code is not reaching the table and it stays empty while, using breakpoints, I can see the values to allocate the role are correct. I am using ASP.NET MVC 5 as a school project where I am trying to give users roles for different privileges in the website.
The method to set the roles is in the business layer:
public void RegisterUser(CommonLayer.User User, string ConfirmPassword)
{
CommonLayer.User Existing = this.GetUser(User.UserEmail);
BuisnessLayer.Roles roles = new BuisnessLayer.Roles();
if (Existing == null)
{
if (User.UserPassword.Equals(ConfirmPassword))
{
User.UserId = Guid.NewGuid();
User.UserPassword = HashSHA512String(User.UserPassword, User.UserId.ToString());
User.UserTypeId = (1);
this.AddUser(User);
roles.AddUserRole("USR",User.UserId);
}
}
}
AddUserRole seems to be the problem because it is not inserting in the table the method for AddUserRole in the business logic is:
public void AddUserRole(string RoleCode, Guid UserId)
{
DataLayer.DARoles dar = new DataLayer.DARoles(this.entities);
DataLayer.DAUsers dau = new DataLayer.DAUsers(this.entities);
CommonLayer.User User = dau.GetUser(UserId);
CommonLayer.Role Role = dar.GetRole(RoleCode);
dar.AllocateUserRole(User, Role);
}
Here are the codes for GetUser and GetRole in the data layer:
public CommonLayer.User GetUser(Guid UserId)
{
return this.Entities.Users.SingleOrDefault(p => p.UserId.Equals(UserId));
}
public CommonLayer.Role GetRole(string RoleCode)
{
return this.Entities.Roles.SingleOrDefault(p => p.RoleId == RoleCode);
}
And, here is AllocateUserRole in the data layer:
public void AllocateUserRole(CommonLayer.User User, CommonLayer.Role Role)
{
User.Roles.Add(Role);
this.Entities.SaveChanges();
}
The problem is that you are comparing RoleId with RoleCode.
You send RoleCode to GetRole method :
public void AddUserRole(string RoleCode, Guid UserId)
{
...
CommonLayer.Role Role = dar.GetRole(RoleCode);
...
}
However your GetRole method compares it with RoleId
public CommonLayer.Role GetRole(string RoleCode)
{
return this.Entities.Roles.SingleOrDefault(p => p.RoleId == RoleCode);
}
UPDATE :
I realized that you forget to add your user to DbContext , which is this.Entities in your context.
public void AllocateUserRole(CommonLayer.User User, CommonLayer.Role Role)
{
User.Roles.Add(Role); // this does nothing to your Database.
//You should use this to add user to your context ( db )
this.Entities.Users.Add(User);
// or this if you want to update your user in your context ( db ).
this.Entities.Set<CommonLayer.User>().Attach(User);
this.Entities.Entry(User).State = EntityState.Modified;
this.Entities.SaveChanges();
}

How to add entity in a table that have many to many relation ship using entity framework?

I am facing a logic problem in asp.net web API. I have registration method for adding users where each user belongs to an account the relation between user and account is many to many "one account can add many users" and "user can exist in many accounts" so i have 3 tables. As a result when i till entity framework to add user it just add the information in table user i need also to make add account id in the 3rd table(result of many to many).
UserDetail is a view in the database contain both account id and user i check if user exists or not if not i add user but unfortunately without account id.
UserDetail newUser = db.UserDetails.Where(newuser => newuser.UserMail == user.UserMail && newuser.AccId == accID).SingleOrDefault();
if (newUser == null)
{
/*string passPhrase = System.Configuration.ConfigurationSettings.AppSettings["PassPhrase"];
string random_token = Crypto.RandomToken().ToString();
string EnToken = Crypto.Encrypt(random_token, passPhrase);
user.ConfirmToken = random_token;*/
db.Users.Add(user);
try
{
db.SaveChanges();
//Mail.SendMail(EnToken,user.UserMail);
return Request.CreateResponse(HttpStatusCode.Accepted);
}
catch
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
You need to add the account to the user or the user to the account.
Here's an example of adding an account to the user:
var newUser = db.UserDetails.FirstOrDefault(newuser => newuser.UserMail == user.UserMail && newuser.AccId == accID);
if (newUser == null)
{
var user = new User();
var account = db.Accounts.FirstOrDefault(acc => acc === "some account");
if(account != null)
{
//Depending on your entity, you may need to check if Accounts is null here.
user.Accounts.add(account);
}
db.Users.Add(user);
try
{
db.SaveChanges();
return Request.CreateResponse(HttpStatusCode.Accepted);
}
catch
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
If your classes look kind of like the following, then EF will automatically map the tables for you.
public class User
{
public ICollection<Account> Accounts {get;set;}
}
public class Account
{
public ICollection<User> Users {get;set;}
}

Categories