Seed method doesn't update database second time after editing it. What to do? - sql

I create web app using ASP.Net Core 2.1 MVC. To add initial data to some tables (for ex. categories) I wrote Seed method and added Migration and updated database successfully (with no errors). Data (with all values added) was seen in the database (SQL Server) after running application. Then I added some other categories in the seed method. This time I didn't see changes (updates) in SQL server after running application and updating database using migration.
Does Seed work only first time or I can somehow update (increase initial data) the database using Seed?
This is my Seed method:
public static void Seed(OfferDbContext offerDbContext)
{
if (!offerDbContext.Categories.Any())
{
Category avto = new Category()
{
Name = "Avto"
};
Category home = new Category()
{
Name = "Ev"
};
Category digital = new Category()
{
Name = "Digital"
};
Category household = new Category()
{
Name = "Məişət"
};
Category entertainment = new Category()
{
Name = "Əyləncə"
};
Category furniture = new Category()
{
Name = "Mebel"
};
Category clothes = new Category()
{
Name = "Geyim"
};
Category cafe = new Category()
{
Name = "Kafe"
};
Category food = new Category()
{
Name = "Qida"
};
Category edu = new Category()
{
Name = "Təhsil"
};
Category medical = new Category()
{
Name = "Tibb"
};
Category tourism = new Category()
{
Name = "turizm"
};
offerDbContext.Categories.AddRange(avto, home, digital, household, entertainment, furniture, clothes, cafe, food, edu, medical, tourism);
offerDbContext.SaveChanges();
}
}
And Main method in Program.cs where I call that Seed:
public static void Main(string[] args)
{
IWebHost webHost = CreateWebHostBuilder(args).Build();
using (IServiceScope serviceScope = webHost.Services.CreateScope())
{
using(OfferDbContext offerDbContext = serviceScope.ServiceProvider.GetRequiredService<OfferDbContext>())
{
OfferDb.Seed(offerDbContext);
}
}
webHost.Run();
}

I would think that the issue is with the below line
if (!offerDbContext.Categories.Any())
Basically it says if there are no categories then do the adds, there is no else so if there are already categories there it will do nothing.
So maybe throw an else in and if there are already categories just add the new ones,
OR
Potentially you could wrap each of the creates in an if to see if it already exists and if it does not then crate it.

Related

Asp.net core: count hotel booking date

Is there any way to count the day of booking? either using lambda or sqlite query thanks, can find the solution for a while thanks~
The bookingInput is from a webpage of booking which contains rooms, checkin and check out.
The below code is Create.cshtml.cs in Booking folder (created by scaffolding)
int count = bookingInput.CheckOut.DayOfWeek - booking.CheckIn.DayOfWeek;
booking.Cost = count * theRoom.Price;
You could use the TimeSpan.TotalDays Property to get the number of days between two dates.
Check the following code:
//get data from the Repository.
var result = _repo.GetBookings().Select(c => new
{
Id = c.Id,
Name = c.Name,
RoomNumner = c.RoomNumber,
CheckIn = c.CheckIn,
CheckOut = c.CheckOut,
Days = (c.CheckOut - c.CheckIn).TotalDays,
RoundDays = Math.Round((c.CheckOut - c.CheckIn).TotalDays)
});
The result as below:
The test data in the Repository:
public interface IDataRepository
{
List<BookingViewModel> GetBookings();
}
public class DataRepository : IDataRepository
{
public List<BookingViewModel> GetBookings()
{
return new List<BookingViewModel>()
{
new BookingViewModel(){ Id=101, Name="David", RoomNumber=1001, CheckIn= new DateTime(2021,1,18,15,30,0), CheckOut=DateTime.Now },
new BookingViewModel(){ Id=102, Name="Jack", RoomNumber=2001, CheckIn= new DateTime(2021,1,15,10,30,0), CheckOut=DateTime.Now },
new BookingViewModel(){ Id=103, Name="Tom", RoomNumber=3001, CheckIn= new DateTime(2021,1,20,18,30,0), CheckOut=DateTime.Now },
};
}
}

SaveChanges() outside of foreach saves only the last object added

Seems that the SaveChanges() only saves the last object added into the table:
static void Main(string[] args)
{
Category c = new Category();
using (GenericDBEntities db = new GenericDBEntities())
{
foreach (Match i in db.Matches)
{
if (!db.Categories.Any())
{
c.CategoryInternalId = i.CategoryId;
c.CategoryName = i.CategoryName;
c.SportId = i.SportId;
db.Categories.Add(c);
}
else
{
foreach (Category a in db.Categories)
{
if (i.CategoryId != a.CategoryInternalId)
{
c.CategoryInternalId = i.CategoryId;
c.CategoryName = i.CategoryName;
c.SportId = i.SportId;
db.Categories.Add(c);
}
else
{
return;
}
}
}
}
db.SaveChanges();
I have tried it in a few different ways which all have the same or less result, the for loop saves them all, no matter if condition is met or not. Why is it only saving the last object of the Match table? What am I missing?
Clarification: The task is to go through Match table, which has duplicate CategoryIDs and to, if it is not already in the Category table, add it, so it won't store duplicates again, the problem is Category table is initially empty, hence the newbie logic, still learnin'!
You need to create the Category object for each inserted category, otherwise you have just one category object which you are editing all the time and hence just this one (the last edit) will be saved to the DB.
If you want to add all new categories from Matches based on CategoryId you can use something like this:
using (GenericDBEntities db = new GenericDBEntities())
{
var newCategories = db.Matches
.Where(m => !db.Categories
.Select(c => c.CategoryInternalId)
.Distinct().Contains(m.CategoryId))
.Select(m => new { m.CategoryId, m.CategoryName, m.SportId })
.GroupBy(m => m.otherid)
.Select(g => g.FirstOrDefault())
.ToList();
foreach (var i in newCategories)
{
var c = new Category()
{
CategoryInternalId = i.CategoryId,
CategoryName = i.CategoryName,
SportId = i.SportId
};
db.Categories.Add(c);
}
db.SaveChanges();
}
Now you'll get all the new categories from the Matches table in one go. This is a first draft, meaning that the performance can be further tuned if needed.

NHibernate Dynamic Component Default Value Issue

All of my entities (that are mapped to a database table) inherit from an entity class with a dynamic component on it called Attributes e.g.:
public abstract class Entity<T> {
public virtual T Id { get; set; }
private IDictionary _attributes;
public virtual IDictionary Attributes {
get { return _attributes ?? (_attributes = new Hashtable()); }
set { _attributes = value; }
}
}
The Attributes collection allows me to add extra fields to each entity without directly changing the entity itself. This allows me to make my application more modular.
For example say I have the following entity:
public class User : Entity<int> {
public virtual string Name { get; set; }
}
Now say I have a Forum module which needs a NumPosts property against the User. I would add the field against the Users table in the database. This field is non nullable and has a default value of 0. I then map the field using the dynamic component against the User entity.
However when I try inserting the user by saying:
session.Save(new User() { Name = "Test" });
It throws an error as it's expecting me to set a value for NumPosts and the generated SQL would be something like:
INSERT INTO Users (Name, NumPosts) VALUES ('Test', NULL)
However NumPosts does not allow nulls and hence the error. Ideally I'd like it to say the following if the Attributes collection does not contain an entry for NumPosts:
INSERT INTO Users (Name) VALUES ('Test')
An alternative is to say the following which would work fine:
session.Save(new User() { Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } } });
The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this.
For reference here's a bare bones version of session factory method which maps the NumPosts field:
return Fluently.Configure()
...
.ExposeConfiguration(c => {
// Get the persistent class
var persistentClass = c.GetClassMapping("User");
// Create the attributes component
var component = new Component(persistentClass);
// Create a simple value
var simpleValue = new SimpleValue(persistentClass.Table);
// Set the type name
simpleValue.TypeName = "Int32";
// Create a new db column specification
var column = new Column("NumPosts");
column.Value = simpleValue;
column.Length = 10;
column.IsNullable = false;
column.DefaultValue = "0";
// Add the column to the value
simpleValue.AddColumn(column);
// Ad the value to the component
component.AddProperty(new Property() { Name = column.Name, Value = simpleValue });
// Add the component property
persistentClass.AddProperty(new Property() { Name = "Attributes", Value = component });
})
.BuildConfiguration();
I'd appreciate if someone could let me know if this is possible. Thanks
You know how to make it working as described above:
... An alternative is to say the following which would work fine:
session.Save(new User()
{
Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } }
});
... The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this...
In case, that the biggest issue is the explicit Attributes initialization ("...I don't want the modules to have a dependency...") we can use:
12.2. Event system
So, with Listener like this:
[Serializable]
public class MyPersistListener : NHibernate.Event.ISaveOrUpdateEventListener
{
public void OnSaveOrUpdate(SaveOrUpdateEvent #event)
{
var entity = #event.Entity as Entity<int>; // some interface IHaveAttributes
if (entity == null) // would be more appropriate
{
return;
}
var numPosts = entity.Attributes["NumPosts"] as int?;
if (numPosts.HasValue)
{
return;
}
entity.Attributes["NumPosts"] = 0;
}
}
Based on this doc snippet:
Configuration cfg = new Configuration();
ILoadEventListener[] stack = new ILoadEventListener[] { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners.LoadEventListeners = stack;
This should be the init in our case:
.ExposeConfiguration(c => {
var stack = new ISaveOrUpdateEventListener [] { new MyPersistListener() };
c.EventListeners.SaveEventListeners= stack;

Confusion on ektron persona management

i have created a custom connection in dxh but that is not available on persona management site. from this video i understand that i have to create a class library for my custom connection. Is there any demo code available for this custom library?
i am stuck with the login management of dxh and persona, and how is this attributes working?
my code
using Attribute = Ektron.PersonaManagement.Common.Attribute;
public class ETPersona : PersonaAttributeProvider
{
public override IEnumerable<Attribute> GetAttributes()
{
return new List<Attribute>() {
new Attribute() {
DataSource = "test",
DataSourceId = "test",
Id = "test",
Name = new LocalizableText("test","test"),
Type =FieldType.String,
Options = new List<LocalizableText>(){new LocalizableText("test","test")}
}
};
}
public override UserAttributeData GetUserAttributeData(VisitorContext context)
{
return new UserAttributeData()
{
Attributes = new List<UserAttribute>() { new UserAttribute() { } },
HaveUserData = true,
UserKey = ""
};
}
}
Please see Ektron's forum discussion here for information:
http://developer.ektron.com/Forums/?v=t&t=1975

Mocking WebSecurity provider

I'm trying to create some simple unit tests for my controllers and I've come across an issue.
I'm using the new membership provider in MVC 4 and getting the WebSecurity.CurrentUserId and storing that value in the database.
When I run my unit tests against this it's failing and I think I have track this back to the fact that the WebSecurity isn't being mocked at all.
Here's my code if it helps at all,
The Controller
[HttpPost]
public ActionResult Create(CreateOrganisationViewModel viewModel)
{
if (ModelState.IsValid)
{
Group group = _groupService.Create(
new Group
{
Name = viewModel.Name,
Slug = viewModel.Name.ToSlug(),
Profile = new Profile
{
Country = viewModel.SelectedCountry,
Description = viewModel.Description
},
CreatedById = WebSecurity.CurrentUserId,
WhenCreated = DateTime.UtcNow,
Administrators = new List<User> {_userService.SelectById(WebSecurity.CurrentUserId)}
});
RedirectToAction("Index", new {id = group.Slug});
}
return View(viewModel);
}
The Test
[Test]
public void SuccessfulCreatePost()
{
CreateOrganisationViewModel createOrganisationViewModel = new CreateOrganisationViewModel
{
Description = "My Group, bla bla bla",
Name = "My Group",
SelectedCountry = "gb"
};
IUserService userService = MockRepository.GenerateMock<IUserService>();
IGroupService groupService = MockRepository.GenerateMock<IGroupService>();
groupService.Stub(gS => gS.Create(null)).Return(new Group {Id = 1});
GroupController controller = new GroupController(groupService, userService);
RedirectResult result = controller.Create(createOrganisationViewModel) as RedirectResult;
Assert.AreEqual("Index/my-group", result.Url);
}
Thanks
A possible solution is to create a wrapper class around WebSecurity - say WebSecurityWrapper. Expose the static WebSecurity methods such as WebSecurity.CurrentUserId as instance methods on the wrapper. The wrapper's job in this case would be simply to delegate all the calls to WebSecurity.
Inject WebSecurityWrapper into the GroupController's constructor. Now, you can stub the wrapper using the mocking framework of your choice - and thus test the controller logic.
Hope this helps.