I'm using Dynamic Data and LINQ to SQL for some admin pages on a .NET 3.5 web app. All my admin tables have a CreatedBy, CreatedDate, UpdatedBy, and UpdatedDate.
I'm looking for a way to inject the setting of these properties before the objects are inserted and updated.
I've seen an object_inserting hook if you have a linq to sql datasource in the web form, but I'm using dynamic data...is there an easy way to generically set that? And I've also looked at modifying each of the partial classes for my admin objects, but the closest hook I see is to implement the OnValidate method with the Insert action. Any suggestions? TIA.
David Turvey has published a great example of adding in an OnSaving and OnSaved methods for your entities, check here: Adding OnSaving an OnSaved Events to LINQ to SQL Entities
By implementing the above on your entities, you can extend them with a partial class, e.g.
partial class MyAdminEntity : EntityBase
{
internal override OnSaving(ChangeAction changeAction)
{
if (changeAction == ChangeAction.Insert)
{
CreatedBy = "<username>";
CreatedDate = DateTime.Now;
}
else if (changeAction == ChangeAction.Update)
{
CreatedBy = "<username>";
CreatedDate = DateTime.Now;
}
}
}
I got tried, add your entity class to app_code, change the class to partial class, it works for me! Hope this help! Reference here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.Objects;
using System.Data.Linq;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace NorthwindModel
{
public partial class NorthwindEntities
{
partial void OnContextCreated()
{
// Register the handler for the SavingChanges event.
this.SavingChanges += new EventHandler(context_SavingChanges);
}
// SavingChanges event handler.
private static void context_SavingChanges(object sender, EventArgs e)
{
var objects = ((ObjectContext)sender).ObjectStateManager;
// Get new objects
foreach (ObjectStateEntry entry in objects.GetObjectStateEntries(EntityState.Added))
{
// Find an object state entry for a SalesOrderHeader object.
if (entry.Entity.GetType() == typeof(Employee))
{
var usr = entry.Entity as Employee;
// Do your Business Logic here.
}
}
}
}
}
I know this is an old post, but this could help others in solving their problem.
There are some other ways to do that.
You can use this:
public partial class BasicModelDataContext : DataContext
{
partial void InsertEmployee(Employee instance)
{
instance.MyValue = "NEW VALUE";
Employee.Insert(instance);
}
partial void UpdateEmployee(Employee instance)
{
instance.MyValue = "NEW Update VALUE";
Employee.Update(instance);
}
}
Related
I have a button method that expects me to give it a value from the field that serves as the ID.
async void Tapped(System.Object sender, Xamarin.Forms.ItemTappedEventArgs e)
{
var person = await App.SQLiteDb.GetItemAsync(Convert.ToInt32(txtPersonId.Text));
if (person != null)
{
await DisplayAlert("Success", "Person Name: " + person.Name + Environment.NewLine + "Person ID: " + person.PersonID, "OK");
}
}
How to replace:
Convert.ToInt32(txtPersonId.Text)
With:
person.PersonID
So not to expect an input parameter but to get an ID from the database for the respective name ?
My SQLiteHelper.cs look like this:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SQLite;
namespace WeatherLocationInfo.Views
{
public class SQLiteHelper
{
SQLiteAsyncConnection db;
public SQLiteHelper(string dbPath)
{
db = new SQLiteAsyncConnection(dbPath);
db.CreateTableAsync<Person>().Wait();
}
//Insert and Update new record
public Task<int> SaveItemAsync(Person person)
{
if (person.PersonID != 0)
{
return db.UpdateAsync(person);
}
else
{
return db.InsertAsync(person);
}
}
//Delete
public Task<int> DeleteItemAsync(Person person)
{
return db.DeleteAsync(person);
}
//Read All Items
public Task<List<Person>> GetItemsAsync()
{
return db.Table<Person>().ToListAsync();
}
//Read Item
public Task<Person> GetItemAsync(int personId)
{
return db.Table<Person>().Where(i => i.PersonID == personId).FirstOrDefaultAsync();
}
}
}
My Person object look like this:
using System;
using SQLite;
namespace WeatherLocationInfo.Views
{
public class Person
{
[PrimaryKey, AutoIncrement]
public int PersonID { get; set; }
public string Name { get; set; }
}
}
My App Xaml.cs look like this:
public static SQLiteHelper SQLiteDb
{
get
{
if (db == null)
{
db = new SQLiteHelper(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "XamarinSQLite.db3"));
}
return db;
}
}
I used tabbed forms on my xamarin project and want to pass as an external parameter id from the database.
I want to completely delete the field for filling in the ID and everything happens automatically.
If I understand your question, first you have to bind your text field into a Person property in your ViewModel then use the Person Object in your Event handler.
var bindingContext = this.BindingContext as YourViewModelName ;
var person = await App.SQLiteDb.GetItemAsync(bindingContext.person.PersonID);
The way you have written your code suggests that you are currently calling your database (SQLLite) methods from your view (mobile app or 'Tabbed forms').
While this is doable, it does not permit binding which is exactly what you are asking for when you mentioned that you would like to use 'person.PersonID' instead of 'Convert.ToInt32(txtPersonId.Text)'
In order to do this, you need to follow a layered architecture of a mobile app where calls are made as follows:
View: This is the face of your mobile app, the screen you see on the phone when you open the app.
ViewModel: The ViewModel communicates with the View and the database and is responsible for fetching data from SQLLite and connecting or 'binding' it do the View.
Model: In your example, this would be the 'Person' object/class and is also used by SQLLite do create tables in the database.
DataLayer: To start off with, I would suggest adding this layer between ViewModel and Model. This layer will have calls like 'App.SQLiteDb.GetItemAsync()' or 'App.SQLiteDb.SaveItemAsync()', just so that you do not have to write this code in the ViewModel, there by keeping the ViewModel clean.
Lastly, I would recommend you familiarizing yourself with the concepts of:
Xamarin and MVVM
Dependency Injection
Try googling out for 'Xamarin mobile app sqlite mvvm tutorials'. There are loads out there that would explain these concepts.
All the best.
I am trying to sort an observable collection of objects by one of their properties in Xamarin Forms.
It seems like the following code should work, but it does not. I am trying to learn as much as I can, so I am very interested why it does not. I'm certain I can copy and paste the solution somewhere, but I've yet to find an answer that explains the why.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
namespace engME
{
public partial class YourFullNamesListPage : ContentPage
{
ObservableCollection<NameObject> _nameList {get; } = MainPage.NameList;
public YourFullNamesListPage()
{
InitializeComponent();
_nameList = _nameList.OrderByDescending(a => a.Name);
FullNamesList.ItemsSource= _nameList;
}
}
}
Well first of all i don't think your property should be like
ObservableCollection<NameObject> _nameList {get; } = MainPage.NameList;
Since you have to made changes in it later i would suggest you have get as well as set in your property(Since you might have to use it in some other class):
ObservableCollection<NameObject> _nameList {get; set; } = MainPage.NameList;
Then to set your order you do it something like this:
public YourFullNamesListPage()
{
InitializeComponent();
_nameList = new ObservableCollection<NameObject>(_nameList.OrderByDescending(x => x.Name));
FullNamesList.ItemsSource= _nameList;
}
Where the below is of the type System.Linq.IOrderedEnumerable<T> and hence you need to convert it to an observable collection by wrapping it into one.
_nameList.OrderByDescending(x => x.Name)
And then you are good to go
Revert in case of queries.
Your _nameList is getonly, thus the following assignment wont change it at all:
_nameList = ObservableCollection<NameObject>(MainPage.NameList.OrderByDescending(a => a.Name));
I've put together an MVC application using a repository pattern with Entity Framework and everything is going smoothly - but I've run into a stopping block and I'm not sure how to proceed.
I have a few dozen databases with the same schema, and I want to be able to choose one or many at runtime. For example, let's say I start with a database of users (not made yet). That user has connection string information associated with them (possibly more than one). Once the user has "logged in", I want the Enumerables I feed to my Views to contain matching data from all of the databases that user has access to.
Here's an example of what I have right now:
Entity:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations.Schema;
namespace Dashboard.Domain.Entities
{
public class Flight
{
public Guid Id { get; set; }
public string CarrierCode { get; set; }
public string FlightNo { get; set; }
public string MarketingCarrierCode { get; set; }
public string MarketingFlightNo { get; set; }
public string Type { get; set; }
public string TailNo { get; set; }
public string OriginIATA { get; set; }
...
}
}
DB Context:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using Dashboard.Domain.Entities;
namespace Dashboard.Domain.Concrete
{
public class EFDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Passenger>().ToTable("PAX");
}
public DbSet<Flight> Flights { get; set; }
public DbSet<Passenger> PAX { get; set; }
public DbSet<Airport> Airports { get; set; }
}
}
Flight repository interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dashboard.Domain.Entities;
namespace Dashboard.Domain.Abstract
{
public interface IFlightRepository
{
IQueryable<Flight> Flights { get; }
}
}
EF Flight Repository:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dashboard.Domain.Abstract;
using Dashboard.Domain.Entities;
namespace Dashboard.Domain.Concrete
{
public class EFFlightRepository : IFlightRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Flight> Flights
{
get { return context.Flights; }
}
}
}
Controller:
public class FlightController : Controller
{
private IFlightRepository fRepository;
private IPaxRepository pRepository;
private IAirportRepository aRepository;
public int PageSize = 10;
public FlightController(IFlightRepository flightRepository, IPaxRepository paxRepository, IAirportRepository airportRepository)
{
this.fRepository = flightRepository;
this.pRepository = paxRepository;
this.aRepository = airportRepository;
}
public ViewResult List(byte status = 1, int page = 1)
{ ...
I want those repositories to contain all of the data from all of the connection strings specified, but I have no idea where to start. EF is getting my connection string from the web.config, but I need to be able to set it dynamically somehow and I need to put more than one database's data into the repository.
Is this possible? I should mention that the site is READ ONLY, so I won't need to write changes back to the DBs.
UPDATE:
I've changed the code so I can pass a connection string to the constructor of my EF Repository, but when I try to merge the IQueryables from two different contexts, as below:
public class EFFlightRepository : IFlightRepository
{
private EFDbContext context1 = new EFDbContext(connectionstring1);
private EFDbContext context2 = new EFDbContext(connectionstring2);
private IQueryable<Flight> context;
public EFFlightRepository()
{
context = (IQueryable<Flight>)context1.Flights.Union(context2.Flights);
}
public IQueryable<Flight> Flights
{
get { return context;}
}
}
I get this exception:
The specified LINQ expression contains references to queries that are
associated with different contexts.
How can I combine them so I can run my LINQ queries just like it's ONE set of data?
It is difficult to come up with a detailed solution because it really depends on your software design choices, but I think a possible solution consists of the following things:
1) A method / class that creates a collection of DbContext objects using the DbContext constructor with connection string or connection string name (is the same constructor) as Willian Werlang mentioned:
new DbContext("DB1");
2) Your repositories should be able to accept the list of DbContext's rather than a single one. It could e.g. be injected with the constructor of it.
3) The retrieval methods should iterate over the repositories and load (eager load when detaching) the relevant objects.
4) The retrieved objects could be detached from their DbContext using the following code:
dbContext.Entry(entity).State = EntityState.Detached;
This isn't required but might be a consideration since you would return a mix of different data sources.
5) The retrieved/detached objects should be added to a returned List<> or you could yield return the results one by one with IEnumerable<> is return type.
Returning an IQueryable isn't possible in this case but an IEnumerable will do as result.
An example of a simple retrieval method for a flight repository could be something like:
public IEnumerable<Flight> GetFlights() {
// dbContexts is an IEnumerable<DbContext> that was injected in the constructor
foreach (var ctx in dbContexts) {
foreach (var flight in ctx.Flights) {
yield return flight;
}
}
}
You can set multiples databases on your web.config, but with different names, so your DbContext's can receive the name of the database you want as parameter, like:
new DbContext("DB1");
This way you can choose from which database you'll get the data but I don't think you can get data from multiples bases at the same time with only onde dbContext;
My solution was to change my Repository classes to take a connection string parameter, like this:
namespace Dashboard.Domain.Concrete
{
public class EFFlightRepository : IFlightRepository
{
private EFDbContext context;
public IQueryable<Flight> Flights
{
get { return context.Flights;}
}
public EFFlightRepository(string connectionString)
{
context = new EFDbContext(connectionString);
}
}
}
Then create a factory class (using Ninject.Extensions.Factory) to pass the parameter when the repository is being created (How to pass parameters down the dependency chain using Ninject):
namespace Dashboard.Domain.Factories
{
public interface IFlightRepoFactory
{
IFlightRepository CreateRepo(string connectionString);
}
}
I have another Factory class that produces a list of Repositories based on a list of strings (connection strings to feed to the individual repository classes).
namespace Dashboard.Domain.Factories
{
public interface IRepoCollectionFactory
{
IRepositoryCollection CreateCollection(List<string> connectionStrings);
}
}
Then, in my controller class, I iterate through the Collection generated by the Collection Factory, running whatever query needs to be run on each set of repositories, and combine the results.
This ultimately gives me a list that contains all of the data from each query on each repository.
public FlightController(IRepoCollectionFactory repoCollectionFactory)
{
this.repoCollectionFactory = repoCollectionFactory;
this.collection = repoCollectionFactory.CreateCollection(new List<string> {
// each connection string for each database here
});
}
Bindings in Ninject class:
private void AddBindings()
{
ninjectKernel.Bind<IFlightRepoFactory>().ToFactory();
ninjectKernel.Bind<IAirportRepoFactory>().ToFactory();
ninjectKernel.Bind<IPaxRepoFactory>().ToFactory();
ninjectKernel.Bind<IRepoFactory>().ToFactory();
ninjectKernel.Bind<IRepoCollectionFactory>().ToFactory();
ninjectKernel.Bind<IRepositories>().To<EFRepositories>();
ninjectKernel.Bind<IRepositoryCollection>().To<EFRepositoryCollection>();
ninjectKernel.Bind<IFlightRepository>().To<EFFlightRepository>();
ninjectKernel.Bind<IPaxRepository>().To<EFPaxRepository>();
ninjectKernel.Bind<IAirportRepository>().To<EFAirportRepository>();
}
I use Entity Framework ObjectResult coming from Execute method to bind data to a WPF control, like here:
using System;
using System.Data;
using System.Data.Objects;
using System.Windows;
using System.Linq;
namespace Microsoft.Samples.Edm
{
/// <summary>
/// Interaction logic for SalesOrders.xaml
/// </summary>
public partial class SalesOrders : Window
{
private AdventureWorksEntities context;
private int customerId = 277;
private void SalesOrdersForm_Loaded(object sender, RoutedEventArgs e)
{
// Instantiate the ObjectContext.
context = new AdventureWorksEntities();
// Define a query that returns orders for a customer.
// Because lazy loading is on by default, SalesOrderDetails
// related to a SalesOrderHeader will be loaded when the query
// is executed.
var query = from o in context.SalesOrderHeaders
where o.CustomerID == customerId
select o;
// Execute the query and bind the result to the OrderItems control.
this.orderItemsGrid.DataContext = ((ObjectQuery)query).Execute(MergeOption.AppendOnly);
}
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
public SalesOrders()
{
InitializeComponent();
}
}
}
The example comes from MSDN
It work fine, but how do I refresh the binding? Either programmatically or when database changes?
The SalesOrdersForm_Loaded code should be seperated from this event.
Place this code in a function. and call it in form load. Now you can call this function on your requirement basis.
I hope it makes sense.
Edit
You can call this function on button click / Timer or any event based on your requirement to update the bindings
Here is my method that returns an IQueryable of Countries:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace InternationalShipments.Repository
{
public class CountryRepository
{
ShipmentsEntities db = new ShipmentsEntities();
public IQueryable<Country> FindAll()
{
return db.Countries;
}
public Country Get(int id)
{
return db.Countries.FirstOrDefault(x => x.ID == id);
}
public void Add(Country country)
{
db.Countries.AddObject(country);
}
public void Delete(Country country)
{
db.Countries.DeleteObject(country);
}
public void Save()
{
db.SaveChanges();
}
}
}
My intent is to show a form that lets you create new countries and on the same page, continue to display all countries in the DB. So if a user adds a new country, it should display in the table above.
Any guidance?
The easiest way would be to use a Repeater. Using that you can easily bind your data to the control and create an HTML template for the output. If you want something with a little more power, you can try the DataGrid or Gridview
I think a Gridview using a Linq to SQL or SQL Datasource would fit the bill. I recall that the gridview can be customized to include an "add record" feature as well as the ability to edit returned records.