I have model:
public class Department
{
public int DepartmentID { get; set; }
[Required]
[UniqueDepartmentName]
public string Name { get; set; }
public List<Person> Persons { get; set; }
}
And DBcontext:
public class InstituteContext : DbContext
{
public InstituteContext (DbContextOptions<InstituteContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>().HasIndex(p => p.Name).IsUnique();
}
public DbSet<Institute.Models.Department> Department { get; set; }
}
As you see property "NAME" i make unique.
For validation i create My validation Attribute:
public class UniqueDepartmentName : ValidationAttribute
{
public override bool IsValid(object value)
{
var db = new InstituteContext();
}
}
But i can not create instance of InstituteContext, because constructor need parameters.
How i can create instance of InstituteContext? Or what should i pass to constructor in parameters?
Try this:
public class UniqueDepartmentName : ValidationAttribute
{
public override bool IsValid(object value)
{
var connectionString = "Your ConnectionString"
var options = new DbContextOptionsBuilder<InstituteContext>()
.UseSqlServer(new SqlConnection(connectionString)).Options;
using (var dbContext = new BloggingContext(options))
{
// Do necessary staffs here with dbContext
}
}
}
Your DbContextOptions method is in the wrong place, your constructor can be empty, and you need to add the method OnConfiguring, which receives the DbContextOptions.
Something like:
public DbSet<Department> Department { get; private set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) {
// In my case I'm passing the connection string in this method below
options.UseSqlServer("Data Source=DATABASEIP;Initial Catalog=DATABASETABLE;" +
"User ID=USER;Password=PASSWORD");
}
Related
I have a simple RESTful API and this is the post route handler I'm trying to apply AutoMapper in:
[HttpPost]
[Route("[action]")]
public async Task<IActionResult> CreateHotel([FromBody]Hotel hotelCreateDto)
{
var hotel = _mapper.Map<Hotel>(hotelCreateDto);
var createdHotel = await _hotelService.CreateHotel(hotel);
var hotelReadDto = _mapper.Map<HotelReadDto>(createdHotel);
return CreatedAtAction("GetHotelById", new { id = hotelReadDto.Id }, hotelReadDto);
}
So in the request I get a hotelCreateDto which looks like that:
public class HotelCreateDto
{
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
}
and I map this to Hotel entity:
public class Hotel
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
}
and a new hotel object is created in the next line. However when hotelReadDto is going to be assigned to the new mapped object, a 500 error occurs: "AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping."
Could you catch a mistake here? I don't know where I'm doing wrong.
Edit: there'S also this things after the error above: "Mapping types:
Object -> HotelReadDto
System.Object -> HotelFinder.DTO.DTOs.HotelReadDto"
Edit2: Here is the code in the Configure Services:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
And in the Profile class:
public class HotelProfile : Profile
{
public HotelProfile()
{
CreateMap<Hotel, HotelReadDto>();
CreateMap<HotelCreateDto, Hotel>();
}
}
Add this in your services in startup :
it's reusable and cleaner
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
}
add these interface and class in your project
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
using AutoMapper;
using System;
using System.Linq;
using System.Reflection;
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.GetInterfaces()
.Any(i =>i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetMethod("Mapping")
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
methodInfo?.Invoke(instance, new object[] { this });
}
}
}
and your dto be like this (map hotel to HotelDto):
public class HotelCreateDto : IMapFrom<HotelCreateDto>
{
[StringLength(50)]
[Required]
public string Name { get; set; }
[StringLength(50)]
[Required]
public string City { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<Hotel,HotelCreateDto>();
}
}
I work with Asp.Net Core WebApi project.
Can I add my tables to IdentityDbContext, like this:
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{ }
public DbSet<ProgrammerRole> ProgrammerRoles { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<SubProject> SubProjects { get; set; }
public DbSet<Report> Reports { get; set; }
}
Or do I need to create a second DbContext. And if i create a second DbContext
how can I communicate wiht User in IdentityDbContext.
And my second question:
If i add data in IdentityDbContext, like above, How do I get the data from my tables in ApplicationDbContext?
Because i need to pass DbContextOptions object every time I create a new instance оf ApplicationDbContext. I do this in Startup.cs:
// ===== Add DbContext ========
var connectionString = Configuration.GetConnectionString("DbConnection");
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
I saw in older version of Asp.Net Core, that i can pass Connection String in IdentityDbContext constructor, but now only DbContextOptions.
And i can't do, for example this:
[AllowAnonymous]
[HttpGet]
public IEnumerable<Project> GetRoles()
{
using (var db = new ApplicationDbContext())
{
return db.Projects;
}
}
Can I add my tables to IdentityDbContext, like this:
Yes, it is how you create custom tables. You do not need to create another DbContext. E.g.
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Project> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Project>(entity =>
{
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(50);
});
base.OnModelCreating(builder);
}
}
Note: you might need to run dotnet ef migrations add Initial and dotnet ef database update for database migration.
using (var db = new ApplicationDbContext()) {...}
You should not create or manage ApplicationDbContext inside controller. If you do so, they become tightly coupled, and you cannot implement unit tests.
Instead, you let dependency inversion (DI) container manage it for you. E.g.
public class UserController : Controller
{
private readonly ApplicationDbContext _context;
public UserController(ApplicationDbContext context)
{
_context = context;
}
[AllowAnonymous]
[HttpGet]
public IEnumerable<Project> GetRoles()
{
return _context.Projects;
}
}
I solve my problem, i just replaced code in my ApplicationDbContext, and get connection string from method:
public class ApplicationDbContext : IdentityDbContext<User>
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(GetConnectionString());
}
private static string GetConnectionString()
{
const string databaseName = "EmployeeReportsDb";
const string databasePass = "SuperPuper_Random_DB-key!";
return $"Server=localhost;" +
$"database={databaseName};" +
$"Trusted_Connection = True;" +
$"MultipleActiveResultSets = True;" +
$"pwd={databasePass};" +
$"pooling=true;";
}
public DbSet<ProgrammerRole> ProgrammerRoles { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<SubProject> SubProjects { get; set; }
public DbSet<Report> Reports { get; set; }
}
here is the resource: https://medium.com/#ozgurgul/asp-net-core-2-0-webapi-jwt-authentication-with-identity-mysql-3698eeba6ff8
I'm new to the forum and in the "world" of programming. I bumped into a problem while creating a game with SignalR Web technology and it is expressed in terms of access to the database (using EF) with multiple requests from UI-a. What is the best solution using the repository pattern? The decision to me at this stage is added Lock {} structure in each method, which accesses the database. How can I avoid blocking requests to a server?
public interface IRepository<T> where T : class
{
IQueryable<T> GetAll();
T GetById(object id);
void Add(T item);
void Update(T item);
void Delete(T item);
void Delete(object id);
}
public class DBRepository<T> : IRepository<T> where T : class
{
private DbContext DbContext;
private DbSet<T> Entities
{
get
{
return this.DbContext.Set<T>();
}
}
public DBRepository(DbContext context)
{
this.DbContext = context;
}
public IQueryable<T> GetAll()
{
return Entities.AsQueryable();
}
.....
public class TicTacToeContext : DbContext
{
public DbSet<Game> Games { get; set; }
public DbSet<Guess> Guesses { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<MessageState> MessageStates { get; set; }
public DbSet<MessageType> MessageTypes { get; set; }
public DbSet<User> Users { get; set; }
public TicTacToeContext()
: base("TicTacToeDb")
{
}
public interface IGameService
{
void CreateGame(CreateGameModel gameModel);
void JoinGame(JoinGameModel gameModel);
...
public abstract class BaseService
{
public IRepository<User> UserRepository;
public IRepository<Game> GameRepository;
...
public class GameService : BaseService, IGameService
{
public GameService(IRepository<Game> gameRepositort, IRepository<User> userRepository, ISessionService sessionService)
{
this.UserRepository = userRepository;
this.GameRepository = gameRepositort;
}
public void CreateGame(CreateGameModel gameModel)
{
....
}
public class TicTacToeHub : Hub
{
IUserService UserServise;
IGameService GameServise;
private static object _syncRoot = new object();
public TicTacToeHub(IUserService userService, IGameService gameService)
{
this.UserServise = userService;
this.GameServise = gameService;
}
.....
public void ReturnOpenGamesToClient(string sessionKey)
{
IEnumerable<GameModel> openGames;
lock (_syncRoot)
{
openGames = GameServise.GetOpenGames(sessionKey).ToList();
}
Clients.Caller.updateOpenGamesList(openGames);
}
Why locks? You use a DB and only update one entity (No transaction scope needed).
Locks needs to be used for Inmemory types like IList or IDictionary otherwise it will crash when one request reads and another one writes. But SQL takes care of this for you
Here is my model classes:
public abstract class BaseEntity
{
[Key]
public Guid Guid { get; set; }
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Id
{
get;
set;
}
}
public class Menu : Abstract.BaseEntity
{
public string Title { get; set; }
public string Url { get; set; }
}
public class SpaceShipEntities : DbContext
{
public DbSet<Menu> Menu { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
I use migrations. And there is my configuration class:
internal sealed class Configuration : DbMigrationsConfiguration<SpaceProject.Models.SpaceShipEntities>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SpaceProject.Models.SpaceShipEntities context)
{
context.Menu.AddOrUpdate(
c => c.Title,
new Menu() { Guid = Guid.NewGuid(), Title = "Главная" }
);
}
}
When i use package command "Update-Database -Verbose" in empty database new rows has inserted to database. But when i use "Update-Database -Verbose" second time, and Seed method tries to update rows, i have following error:
When updating records the error occurred. For details, see the inner exception. ---> System.InvalidOperationException: Changing column template "Identity" is not supported. Column: "Id". Table: "CodeFirstDatabaseSchema.Menu".
The Guid will be indexed anyway if you use it as a key. I would use Id as the key and decorate Guid with [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)] then seed it with:
protected override void Seed(SpaceProject.Models.SpaceShipEntities context)
{
context.Menu.AddOrUpdate(
c => c.Guid,
new Menu() { Guid = Guid.Parse("..."), Title = "Главная" }
);
}
I have two simple model classes.
Service
Attendee
Each service can be attended by 1 or more people. I want Create and Edit views based on service where all attendees display as a check box and when any of the selected/unselected it should add or remove from the corresponding service in the database.
I have been trying to build it for more than a week now but no success!
any help will be highly appreciated.
My code is as follows for these classes.
public class Service
{
public int ServiceID { get; set; }
public string Problem { get; set; }
public virtual ICollection<Attendie> AttendedBy { get; set; }
}
public class Attendie
{
public int AttendieID { get; set; }
public string Name { get; set; }
public virtual Service Service { get; set; }
}
public class ServiceAttendiesDBContext: DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<Service> Services { get; set; }
public DbSet<Attendie> Attendies { get; set; }
}
public class ServiceAttendiesInitializer : DropCreateDatabaseAlways<ServiceAttendiesDBContext>
{
protected override void Seed(ServiceAttendiesDBContext context)
{
base.Seed(context);
var attendies = new List<Attendie>
{
new Attendie{AttendieID=1,Name="Attendie1"},
new Attendie{AttendieID=2,Name="Attendie2"},
new Attendie{AttendieID=3,Name="Attendie3"}
};
attendies.ForEach(at => context.Attendies.Add(at));
context.SaveChanges();
var services = new List<Service>
{
new Service{ ServiceID=1,Problem="Problem1",AttendedBy=new List<Attendie>()},
new Service{ ServiceID=2,Problem="Problem2",AttendedBy=new List<Attendie>()},
new Service{ ServiceID=3,Problem="Problem3",AttendedBy=new List<Attendie>()}
};
services.ForEach(ser => context.Services.Add(ser));
context.SaveChanges();
services[0].AttendedBy.Add(attendies[0]);
services[0].AttendedBy.Add(attendies[1]);
services[1].AttendedBy.Add(attendies[2]);
services[1].AttendedBy.Add(attendies[2]);
services[2].AttendedBy.Add(attendies[1]);
context.SaveChanges();
}